dirsql 0.3.71__tar.gz → 0.3.73__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 (136) hide show
  1. {dirsql-0.3.71 → dirsql-0.3.73}/Cargo.lock +1 -1
  2. {dirsql-0.3.71 → dirsql-0.3.73}/PKG-INFO +1 -1
  3. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/_async.py +7 -10
  4. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/cli/main.py +1 -1
  5. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/cli/resolve_config_extensions.py +7 -9
  6. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/resolve_config_extensions.py +8 -12
  7. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/resolve_extension.py +9 -18
  8. {dirsql-0.3.71/packages/rust → dirsql-0.3.73}/docs/howto/columns-from-paths.md +2 -8
  9. {dirsql-0.3.71/packages/python → dirsql-0.3.73}/docs/howto/define-tables.md +2 -16
  10. {dirsql-0.3.71/packages/python → dirsql-0.3.73}/docs/howto/extract-from-contents.md +2 -8
  11. {dirsql-0.3.71/packages/python → dirsql-0.3.73}/docs/howto/load-extension.md +2 -5
  12. {dirsql-0.3.71/packages/python → dirsql-0.3.73}/docs/howto/search-by-meaning.md +9 -17
  13. {dirsql-0.3.71/packages/rust → dirsql-0.3.73}/docs/howto/skip-files.md +1 -5
  14. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/Cargo.toml +1 -1
  15. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/conftest.py +4 -10
  16. {dirsql-0.3.71 → dirsql-0.3.73/packages/python}/docs/howto/columns-from-paths.md +2 -8
  17. {dirsql-0.3.71/packages/rust → dirsql-0.3.73/packages/python}/docs/howto/define-tables.md +2 -16
  18. {dirsql-0.3.71/packages/rust → dirsql-0.3.73/packages/python}/docs/howto/extract-from-contents.md +2 -8
  19. {dirsql-0.3.71/packages/rust → dirsql-0.3.73/packages/python}/docs/howto/load-extension.md +2 -5
  20. {dirsql-0.3.71/packages/rust → dirsql-0.3.73/packages/python}/docs/howto/search-by-meaning.md +9 -17
  21. {dirsql-0.3.71 → dirsql-0.3.73/packages/python}/docs/howto/skip-files.md +1 -5
  22. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/e2e-attestation.json +2 -2
  23. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/src/lib.rs +11 -26
  24. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/conftest.py +0 -1
  25. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/benches/differ_bench.rs +0 -3
  26. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/benches/scanner_bench.rs +0 -2
  27. {dirsql-0.3.71/packages/python → dirsql-0.3.73/packages/rust}/docs/howto/columns-from-paths.md +2 -8
  28. {dirsql-0.3.71 → dirsql-0.3.73/packages/rust}/docs/howto/define-tables.md +2 -16
  29. {dirsql-0.3.71 → dirsql-0.3.73/packages/rust}/docs/howto/extract-from-contents.md +2 -8
  30. {dirsql-0.3.71 → dirsql-0.3.73/packages/rust}/docs/howto/load-extension.md +2 -5
  31. {dirsql-0.3.71 → dirsql-0.3.73/packages/rust}/docs/howto/search-by-meaning.md +9 -17
  32. {dirsql-0.3.71/packages/python → dirsql-0.3.73/packages/rust}/docs/howto/skip-files.md +1 -5
  33. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/bin/dirsql.rs +5 -10
  34. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/execute.rs +15 -2
  35. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/init.rs +5 -9
  36. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/mod.rs +8 -27
  37. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/serialize.rs +1 -9
  38. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/server.rs +2 -7
  39. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/command.rs +5 -19
  40. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/config.rs +14 -58
  41. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/db.rs +58 -195
  42. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/differ.rs +3 -40
  43. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/lib.rs +115 -435
  44. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/matcher.rs +1 -15
  45. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/persist.rs +18 -57
  46. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/scanner.rs +3 -13
  47. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/watcher.rs +7 -11
  48. {dirsql-0.3.71 → dirsql-0.3.73}/Cargo.toml +0 -0
  49. {dirsql-0.3.71 → dirsql-0.3.73}/README.md +0 -0
  50. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/__init__.py +0 -0
  51. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/_dirsql.pyi +0 -0
  52. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/cli/__init__.py +0 -0
  53. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/cli/binary_path.py +0 -0
  54. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/cli/is_windows.py +0 -0
  55. {dirsql-0.3.71 → dirsql-0.3.73}/dirsql/py.typed +0 -0
  56. {dirsql-0.3.71 → dirsql-0.3.73}/docs/.claude/CLAUDE.md +0 -0
  57. {dirsql-0.3.71 → dirsql-0.3.73}/docs/.vitepress/config.ts +0 -0
  58. {dirsql-0.3.71 → dirsql-0.3.73}/docs/.vitepress/theme/index.ts +0 -0
  59. {dirsql-0.3.71 → dirsql-0.3.73}/docs/.vitepress/theme/lang.ts +0 -0
  60. {dirsql-0.3.71 → dirsql-0.3.73}/docs/AGENTS.md +0 -0
  61. {dirsql-0.3.71 → dirsql-0.3.73}/docs/explanation.md +0 -0
  62. {dirsql-0.3.71 → dirsql-0.3.73}/docs/getting-started.md +0 -0
  63. {dirsql-0.3.71 → dirsql-0.3.73}/docs/howto/embed.md +0 -0
  64. {dirsql-0.3.71 → dirsql-0.3.73}/docs/howto/persist.md +0 -0
  65. {dirsql-0.3.71 → dirsql-0.3.73}/docs/howto/react-to-changes.md +0 -0
  66. {dirsql-0.3.71 → dirsql-0.3.73}/docs/index.md +0 -0
  67. {dirsql-0.3.71 → dirsql-0.3.73}/docs/migrations.md +0 -0
  68. {dirsql-0.3.71 → dirsql-0.3.73}/docs/package.json +0 -0
  69. {dirsql-0.3.71 → dirsql-0.3.73}/docs/playwright.config.ts +0 -0
  70. {dirsql-0.3.71 → dirsql-0.3.73}/docs/pnpm-lock.yaml +0 -0
  71. {dirsql-0.3.71 → dirsql-0.3.73}/docs/pnpm-workspace.yaml +0 -0
  72. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/cli.md +0 -0
  73. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/columns.md +0 -0
  74. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/config.md +0 -0
  75. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/hooks.md +0 -0
  76. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/http-api.md +0 -0
  77. {dirsql-0.3.71 → dirsql-0.3.73}/docs/reference/sdk.md +0 -0
  78. {dirsql-0.3.71 → dirsql-0.3.73}/docs/tests/integration/home.spec.ts +0 -0
  79. {dirsql-0.3.71 → dirsql-0.3.73}/docs/tests/integration/language-flag.spec.ts +0 -0
  80. {dirsql-0.3.71 → dirsql-0.3.73}/docs/tests/integration/sidebar.spec.ts +0 -0
  81. {dirsql-0.3.71 → dirsql-0.3.73}/docs/tests/unit/config.test.ts +0 -0
  82. {dirsql-0.3.71 → dirsql-0.3.73}/docs/tests/unit/lang.test.ts +0 -0
  83. {dirsql-0.3.71 → dirsql-0.3.73}/docs/vitest.config.ts +0 -0
  84. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/README.md +0 -0
  85. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/.claude/CLAUDE.md +0 -0
  86. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/.vitepress/config.ts +0 -0
  87. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  88. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  89. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/AGENTS.md +0 -0
  90. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/explanation.md +0 -0
  91. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/getting-started.md +0 -0
  92. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/howto/embed.md +0 -0
  93. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/howto/persist.md +0 -0
  94. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/howto/react-to-changes.md +0 -0
  95. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/index.md +0 -0
  96. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/migrations.md +0 -0
  97. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/package.json +0 -0
  98. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/playwright.config.ts +0 -0
  99. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/pnpm-lock.yaml +0 -0
  100. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/pnpm-workspace.yaml +0 -0
  101. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/cli.md +0 -0
  102. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/columns.md +0 -0
  103. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/config.md +0 -0
  104. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/hooks.md +0 -0
  105. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/http-api.md +0 -0
  106. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/reference/sdk.md +0 -0
  107. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  108. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  109. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  110. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/tests/unit/config.test.ts +0 -0
  111. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  112. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/docs/vitest.config.ts +0 -0
  113. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/__init__.py +0 -0
  114. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/binding/__init__.py +0 -0
  115. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/e2e/__init__.py +0 -0
  116. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/integration/__init__.py +0 -0
  117. {dirsql-0.3.71 → dirsql-0.3.73}/packages/python/tests/smoke/__init__.py +0 -0
  118. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/Cargo.toml +0 -0
  119. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/README.md +0 -0
  120. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/benches/db_bench.rs +0 -0
  121. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/benches/matcher_bench.rs +0 -0
  122. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/explanation.md +0 -0
  123. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/getting-started.md +0 -0
  124. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/howto/embed.md +0 -0
  125. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/howto/persist.md +0 -0
  126. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/howto/react-to-changes.md +0 -0
  127. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/index.md +0 -0
  128. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/migrations.md +0 -0
  129. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/cli.md +0 -0
  130. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/columns.md +0 -0
  131. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/config.md +0 -0
  132. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/hooks.md +0 -0
  133. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/http-api.md +0 -0
  134. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/docs/reference/sdk.md +0 -0
  135. {dirsql-0.3.71 → dirsql-0.3.73}/packages/rust/src/cli/router.rs +0 -0
  136. {dirsql-0.3.71 → dirsql-0.3.73}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.71"
503
+ version = "0.3.73"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.71
3
+ Version: 0.3.73
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -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 (#298 / #313).
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 (``importlib`` + filesystem globs) and the
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 (#313) -- appended after the programmatic ones, matching the
107
- core's ordering -- and suppresses the core's own config-extension
108
- loading so the entries are not loaded a second time (and the core never
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 runtime
133
- env (#298); path-looking values are passed through verbatim (mirroring
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 (#227).
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
- Mirrors the TypeScript launcher. The compiled ``dirsql`` binary reads a
4
- ``.dirsql.toml`` itself and loads its extensions literally -- it has no
5
- ``importlib``, so it cannot resolve a bare **package name** (#227). This
6
- launcher can. When a TOML config names an extension by package name, the
7
- shared SDK resolver (:mod:`dirsql.resolve_config_extensions`, #313) resolves
8
- every one of its extensions and this launcher passes the resolved literal
9
- paths to the binary via repeatable ``--extension`` flags; the binary then
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 parses a ``.dirsql.toml`` itself and loads its extensions
4
- literally -- it has no ``importlib``, so it cannot resolve a bare **package
5
- name** (#227). The SDK can (#313). When a TOML config names an extension by
6
- package name, the SDK resolves every one of its extensions here, hands the
7
- core the resolved literal paths, and suppresses the core's own config-extension
8
- loading (the Rust ``suppress_config_extensions`` builder toggle) so the
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
- (``dirsql.cli.resolve_config_extensions``, which converts the resolved specs
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; a config
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
- #225 supports only literal file paths. #298 adds resolving a bare **package
4
- name**: when ``path`` carries no path separator and no loadable-file suffix, it
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, mirroring the Rust builder).
9
+ verbatim (programmatic entries).
16
10
  2. **Bare name** -- a same-named local file under ``base`` *shadows* the
17
- package (parity with #225's file-first probe); otherwise the package dir is
18
- located via :func:`importlib.util.find_spec` and the current platform's
19
- loadable is globbed from inside it. Zero matches and multiple matches are
20
- both hard errors -- the caller must disambiguate with a literal path.
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" (so package resolution is
29
- # never attempted) and, per platform, the globs used to find a loadable inside
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:
@@ -29,12 +29,8 @@ within one path segment) are in
29
29
 
30
30
  ## 2. Query the captured columns
31
31
 
32
- Start the server (`npx dirsql` / `uvx dirsql`) and query:
33
-
34
32
  ```bash
35
- curl -s http://localhost:7117/query \
36
- -H 'content-type: application/json' \
37
- -d '{"sql":"SELECT year, month, _basename FROM photos ORDER BY year, month"}'
33
+ dirsql query "SELECT year, month, _basename FROM photos ORDER BY year, month"
38
34
  ```
39
35
 
40
36
  ```json
@@ -44,9 +40,7 @@ curl -s http://localhost:7117/query \
44
40
  Captures are real SQL columns, so aggregation works:
45
41
 
46
42
  ```bash
47
- curl -s http://localhost:7117/query \
48
- -H 'content-type: application/json' \
49
- -d '{"sql":"SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"}'
43
+ dirsql query "SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"
50
44
  ```
51
45
 
52
46
  ```json
@@ -24,26 +24,12 @@ glob = "posts/**/*.md"
24
24
  filesystem facts `dirsql` computes for every file. Facts are opt-in by
25
25
  DDL: only the ones you declare become columns.
26
26
 
27
- ## 2. Start the server and query
28
-
29
- ::: code-group
30
-
31
- ```bash [npm]
32
- npx dirsql
33
- ```
34
-
35
- ```bash [PyPI]
36
- uvx dirsql
37
- ```
38
-
39
- :::
27
+ ## 2. Query the table
40
28
 
41
29
  Each matched file is one row:
42
30
 
43
31
  ```bash
44
- curl -s http://localhost:7117/query \
45
- -H 'content-type: application/json' \
46
- -d '{"sql":"SELECT _path, _size FROM posts ORDER BY _path"}'
32
+ dirsql query "SELECT _path, _size FROM posts ORDER BY _path"
47
33
  ```
48
34
 
49
35
  ```json
@@ -31,12 +31,8 @@ by every hook.
31
31
 
32
32
  ## 2. Query the extracted columns
33
33
 
34
- Start the server (`npx dirsql` / `uvx dirsql`) and query:
35
-
36
34
  ```bash
37
- curl -s http://localhost:7117/query \
38
- -H 'content-type: application/json' \
39
- -d '{"sql":"SELECT title, author, year, _path FROM books ORDER BY year"}'
35
+ dirsql query "SELECT title, author, year, _path FROM books ORDER BY year"
40
36
  ```
41
37
 
42
38
  ```json
@@ -61,9 +57,7 @@ on-file = "jq -c -s '.' {path}"
61
57
  ```
62
58
 
63
59
  ```bash
64
- curl -s http://localhost:7117/query \
65
- -H 'content-type: application/json' \
66
- -d '{"sql":"SELECT event, user FROM events"}'
60
+ dirsql query "SELECT event, user FROM events"
67
61
  ```
68
62
 
69
63
  ```json
@@ -23,13 +23,10 @@ overrides the init symbol when it doesn't match the filename-derived
23
23
  default — `sqlite-vec` is exactly such a case
24
24
  ([reference](../reference/config.md#dirsql-extension)).
25
25
 
26
- Start the server (`npx dirsql` / `uvx dirsql`) and the extension's
27
- functions are callable:
26
+ The extension's functions are callable:
28
27
 
29
28
  ```bash
30
- curl -s http://localhost:7117/query \
31
- -H 'content-type: application/json' \
32
- -d '{"sql":"SELECT vec_version() AS vec_version"}'
29
+ dirsql query "SELECT vec_version() AS vec_version"
33
30
  ```
34
31
 
35
32
  ```json
@@ -99,20 +99,14 @@ installed `sqlite_vec` module to its bundled loadable. Naming rules per
99
99
  runtime — and the literal-path alternative that works everywhere — are in
100
100
  [Load a SQLite extension](./load-extension.md).
101
101
 
102
- ## 3. Start the server and ask questions
102
+ ## 3. Ask questions
103
103
 
104
- Launch with `sqlite-vec` available to the launcher's environment:
104
+ Run with `sqlite-vec` available to the launcher's environment. The initial
105
+ scan runs `embed.py` once per note, then the query argument goes straight to
106
+ `pre-query`, exactly as a `POST /query` body would:
105
107
 
106
108
  ```bash
107
- uvx --with sqlite-vec dirsql
108
- ```
109
-
110
- The initial scan runs `embed.py` once per note. Then ask:
111
-
112
- ```bash
113
- curl -s http://localhost:7117/query \
114
- -H 'content-type: application/json' \
115
- -d '{"q": "how do I cook pasta?"}'
109
+ uvx --with sqlite-vec dirsql query '{"q": "how do I cook pasta?"}'
116
110
  ```
117
111
 
118
112
  ```json
@@ -120,9 +114,7 @@ curl -s http://localhost:7117/query \
120
114
  ```
121
115
 
122
116
  ```bash
123
- curl -s http://localhost:7117/query \
124
- -H 'content-type: application/json' \
125
- -d '{"q": "reviewing code on github"}'
117
+ uvx --with sqlite-vec dirsql query '{"q": "reviewing code on github"}'
126
118
  ```
127
119
 
128
120
  ```json
@@ -133,9 +125,9 @@ Neither question shares a keyword with its top note — "cook" appears
133
125
  nowhere in `pasta.md`, "github" nowhere in `branches.md`. The distance
134
126
  ranking is doing the work.
135
127
 
136
- Because `pre-query` is set, the request body is *not* the usual
137
- `{"sql": …}` — the raw body goes to your script, which decides what SQL
138
- runs ([hook interactions](../reference/http-api.md#hook-interactions)).
128
+ Because `pre-query` is set, the query argument is *not* the usual
129
+ `{"sql": …}` — it goes to your script as-is, which decides what SQL runs
130
+ ([hook interactions](../reference/http-api.md#hook-interactions)).
139
131
 
140
132
  ## Recomputing vs. caching
141
133
 
@@ -31,12 +31,8 @@ ignored file never reaches any table — even one whose glob would match it.
31
31
 
32
32
  ## 2. Confirm what made it in
33
33
 
34
- Start the server (`npx dirsql` / `uvx dirsql`) and check:
35
-
36
34
  ```bash
37
- curl -s http://localhost:7117/query \
38
- -H 'content-type: application/json' \
39
- -d '{"sql":"SELECT _path FROM notes ORDER BY _path"}'
35
+ dirsql query "SELECT _path FROM notes ORDER BY _path"
40
36
  ```
41
37
 
42
38
  ```json
@@ -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.71"
7
+ version = "0.3.73"
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
- `cli_test.py` exercises the pure-Python launcher; it must be runnable
4
- without `maturin develop` having built the PyO3 extension. We stub the
5
- missing module here (rather than in the test file) so the stub is in
6
- place before pytest imports `dirsql.__init__`, which transitively
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
@@ -29,12 +29,8 @@ within one path segment) are in
29
29
 
30
30
  ## 2. Query the captured columns
31
31
 
32
- Start the server (`npx dirsql` / `uvx dirsql`) and query:
33
-
34
32
  ```bash
35
- curl -s http://localhost:7117/query \
36
- -H 'content-type: application/json' \
37
- -d '{"sql":"SELECT year, month, _basename FROM photos ORDER BY year, month"}'
33
+ dirsql query "SELECT year, month, _basename FROM photos ORDER BY year, month"
38
34
  ```
39
35
 
40
36
  ```json
@@ -44,9 +40,7 @@ curl -s http://localhost:7117/query \
44
40
  Captures are real SQL columns, so aggregation works:
45
41
 
46
42
  ```bash
47
- curl -s http://localhost:7117/query \
48
- -H 'content-type: application/json' \
49
- -d '{"sql":"SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"}'
43
+ dirsql query "SELECT year, COUNT(*) AS photos FROM photos GROUP BY year"
50
44
  ```
51
45
 
52
46
  ```json
@@ -24,26 +24,12 @@ glob = "posts/**/*.md"
24
24
  filesystem facts `dirsql` computes for every file. Facts are opt-in by
25
25
  DDL: only the ones you declare become columns.
26
26
 
27
- ## 2. Start the server and query
28
-
29
- ::: code-group
30
-
31
- ```bash [npm]
32
- npx dirsql
33
- ```
34
-
35
- ```bash [PyPI]
36
- uvx dirsql
37
- ```
38
-
39
- :::
27
+ ## 2. Query the table
40
28
 
41
29
  Each matched file is one row:
42
30
 
43
31
  ```bash
44
- curl -s http://localhost:7117/query \
45
- -H 'content-type: application/json' \
46
- -d '{"sql":"SELECT _path, _size FROM posts ORDER BY _path"}'
32
+ dirsql query "SELECT _path, _size FROM posts ORDER BY _path"
47
33
  ```
48
34
 
49
35
  ```json
@@ -31,12 +31,8 @@ by every hook.
31
31
 
32
32
  ## 2. Query the extracted columns
33
33
 
34
- Start the server (`npx dirsql` / `uvx dirsql`) and query:
35
-
36
34
  ```bash
37
- curl -s http://localhost:7117/query \
38
- -H 'content-type: application/json' \
39
- -d '{"sql":"SELECT title, author, year, _path FROM books ORDER BY year"}'
35
+ dirsql query "SELECT title, author, year, _path FROM books ORDER BY year"
40
36
  ```
41
37
 
42
38
  ```json
@@ -61,9 +57,7 @@ on-file = "jq -c -s '.' {path}"
61
57
  ```
62
58
 
63
59
  ```bash
64
- curl -s http://localhost:7117/query \
65
- -H 'content-type: application/json' \
66
- -d '{"sql":"SELECT event, user FROM events"}'
60
+ dirsql query "SELECT event, user FROM events"
67
61
  ```
68
62
 
69
63
  ```json
@@ -23,13 +23,10 @@ overrides the init symbol when it doesn't match the filename-derived
23
23
  default — `sqlite-vec` is exactly such a case
24
24
  ([reference](../reference/config.md#dirsql-extension)).
25
25
 
26
- Start the server (`npx dirsql` / `uvx dirsql`) and the extension's
27
- functions are callable:
26
+ The extension's functions are callable:
28
27
 
29
28
  ```bash
30
- curl -s http://localhost:7117/query \
31
- -H 'content-type: application/json' \
32
- -d '{"sql":"SELECT vec_version() AS vec_version"}'
29
+ dirsql query "SELECT vec_version() AS vec_version"
33
30
  ```
34
31
 
35
32
  ```json
@@ -99,20 +99,14 @@ installed `sqlite_vec` module to its bundled loadable. Naming rules per
99
99
  runtime — and the literal-path alternative that works everywhere — are in
100
100
  [Load a SQLite extension](./load-extension.md).
101
101
 
102
- ## 3. Start the server and ask questions
102
+ ## 3. Ask questions
103
103
 
104
- Launch with `sqlite-vec` available to the launcher's environment:
104
+ Run with `sqlite-vec` available to the launcher's environment. The initial
105
+ scan runs `embed.py` once per note, then the query argument goes straight to
106
+ `pre-query`, exactly as a `POST /query` body would:
105
107
 
106
108
  ```bash
107
- uvx --with sqlite-vec dirsql
108
- ```
109
-
110
- The initial scan runs `embed.py` once per note. Then ask:
111
-
112
- ```bash
113
- curl -s http://localhost:7117/query \
114
- -H 'content-type: application/json' \
115
- -d '{"q": "how do I cook pasta?"}'
109
+ uvx --with sqlite-vec dirsql query '{"q": "how do I cook pasta?"}'
116
110
  ```
117
111
 
118
112
  ```json
@@ -120,9 +114,7 @@ curl -s http://localhost:7117/query \
120
114
  ```
121
115
 
122
116
  ```bash
123
- curl -s http://localhost:7117/query \
124
- -H 'content-type: application/json' \
125
- -d '{"q": "reviewing code on github"}'
117
+ uvx --with sqlite-vec dirsql query '{"q": "reviewing code on github"}'
126
118
  ```
127
119
 
128
120
  ```json
@@ -133,9 +125,9 @@ Neither question shares a keyword with its top note — "cook" appears
133
125
  nowhere in `pasta.md`, "github" nowhere in `branches.md`. The distance
134
126
  ranking is doing the work.
135
127
 
136
- Because `pre-query` is set, the request body is *not* the usual
137
- `{"sql": …}` — the raw body goes to your script, which decides what SQL
138
- runs ([hook interactions](../reference/http-api.md#hook-interactions)).
128
+ Because `pre-query` is set, the query argument is *not* the usual
129
+ `{"sql": …}` — it goes to your script as-is, which decides what SQL runs
130
+ ([hook interactions](../reference/http-api.md#hook-interactions)).
139
131
 
140
132
  ## Recomputing vs. caching
141
133
 
@@ -31,12 +31,8 @@ ignored file never reaches any table — even one whose glob would match it.
31
31
 
32
32
  ## 2. Confirm what made it in
33
33
 
34
- Start the server (`npx dirsql` / `uvx dirsql`) and check:
35
-
36
34
  ```bash
37
- curl -s http://localhost:7117/query \
38
- -H 'content-type: application/json' \
39
- -d '{"sql":"SELECT _path FROM notes ORDER BY _path"}'
35
+ dirsql query "SELECT _path FROM notes ORDER BY _path"
40
36
  ```
41
37
 
42
38
  ```json
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783389455,
3
+ "ran_at": 1783427443,
4
4
  "exit_code": 0,
5
- "commit": "edd5f78fd484ce51829ff3f8387e720b989fb8fb"
5
+ "commit": "16fc390f62d2f4f9d607eb28fc2adc8ee1d6ea0a"
6
6
  }
@@ -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`), or `None` if the DDL doesn't
38
- /// match `CREATE TABLE <name> (...)`. Computed once at construction
39
- /// via `dirsql::db::parse_table_name` so the `dirsql interpret`
40
- /// dispatcher (#196) and other consumers share the core's single
41
- /// source of truth instead of re-parsing DDL. We return `None`
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`]. Mirrors
68
- /// the `[[dirsql.extension]]` config-file fields and the Rust builder's
69
- /// `Extension { path, entrypoint }`. Paths are taken verbatim (the
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 -- #313) and passing the resolved literal
121
- /// paths via `extensions`, so the entries are not loaded twice.
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). Splitting the two keeps the
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