dirsql 0.3.41__tar.gz → 0.3.42__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 (137) hide show
  1. {dirsql-0.3.41 → dirsql-0.3.42}/Cargo.lock +1 -1
  2. {dirsql-0.3.41 → dirsql-0.3.42}/PKG-INFO +1 -1
  3. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/_async.py +0 -30
  4. dirsql-0.3.42/dirsql/cli/interpret/__init__.py +14 -0
  5. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/cli/main.py +2 -12
  6. {dirsql-0.3.41/packages/rust → dirsql-0.3.42}/docs/api/index.md +3 -2
  7. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/Cargo.toml +1 -1
  8. {dirsql-0.3.41 → dirsql-0.3.42/packages/python}/docs/api/index.md +3 -2
  9. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/e2e-attestation.json +2 -2
  10. {dirsql-0.3.41/packages/python → dirsql-0.3.42/packages/rust}/docs/api/index.md +3 -2
  11. {dirsql-0.3.41 → dirsql-0.3.42}/pyproject.toml +1 -1
  12. dirsql-0.3.41/dirsql/cli/interpret/__init__.py +0 -17
  13. dirsql-0.3.41/dirsql/cli/interpret/dispatch_extract.py +0 -41
  14. dirsql-0.3.41/dirsql/cli/interpret/load_app.py +0 -32
  15. dirsql-0.3.41/dirsql/cli/interpret/run.py +0 -85
  16. dirsql-0.3.41/dirsql/cli/interpret/write_message.py +0 -18
  17. dirsql-0.3.41/dirsql/resolve_config.py +0 -61
  18. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/data/a/meta.json +0 -1
  19. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/data/b/meta.json +0 -1
  20. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/dirsql.config.py +0 -29
  21. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/data/a/meta.json +0 -1
  22. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/data/b/meta.json +0 -1
  23. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config.py +0 -27
  24. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_nested.py +0 -13
  25. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_no_app.py +0 -3
  26. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_no_root.py +0 -26
  27. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/dirsql.config_raises.py +0 -21
  28. dirsql-0.3.41/packages/python/tests/e2e/__fixtures__/interpret/nested.dirsql.toml +0 -6
  29. dirsql-0.3.41/packages/python/tests/e2e/interpret_subprocess.py +0 -96
  30. {dirsql-0.3.41 → dirsql-0.3.42}/Cargo.toml +0 -0
  31. {dirsql-0.3.41 → dirsql-0.3.42}/README.md +0 -0
  32. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/__init__.py +0 -0
  33. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/_dirsql.pyi +0 -0
  34. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/cli/__init__.py +0 -0
  35. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/cli/binary_path.py +0 -0
  36. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/cli/is_windows.py +0 -0
  37. {dirsql-0.3.41 → dirsql-0.3.42}/dirsql/py.typed +0 -0
  38. {dirsql-0.3.41 → dirsql-0.3.42}/docs/.claude/CLAUDE.md +0 -0
  39. {dirsql-0.3.41 → dirsql-0.3.42}/docs/.vitepress/config.ts +0 -0
  40. {dirsql-0.3.41 → dirsql-0.3.42}/docs/.vitepress/theme/index.ts +0 -0
  41. {dirsql-0.3.41 → dirsql-0.3.42}/docs/.vitepress/theme/lang.ts +0 -0
  42. {dirsql-0.3.41 → dirsql-0.3.42}/docs/AGENTS.md +0 -0
  43. {dirsql-0.3.41 → dirsql-0.3.42}/docs/cli/config.md +0 -0
  44. {dirsql-0.3.41 → dirsql-0.3.42}/docs/cli/http-api.md +0 -0
  45. {dirsql-0.3.41 → dirsql-0.3.42}/docs/cli/index.md +0 -0
  46. {dirsql-0.3.41 → dirsql-0.3.42}/docs/cli/init.md +0 -0
  47. {dirsql-0.3.41 → dirsql-0.3.42}/docs/cli/server.md +0 -0
  48. {dirsql-0.3.41 → dirsql-0.3.42}/docs/getting-started.md +0 -0
  49. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/async.md +0 -0
  50. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/crdt.md +0 -0
  51. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/persistence.md +0 -0
  52. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/querying.md +0 -0
  53. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/tables.md +0 -0
  54. {dirsql-0.3.41 → dirsql-0.3.42}/docs/guide/watching.md +0 -0
  55. {dirsql-0.3.41 → dirsql-0.3.42}/docs/index.md +0 -0
  56. {dirsql-0.3.41 → dirsql-0.3.42}/docs/migrations.md +0 -0
  57. {dirsql-0.3.41 → dirsql-0.3.42}/docs/package.json +0 -0
  58. {dirsql-0.3.41 → dirsql-0.3.42}/docs/playwright.config.ts +0 -0
  59. {dirsql-0.3.41 → dirsql-0.3.42}/docs/pnpm-lock.yaml +0 -0
  60. {dirsql-0.3.41 → dirsql-0.3.42}/docs/pnpm-workspace.yaml +0 -0
  61. {dirsql-0.3.41 → dirsql-0.3.42}/docs/tests/integration/home.spec.ts +0 -0
  62. {dirsql-0.3.41 → dirsql-0.3.42}/docs/tests/integration/language-flag.spec.ts +0 -0
  63. {dirsql-0.3.41 → dirsql-0.3.42}/docs/tests/integration/sidebar.spec.ts +0 -0
  64. {dirsql-0.3.41 → dirsql-0.3.42}/docs/tests/unit/config.test.ts +0 -0
  65. {dirsql-0.3.41 → dirsql-0.3.42}/docs/tests/unit/lang.test.ts +0 -0
  66. {dirsql-0.3.41 → dirsql-0.3.42}/docs/vitest.config.ts +0 -0
  67. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/README.md +0 -0
  68. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/conftest.py +0 -0
  69. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/.claude/CLAUDE.md +0 -0
  70. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/.vitepress/config.ts +0 -0
  71. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  72. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  73. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/AGENTS.md +0 -0
  74. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/cli/config.md +0 -0
  75. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/cli/http-api.md +0 -0
  76. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/cli/index.md +0 -0
  77. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/cli/init.md +0 -0
  78. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/cli/server.md +0 -0
  79. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/getting-started.md +0 -0
  80. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/async.md +0 -0
  81. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/crdt.md +0 -0
  82. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/persistence.md +0 -0
  83. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/querying.md +0 -0
  84. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/tables.md +0 -0
  85. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/guide/watching.md +0 -0
  86. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/index.md +0 -0
  87. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/migrations.md +0 -0
  88. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/package.json +0 -0
  89. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/playwright.config.ts +0 -0
  90. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/pnpm-lock.yaml +0 -0
  91. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/pnpm-workspace.yaml +0 -0
  92. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  93. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  94. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  95. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/tests/unit/config.test.ts +0 -0
  96. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  97. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/docs/vitest.config.ts +0 -0
  98. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/src/lib.rs +0 -0
  99. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/tests/__init__.py +0 -0
  100. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/tests/conftest.py +0 -0
  101. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/tests/e2e/__init__.py +0 -0
  102. {dirsql-0.3.41 → dirsql-0.3.42}/packages/python/tests/integration/__init__.py +0 -0
  103. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/Cargo.toml +0 -0
  104. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/README.md +0 -0
  105. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/benches/db_bench.rs +0 -0
  106. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/benches/differ_bench.rs +0 -0
  107. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/benches/matcher_bench.rs +0 -0
  108. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/benches/scanner_bench.rs +0 -0
  109. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/cli/config.md +0 -0
  110. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/cli/http-api.md +0 -0
  111. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/cli/index.md +0 -0
  112. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/cli/init.md +0 -0
  113. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/cli/server.md +0 -0
  114. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/getting-started.md +0 -0
  115. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/async.md +0 -0
  116. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/crdt.md +0 -0
  117. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/persistence.md +0 -0
  118. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/querying.md +0 -0
  119. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/tables.md +0 -0
  120. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/guide/watching.md +0 -0
  121. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/index.md +0 -0
  122. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/docs/migrations.md +0 -0
  123. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/bin/dirsql.rs +0 -0
  124. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/init.rs +0 -0
  125. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/mod.rs +0 -0
  126. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/native_config.rs +0 -0
  127. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/router.rs +0 -0
  128. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/serialize.rs +0 -0
  129. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/cli/server.rs +0 -0
  130. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/config.rs +0 -0
  131. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/db.rs +0 -0
  132. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/differ.rs +0 -0
  133. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/lib.rs +0 -0
  134. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/matcher.rs +0 -0
  135. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/persist.rs +0 -0
  136. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/scanner.rs +0 -0
  137. {dirsql-0.3.41 → dirsql-0.3.42}/packages/rust/src/watcher.rs +0 -0
@@ -499,7 +499,7 @@ dependencies = [
499
499
 
500
500
  [[package]]
501
501
  name = "dirsql-py-ext"
502
- version = "0.3.41"
502
+ version = "0.3.42"
503
503
  dependencies = [
504
504
  "dirsql",
505
505
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.41
3
+ Version: 0.3.42
4
4
  Requires-Dist: pytest>=8 ; extra == 'dev'
5
5
  Requires-Dist: pytest-describe>=2 ; extra == 'dev'
6
6
  Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
@@ -1,20 +1,8 @@
1
1
  """Async-by-default DirSQL wrapper."""
2
2
 
3
3
  import asyncio
4
- from typing import TYPE_CHECKING
5
4
 
6
5
  from dirsql._dirsql import DirSQL as _RustDirSQL
7
- from dirsql.resolve_config import resolve_config
8
-
9
- if TYPE_CHECKING:
10
- # `typing.override` is 3.12+; the package supports 3.11, so source it from
11
- # `typing_extensions` for the type checker only. At runtime `@override` is
12
- # a pure marker, so a no-op identity avoids the runtime dependency.
13
- from typing_extensions import override
14
- else:
15
-
16
- def override(func):
17
- return func
18
6
 
19
7
 
20
8
  class _WatchStream:
@@ -135,21 +123,3 @@ class DirSQL:
135
123
  def watch(self):
136
124
  """Start watching for file changes. Returns an async iterable of RowEvent."""
137
125
  return _WatchStream(self._db)
138
-
139
- @property
140
- @override
141
- def __dict__(self):
142
- """Resolved construction state as a JSON-serializable dict.
143
-
144
- Recomputed on each access; reads the ``.dirsql.toml`` if ``config=``
145
- was supplied. Works before ``await db.ready()``.
146
- """
147
- return resolve_config(
148
- self._root,
149
- self._tables,
150
- self._ignore,
151
- self._config,
152
- self._persist,
153
- self._persist_path,
154
- self._extensions,
155
- )
@@ -0,0 +1,14 @@
1
+ """Empty package shell — the native-config ``interpret`` helper was removed.
2
+
3
+ The ``dirsql interpret`` subcommand and its NDJSON ``extract`` loop (``run``,
4
+ ``load_app``, ``dispatch_extract``, ``write_message``) were removed in #321
5
+ (#323). The CLI now accepts only ``.dirsql.toml``; to run user-defined
6
+ ``extract`` callbacks, use the programmatic SDK (``DirSQL(...)`` with
7
+ in-process closures).
8
+
9
+ This ``__init__.py`` carries no logic and no re-exports. It remains only
10
+ because the colocated-test tooling cannot yet express *deleting* an exempt
11
+ package barrel (the co-change check flags a deleted source that has no
12
+ co-deleted colocated test, and a retained exempt for a deleted path is
13
+ rejected as stale). The directory is removed once that is resolved.
14
+ """
@@ -1,8 +1,6 @@
1
1
  """Console-script entry point. Execs the bundled binary on POSIX,
2
- subprocesses it on Windows. When ``argv[0] == "interpret"`` the
3
- in-process Python helper handles the subcommand directly so a Rust
4
- orchestrator can spawn this script for native-language configs (#196)
5
- without depending on the bundled Rust binary."""
2
+ subprocesses it on Windows. All argv is forwarded transparently to the
3
+ bundled Rust binary."""
6
4
 
7
5
  from __future__ import annotations
8
6
 
@@ -18,14 +16,6 @@ def main(argv: list[str] | None = None) -> int:
18
16
  if argv is None:
19
17
  argv = sys.argv[1:]
20
18
 
21
- if argv and argv[0] == "interpret":
22
- from .interpret.run import run
23
-
24
- try:
25
- return run(argv[1:])
26
- except KeyboardInterrupt:
27
- return 130
28
-
29
19
  try:
30
20
  binary = binary_path()
31
21
  except FileNotFoundError as exc:
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169
 
@@ -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.41"
7
+ version = "0.3.42"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1782763249,
3
+ "ran_at": 1782833107,
4
4
  "exit_code": 0,
5
- "commit": "8a3051871b21e04c0c2c2cc11a57e4c0a9b94866"
5
+ "commit": "07bd37825c22f9ad73153d70fc65c9e3c92f855e"
6
6
  }
@@ -59,6 +59,7 @@ new DirSQL({
59
59
  tables?: TableDef[],
60
60
  ignore?: string[],
61
61
  config?: string,
62
+ extensions?: ExtensionSpec[], // [{ path: string, entrypoint?: string }]
62
63
  })
63
64
  ```
64
65
 
@@ -76,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
76
77
  - `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
77
78
  - `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
78
79
  - `config` -- Optional path to a `.dirsql.toml` config file. Its `[[table]]` entries are appended to any programmatic `tables`; its `[dirsql].ignore` patterns are appended to any explicit `ignore`; its optional `[dirsql].root` supplies the root directory when `root` is not passed explicitly; its `[[dirsql.extension]]` entries are appended to any programmatic `extensions`.
79
- - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python and Rust SDKs; the TypeScript constructor parameter is tracked in [#230](https://github.com/thekevinscott/dirsql/issues/230). See [Loading extensions](../cli/config.md#loading-extensions).
80
+ - `extensions` -- Optional SQLite extensions to load onto the connection at startup, before any table DDL (enable → load → disable, so the SQL `load_extension()` function is never left exposed). Each entry pairs a shared-library `path` with an optional `entrypoint` init-symbol override (Python: `{ "path", "entrypoint"? }` dicts; Rust: `Extension { path, entrypoint }`; TypeScript: `{ path, entrypoint? }` objects). Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. Available in the Python, Rust, and TypeScript SDKs. See [Loading extensions](../cli/config.md#loading-extensions).
80
81
 
81
82
  ### Methods
82
83
 
@@ -162,7 +163,7 @@ JSON.stringify(db) // via db.toJSON()
162
163
 
163
164
  :::
164
165
 
165
- Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. The Python and Rust snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured); the TypeScript `toJSON` snapshot will gain it with [#230](https://github.com/thekevinscott/dirsql/issues/230). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
+ Returns the resolved construction state as a JSON-compatible value with fields `root`, `tables`, `ignore`, `persist`, `persist_path` (camelCase `persistPath` in TypeScript). Each table is `{ ddl, glob, strict }`. All three SDK snapshots also include `extensions` -- an array of `{ path, entrypoint }` (empty when none are configured; `entrypoint` is `null` when no override was supplied). Excludes the original `config` path (already merged into `root` / `tables` / `ignore`), per-table `extract`, and per-table `name`. Available immediately after construction in Python and TypeScript; Rust's sync `build()` returns a ready instance.
166
167
 
167
168
  ---
168
169
 
@@ -98,7 +98,7 @@ omit = [
98
98
  # them lets a test double's own branches pad the metric (e.g. a fake's
99
99
  # empty-list return). Exclude test files so the floor reflects only
100
100
  # library code. Recursive glob so nested packages (e.g.
101
- # `dirsql/cli/interpret/`) are covered too.
101
+ # `dirsql/cli/`) are covered too.
102
102
  "**/test_*.py",
103
103
  "**/*_test.py",
104
104
  ]
@@ -1,17 +0,0 @@
1
- """`dirsql interpret <config>` -- long-running native config helper (#196).
2
-
3
- Loads a Python config file, takes its module-level ``app = DirSQL(...)``,
4
- and serves ``extract`` requests over NDJSON on stdin/stdout. One line in,
5
- one line out, sequential.
6
-
7
- See the individual submodules for behavior:
8
- - ``run`` -- subcommand entry point invoked from ``dirsql.cli.main``
9
- - ``load_app`` -- importlib-based config loader
10
- - ``write_message`` -- single-line NDJSON writer
11
- - ``dispatch_extract`` -- per-request handler
12
-
13
- No top-level re-exports: the function is at ``.run.run``. Re-exporting
14
- ``run`` at the package level would shadow the submodule (so
15
- ``from . import run`` in tests would resolve to the function and break
16
- ``patch.object(run_module, "load_app", ...)``).
17
- """
@@ -1,41 +0,0 @@
1
- """Single-request handler for the `dirsql interpret` helper.
2
-
3
- Given a parsed request line and a name->table lookup, call the user's
4
- ``extract`` callback and return the NDJSON response payload the loop
5
- will write back to stdout.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- from typing import Any
11
-
12
-
13
- def dispatch_extract(
14
- req: dict[str, Any],
15
- tables: dict[str, Any],
16
- ) -> dict[str, Any]:
17
- """Build the `{"type": "result", ...}` response for one extract request.
18
-
19
- - Unknown table name -> ``ok: false`` with a name-bearing error.
20
- - User's ``extract`` raising -> ``ok: false`` with ``str(exc)``.
21
- - Success -> ``ok: true`` with the row list the callback returned.
22
-
23
- ``req["id"]`` is echoed back unchanged so the caller can correlate
24
- request and response.
25
- """
26
- rid = req.get("id")
27
- name = req.get("table")
28
- path = req.get("path")
29
- table = tables.get(name) if isinstance(name, str) else None
30
- if table is None:
31
- return {
32
- "type": "result",
33
- "id": rid,
34
- "ok": False,
35
- "error": f"unknown table: {name!r}",
36
- }
37
- try:
38
- rows = table.extract(path)
39
- except Exception as exc:
40
- return {"type": "result", "id": rid, "ok": False, "error": str(exc)}
41
- return {"type": "result", "id": rid, "ok": True, "rows": rows}
@@ -1,32 +0,0 @@
1
- """importlib-based config loader for the `dirsql interpret` helper.
2
-
3
- The user's config is a regular Python module that constructs a
4
- ``DirSQL`` instance and assigns it to a top-level ``app`` name. We
5
- import it via ``importlib.util.spec_from_file_location`` so the user
6
- doesn't have to put their config on ``sys.path``.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import importlib.util
12
- import os
13
- from typing import Any
14
-
15
-
16
- def load_app(config_path: str) -> Any:
17
- """Import ``config_path`` and return its ``app`` attribute.
18
-
19
- Raises ``ImportError`` if the file can't be loaded as a module, and
20
- ``AttributeError`` if the module loads but defines no ``app``.
21
- """
22
- abs_path = os.path.abspath(config_path)
23
- spec = importlib.util.spec_from_file_location("_dirsql_user_config", abs_path)
24
- if spec is None or spec.loader is None:
25
- raise ImportError(f"could not load config: {config_path}")
26
- module = importlib.util.module_from_spec(spec)
27
- spec.loader.exec_module(module)
28
- if not hasattr(module, "app"):
29
- raise AttributeError(
30
- f"{config_path}: module must define a top-level `app = DirSQL(...)`"
31
- )
32
- return module.app
@@ -1,85 +0,0 @@
1
- """Subcommand entry point for `dirsql interpret <config>`.
2
-
3
- Glues the per-purpose helpers (`load_app`, `dispatch_extract`,
4
- `write_message`) into the long-running NDJSON loop:
5
-
6
- handshake (helper -> caller, once on startup):
7
- {"type": "config", "state": <vars(app)>}
8
-
9
- extract request (caller -> helper):
10
- {"type": "extract", "id": <int>, "table": "<name>", "path": "<abs>"}
11
-
12
- extract response (helper -> caller):
13
- {"type": "result", "id": <int>, "ok": true, "rows": [...]}
14
- {"type": "result", "id": <int>, "ok": false, "error": "<msg>"}
15
-
16
- Exits 0 when stdin closes; non-zero with a single ``dirsql interpret:``
17
- line on stderr if the config can't be loaded.
18
- """
19
-
20
- from __future__ import annotations
21
-
22
- import json
23
- import os
24
- import sys
25
-
26
- from .dispatch_extract import dispatch_extract
27
- from .load_app import load_app
28
- from .write_message import write_message
29
-
30
-
31
- def run(argv: list[str]) -> int:
32
- if len(argv) != 1:
33
- sys.stderr.write(
34
- f"dirsql interpret: expected one config path, got {len(argv)}\n"
35
- )
36
- return 1
37
- config_path = argv[0]
38
-
39
- try:
40
- app = load_app(config_path)
41
- except Exception as exc:
42
- sys.stderr.write(f"dirsql interpret: {exc}\n")
43
- return 1
44
-
45
- # A config file describes a single DirSQL; it must not itself delegate to
46
- # another `config=` path. The interpret handshake has no field for a
47
- # nested config and would recurse, so reject it up front.
48
- if app._config is not None:
49
- sys.stderr.write(
50
- "dirsql interpret: a config file cannot itself set config= "
51
- "(nested config is not supported)\n"
52
- )
53
- return 1
54
-
55
- # Name comes from `dirsql::db::parse_table_name` -- the canonical
56
- # core parser, surfaced via PyO3 on `Table.name` (#196). No regex
57
- # duplication on the Python side. Tables with a name the parser
58
- # couldn't extract are skipped here; the malformed DDL would also
59
- # surface as a `DirSqlError::Ddl` during the SDK scan, which
60
- # `await app.ready()` would re-raise -- not interpret's job to
61
- # second-guess.
62
- tables = {t.name: t for t in (app._tables or []) if t.name is not None}
63
-
64
- # When the config supplies neither `root` nor `config=`, the resolved
65
- # root is None. Default it to the process cwd -- the directory the
66
- # `dirsql` command was launched from, which interpret inherits from the
67
- # parent binary -- so a root-less config indexes "here".
68
- state = vars(app)
69
- if state.get("root") is None:
70
- state["root"] = os.getcwd()
71
- write_message({"type": "config", "state": state})
72
-
73
- for line in sys.stdin:
74
- stripped = line.strip()
75
- if not stripped:
76
- continue
77
- try:
78
- req = json.loads(stripped)
79
- except json.JSONDecodeError:
80
- continue
81
- if not isinstance(req, dict) or req.get("type") != "extract":
82
- continue
83
- write_message(dispatch_extract(req, tables))
84
-
85
- return 0
@@ -1,18 +0,0 @@
1
- """Single-line NDJSON writer for the `dirsql interpret` helper."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import sys
7
- from typing import Any
8
-
9
-
10
- def write_message(msg: dict[str, Any]) -> None:
11
- """Write one NDJSON line to stdout and flush.
12
-
13
- The flush is required so the orchestrator on the other side of the
14
- pipe sees the message immediately; without it the buffered stream
15
- would deliver lines in batches.
16
- """
17
- sys.stdout.write(json.dumps(msg) + "\n")
18
- sys.stdout.flush()
@@ -1,61 +0,0 @@
1
- """Synchronous config resolver for `DirSQL.__dict__`.
2
-
3
- Mirrors `DirSQLBuilder::resolve` in the Rust core: explicit kwargs win for
4
- scalars; tables and ignore lists are concatenated; persist is OR-ed;
5
- path-valued config fields resolve relative to the config file's parent.
6
- """
7
-
8
- import os
9
- import tomllib
10
-
11
-
12
- def resolve_config(
13
- root, tables, ignore, config, persist, persist_path, extensions=None
14
- ):
15
- """Merge kwargs with a `.dirsql.toml` into the serialized state shape."""
16
- cfg, cfg_tables, cfg_extensions, cfg_dir = {}, [], [], None
17
- if config is not None:
18
- with open(config, "rb") as f:
19
- doc = tomllib.load(f)
20
- cfg = doc.get("dirsql") or {}
21
- cfg_tables = doc.get("table") or []
22
- cfg_extensions = cfg.get("extension") or []
23
- cfg_dir = os.path.dirname(os.path.abspath(config))
24
-
25
- def _abs(p):
26
- # Only reached from the `"<key>" in cfg` branches below, and cfg is
27
- # non-empty only after a config file was loaded -- which sets cfg_dir.
28
- base = cfg_dir
29
- assert base is not None
30
- return p if os.path.isabs(p) else os.path.join(base, p)
31
-
32
- return {
33
- "root": root or (_abs(cfg["root"]) if "root" in cfg else cfg_dir),
34
- "tables": [
35
- {"ddl": t.ddl, "glob": t.glob, "strict": bool(t.strict)}
36
- for t in (tables or [])
37
- ]
38
- + [
39
- {
40
- "ddl": e.get("ddl"),
41
- "glob": e.get("glob"),
42
- "strict": bool(e.get("strict")),
43
- }
44
- for e in cfg_tables
45
- ],
46
- "ignore": list(ignore or []) + list(cfg.get("ignore") or []),
47
- "persist": bool(persist or cfg.get("persist")),
48
- "persist_path": persist_path
49
- or (_abs(cfg["persist_path"]) if "persist_path" in cfg else None),
50
- # Programmatic extensions first (verbatim paths, mirroring the Rust
51
- # builder), then config-file `[[dirsql.extension]]` entries with
52
- # relative paths resolved against the config's parent directory.
53
- "extensions": [
54
- {"path": e["path"], "entrypoint": e.get("entrypoint")}
55
- for e in (extensions or [])
56
- ]
57
- + [
58
- {"path": _abs(e["path"]), "entrypoint": e.get("entrypoint")}
59
- for e in cfg_extensions
60
- ],
61
- }
@@ -1 +0,0 @@
1
- {"title": "Alpha"}
@@ -1 +0,0 @@
1
- {"title": "Beta"}
@@ -1,29 +0,0 @@
1
- """A native Python config file for dirsql.
2
-
3
- When ``dirsql --config <path-to-this-file>`` runs, the Rust binary
4
- spawns ``dirsql interpret`` against this file. The helper loads the
5
- module, takes its module-level ``app``, and dispatches ``extract``
6
- callbacks over NDJSON.
7
- """
8
-
9
- import json
10
- import os
11
-
12
- from dirsql import DirSQL, Table
13
-
14
-
15
- def _extract(path):
16
- with open(path) as f:
17
- return [json.load(f)]
18
-
19
-
20
- app = DirSQL(
21
- root=os.path.join(os.path.dirname(__file__), "data"),
22
- tables=[
23
- Table(
24
- ddl="CREATE TABLE papers (title TEXT)",
25
- glob="**/meta.json",
26
- extract=_extract,
27
- )
28
- ],
29
- )
@@ -1,27 +0,0 @@
1
- """Happy-path fixture for `dirsql interpret` integration tests.
2
-
3
- Exposes a single `papers` table whose `extract` reads each `meta.json`
4
- under the colocated `data/` tree and returns its parsed contents.
5
- """
6
-
7
- import json
8
- import os
9
-
10
- from dirsql import DirSQL, Table
11
-
12
-
13
- def _extract(path):
14
- with open(path, encoding="utf-8") as f:
15
- return [json.load(f)]
16
-
17
-
18
- app = DirSQL(
19
- root=os.path.join(os.path.dirname(__file__), "data"),
20
- tables=[
21
- Table(
22
- ddl="CREATE TABLE papers (title TEXT)",
23
- glob="**/meta.json",
24
- extract=_extract,
25
- )
26
- ],
27
- )
@@ -1,13 +0,0 @@
1
- """Fixture: a native Python config that itself sets ``config=``.
2
-
3
- `dirsql interpret` must reject this -- a config file loaded by interpret
4
- cannot delegate to another config file (nested config loading). The
5
- referenced TOML is valid, so the rejection comes from the loader, not from
6
- a TOML read error.
7
- """
8
-
9
- import os
10
-
11
- from dirsql import DirSQL
12
-
13
- app = DirSQL(config=os.path.join(os.path.dirname(__file__), "nested.dirsql.toml"))
@@ -1,3 +0,0 @@
1
- """Fixture without a top-level `app` -- exercises startup failure."""
2
-
3
- NOT_THE_APP = 42
@@ -1,26 +0,0 @@
1
- """Fixture: a native Python config that omits ``root``.
2
-
3
- `dirsql interpret` should resolve the root to the helper process's current
4
- working directory (the cwd the orchestrator was launched from) rather than
5
- erroring. Mirrors ``dirsql.config.py`` but drops the ``root=`` argument.
6
- """
7
-
8
- import json
9
-
10
- from dirsql import DirSQL, Table
11
-
12
-
13
- def _extract(path):
14
- with open(path, encoding="utf-8") as f:
15
- return [json.load(f)]
16
-
17
-
18
- app = DirSQL(
19
- tables=[
20
- Table(
21
- ddl="CREATE TABLE papers (title TEXT)",
22
- glob="**/meta.json",
23
- extract=_extract,
24
- )
25
- ],
26
- )
@@ -1,21 +0,0 @@
1
- """Fixture whose `extract` raises -- exercises the `ok: false` response."""
2
-
3
- import os
4
-
5
- from dirsql import DirSQL, Table
6
-
7
-
8
- def _boom(_path):
9
- raise ValueError("synthetic extract failure")
10
-
11
-
12
- app = DirSQL(
13
- root=os.path.join(os.path.dirname(__file__), "data"),
14
- tables=[
15
- Table(
16
- ddl="CREATE TABLE papers (title TEXT)",
17
- glob="**/meta.json",
18
- extract=_boom,
19
- )
20
- ],
21
- )
@@ -1,6 +0,0 @@
1
- # Valid config referenced by `dirsql.config_nested.py`. Its validity is the
2
- # point: the interpret loader must reject the *nesting*, not fail on a bad
3
- # TOML read.
4
- [[table]]
5
- ddl = "CREATE TABLE papers (title TEXT)"
6
- glob = "**/meta.json"
@@ -1,96 +0,0 @@
1
- """Subprocess plumbing for the `dirsql interpret` e2e tests.
2
-
3
- Kept in its own module (rather than `conftest.py` or inline in the
4
- test file) because these helpers are not pytest fixtures -- they are
5
- plain functions that drive a child process via NDJSON. Importing them
6
- keeps each test focused on the assertion, not the framing.
7
-
8
- Naming intentionally does not match `test_*.py` / `*_test.py` so
9
- pytest skips collection.
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import json
15
- import shutil
16
- import subprocess
17
- import threading
18
- from pathlib import Path
19
- from typing import Any
20
-
21
-
22
- def cli_argv() -> list[str]:
23
- """Resolve the `dirsql` console-script invocation for this test env.
24
-
25
- Failing loudly here surfaces an environment misconfiguration rather
26
- than masking it as a test assertion failure further down.
27
- """
28
- dirsql = shutil.which("dirsql")
29
- assert dirsql is not None, (
30
- "`dirsql` console script not on PATH -- run `uv run maturin develop`"
31
- )
32
- return [dirsql]
33
-
34
-
35
- def spawn(config_path: Path) -> subprocess.Popen:
36
- """Start `dirsql interpret <config>` with piped stdin/stdout/stderr."""
37
- return subprocess.Popen(
38
- [*cli_argv(), "interpret", str(config_path)],
39
- stdin=subprocess.PIPE,
40
- stdout=subprocess.PIPE,
41
- stderr=subprocess.PIPE,
42
- text=True,
43
- bufsize=1,
44
- )
45
-
46
-
47
- def readline(proc: subprocess.Popen, timeout: float = 5.0) -> str:
48
- """Read one stdout line with a timeout.
49
-
50
- On timeout, kill the process and surface stderr so the failure
51
- message is actionable rather than an opaque hang.
52
- """
53
- result: list[str | None] = [None]
54
-
55
- def reader() -> None:
56
- assert proc.stdout is not None
57
- result[0] = proc.stdout.readline()
58
-
59
- t = threading.Thread(target=reader, daemon=True)
60
- t.start()
61
- t.join(timeout)
62
- if t.is_alive():
63
- proc.kill()
64
- stderr = proc.stderr.read() if proc.stderr else ""
65
- raise AssertionError(
66
- f"timed out waiting for stdout line; stderr was:\n{stderr}"
67
- )
68
- line = result[0] or ""
69
- if not line:
70
- stderr = proc.stderr.read() if proc.stderr else ""
71
- raise AssertionError(
72
- f"helper exited (code={proc.returncode}) before writing a line; "
73
- f"stderr was:\n{stderr}"
74
- )
75
- return line
76
-
77
-
78
- def send(proc: subprocess.Popen, msg: dict[str, Any]) -> None:
79
- """Write one NDJSON line to the helper's stdin."""
80
- assert proc.stdin is not None
81
- proc.stdin.write(json.dumps(msg) + "\n")
82
- proc.stdin.flush()
83
-
84
-
85
- def shutdown(proc: subprocess.Popen) -> None:
86
- """Close stdin and wait for the helper to exit cleanly."""
87
- if proc.stdin is not None:
88
- try:
89
- proc.stdin.close()
90
- except BrokenPipeError:
91
- pass
92
- try:
93
- proc.wait(timeout=5)
94
- except subprocess.TimeoutExpired:
95
- proc.kill()
96
- proc.wait(timeout=2)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes