dirsql 0.3.46__tar.gz → 0.3.48__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.46 → dirsql-0.3.48}/Cargo.lock +1 -1
- {dirsql-0.3.46 → dirsql-0.3.48}/PKG-INFO +1 -1
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/_async.py +23 -1
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/cli/main.py +9 -0
- dirsql-0.3.48/dirsql/cli/resolve_config_extensions.py +81 -0
- dirsql-0.3.48/dirsql/resolve_extension.py +106 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/api/index.md +1 -1
- {dirsql-0.3.46/packages/python → dirsql-0.3.48}/docs/cli/config.md +5 -2
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/Cargo.toml +1 -1
- {dirsql-0.3.46/packages/rust → dirsql-0.3.48/packages/python}/docs/api/index.md +1 -1
- {dirsql-0.3.46 → dirsql-0.3.48/packages/python}/docs/cli/config.md +5 -2
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/e2e-attestation.json +2 -2
- {dirsql-0.3.46/packages/python → dirsql-0.3.48/packages/rust}/docs/api/index.md +1 -1
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/cli/config.md +5 -2
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/bin/dirsql.rs +71 -2
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/lib.rs +32 -11
- {dirsql-0.3.46 → dirsql-0.3.48}/Cargo.toml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/README.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/cli/binary_path.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/cli/interpret/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/cli/is_windows.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/dirsql/py.typed +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/AGENTS.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/cli/http-api.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/cli/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/cli/init.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/cli/server.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/getting-started.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/async.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/crdt.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/persistence.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/querying.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/tables.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/guide/watching.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/migrations.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/package.json +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/playwright.config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/tests/integration/sidebar.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/docs/vitest.config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/README.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/conftest.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/cli/http-api.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/cli/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/cli/init.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/cli/server.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/async.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/crdt.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/persistence.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/querying.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/tables.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/guide/watching.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/migrations.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/package.json +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/playwright.config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/vitest.config.ts +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/src/lib.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/README.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/cli/http-api.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/cli/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/cli/init.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/cli/server.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/async.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/crdt.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/persistence.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/querying.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/tables.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/guide/watching.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/index.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/docs/migrations.md +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/command.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/config.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/db.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/packages/rust/src/watcher.rs +0 -0
- {dirsql-0.3.46 → dirsql-0.3.48}/pyproject.toml +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Async-by-default DirSQL wrapper."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import os
|
|
4
5
|
|
|
5
6
|
from dirsql._dirsql import DirSQL as _RustDirSQL
|
|
7
|
+
from dirsql.resolve_extension import resolve_extension_path
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class _WatchStream:
|
|
@@ -91,13 +93,33 @@ class DirSQL:
|
|
|
91
93
|
config=self._config,
|
|
92
94
|
persist=self._persist,
|
|
93
95
|
persist_path=self._persist_path,
|
|
94
|
-
extensions=self.
|
|
96
|
+
extensions=self._resolved_extensions(),
|
|
95
97
|
)
|
|
96
98
|
except Exception as exc:
|
|
97
99
|
self._init_error = exc
|
|
98
100
|
finally:
|
|
99
101
|
self._ready_event.set()
|
|
100
102
|
|
|
103
|
+
def _resolved_extensions(self):
|
|
104
|
+
"""Resolve each programmatic extension's ``path`` to a loadable file.
|
|
105
|
+
|
|
106
|
+
A bare package name is resolved to the loadable installed in the runtime
|
|
107
|
+
env (#298); path-looking values are passed through verbatim (mirroring
|
|
108
|
+
the Rust builder, which takes programmatic paths as-is). Config-file
|
|
109
|
+
``[[dirsql.extension]]`` entries are resolved by the Rust core, not here.
|
|
110
|
+
"""
|
|
111
|
+
if not self._extensions:
|
|
112
|
+
return self._extensions
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
"path": resolve_extension_path(
|
|
116
|
+
e["path"], base=os.getcwd(), resolve_relative=False
|
|
117
|
+
),
|
|
118
|
+
"entrypoint": e.get("entrypoint"),
|
|
119
|
+
}
|
|
120
|
+
for e in self._extensions
|
|
121
|
+
]
|
|
122
|
+
|
|
101
123
|
async def ready(self):
|
|
102
124
|
"""Wait until the initial scan is complete.
|
|
103
125
|
|
|
@@ -10,6 +10,7 @@ import sys
|
|
|
10
10
|
|
|
11
11
|
from .binary_path import binary_path
|
|
12
12
|
from .is_windows import is_windows
|
|
13
|
+
from .resolve_config_extensions import with_resolved_extensions
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -22,6 +23,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
22
23
|
print(f"dirsql: {exc}", file=sys.stderr)
|
|
23
24
|
return 1
|
|
24
25
|
|
|
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).
|
|
28
|
+
try:
|
|
29
|
+
argv = with_resolved_extensions(argv)
|
|
30
|
+
except Exception as exc:
|
|
31
|
+
print(f"dirsql: {exc}", file=sys.stderr)
|
|
32
|
+
return 1
|
|
33
|
+
|
|
25
34
|
if is_windows():
|
|
26
35
|
completed = subprocess.run([binary, *argv])
|
|
27
36
|
return completed.returncode
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Launcher-side resolution of a TOML config's ``[[dirsql.extension]]`` entries.
|
|
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, we resolve
|
|
7
|
+
every one of its extensions here and pass the resolved literal paths to the
|
|
8
|
+
binary via repeatable ``--extension`` flags; the binary then loads those and
|
|
9
|
+
ignores the config's own extension entries (the Rust ``--extension`` flag /
|
|
10
|
+
``suppress_config_extensions``).
|
|
11
|
+
|
|
12
|
+
Native-language configs (``.py`` / ``.js`` / ``.mjs`` / ``.cjs``) are untouched:
|
|
13
|
+
the binary dispatches those to ``dirsql interpret``, whose handshake already
|
|
14
|
+
carries resolved paths.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import tomllib
|
|
21
|
+
|
|
22
|
+
from ..resolve_extension import is_bare_name, resolve_extension_path
|
|
23
|
+
|
|
24
|
+
# Config extensions the binary dispatches to `dirsql interpret`; never
|
|
25
|
+
# pre-resolved here (that path resolves via the handshake).
|
|
26
|
+
_NATIVE_SUFFIXES = (".py", ".js", ".mjs", ".cjs")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _config_path_from_argv(argv: list[str]) -> str:
|
|
30
|
+
"""The ``--config`` value (``--config X`` or ``--config=X``), or the default."""
|
|
31
|
+
i = 0
|
|
32
|
+
while i < len(argv):
|
|
33
|
+
a = argv[i]
|
|
34
|
+
if a == "--config":
|
|
35
|
+
return argv[i + 1] if i + 1 < len(argv) else ""
|
|
36
|
+
if a.startswith("--config="):
|
|
37
|
+
return a[len("--config=") :]
|
|
38
|
+
i += 1
|
|
39
|
+
return "./.dirsql.toml"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def with_resolved_extensions(argv: list[str]) -> list[str]:
|
|
43
|
+
"""Return ``argv`` plus ``--extension`` flags when the TOML config names an
|
|
44
|
+
extension by package name; otherwise return ``argv`` unchanged. Raises if a
|
|
45
|
+
package name cannot be resolved (the launcher surfaces a clean error)."""
|
|
46
|
+
if argv and argv[0] == "init":
|
|
47
|
+
return argv
|
|
48
|
+
config_path = _config_path_from_argv(argv)
|
|
49
|
+
if config_path.endswith(_NATIVE_SUFFIXES):
|
|
50
|
+
return argv
|
|
51
|
+
if not os.path.isfile(config_path):
|
|
52
|
+
return argv
|
|
53
|
+
try:
|
|
54
|
+
with open(config_path, "rb") as f:
|
|
55
|
+
doc = tomllib.load(f)
|
|
56
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
57
|
+
# Leave a malformed / unreadable config for the binary to report.
|
|
58
|
+
return argv
|
|
59
|
+
|
|
60
|
+
cfg = doc.get("dirsql") or {}
|
|
61
|
+
entries = cfg.get("extension") or []
|
|
62
|
+
if not isinstance(entries, list) or not entries:
|
|
63
|
+
return argv
|
|
64
|
+
# Only intervene when at least one path is a bare package name; a config
|
|
65
|
+
# with only literal paths keeps the binary's existing behavior untouched.
|
|
66
|
+
if not any(
|
|
67
|
+
isinstance(e, dict)
|
|
68
|
+
and isinstance(e.get("path"), str)
|
|
69
|
+
and is_bare_name(e["path"])
|
|
70
|
+
for e in entries
|
|
71
|
+
):
|
|
72
|
+
return argv
|
|
73
|
+
|
|
74
|
+
base = os.path.dirname(os.path.abspath(config_path))
|
|
75
|
+
flags: list[str] = []
|
|
76
|
+
for e in entries:
|
|
77
|
+
path = resolve_extension_path(e["path"], base=base, resolve_relative=True)
|
|
78
|
+
entrypoint = e.get("entrypoint")
|
|
79
|
+
flags.append("--extension")
|
|
80
|
+
flags.append(f"{path}::{entrypoint}" if isinstance(entrypoint, str) else path)
|
|
81
|
+
return [*argv, *flags]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Resolve an extension entry's ``path`` to a concrete loadable file.
|
|
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:
|
|
11
|
+
|
|
12
|
+
1. **Path-looking** (contains a separator, or ends in ``.so`` / ``.dylib`` /
|
|
13
|
+
``.dll`` / ``.pyd``) -- returned as a file path: made absolute against
|
|
14
|
+
``base`` when ``resolve_relative`` is set (config-file entries), else
|
|
15
|
+
verbatim (programmatic entries, mirroring the Rust builder).
|
|
16
|
+
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.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import glob as _glob
|
|
24
|
+
import importlib.util
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
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.
|
|
31
|
+
_LOADABLE_SUFFIXES = (".so", ".dylib", ".dll", ".pyd")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _platform_patterns():
|
|
35
|
+
"""Loadable-file glob(s) for the current platform."""
|
|
36
|
+
if sys.platform == "darwin":
|
|
37
|
+
return ("*.dylib",)
|
|
38
|
+
if sys.platform == "win32":
|
|
39
|
+
return ("*.dll", "*.pyd")
|
|
40
|
+
return ("*.so",)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_bare_name(path):
|
|
44
|
+
"""True when ``path`` is a bare package name rather than a file path."""
|
|
45
|
+
if os.sep in path or (os.altsep and os.altsep in path):
|
|
46
|
+
return False
|
|
47
|
+
return not path.endswith(_LOADABLE_SUFFIXES)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _resolve_package(name):
|
|
51
|
+
"""Locate ``name``'s package dir and glob its platform loadable file."""
|
|
52
|
+
try:
|
|
53
|
+
spec = importlib.util.find_spec(name)
|
|
54
|
+
except (ImportError, ValueError) as exc:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"could not resolve extension package {name!r}: {exc}"
|
|
57
|
+
) from exc
|
|
58
|
+
if spec is None:
|
|
59
|
+
raise ValueError(f"could not resolve extension package {name!r}: not installed")
|
|
60
|
+
|
|
61
|
+
dirs = list(spec.submodule_search_locations or [])
|
|
62
|
+
if not dirs and spec.origin and spec.origin not in ("built-in", "frozen"):
|
|
63
|
+
dirs.append(os.path.dirname(spec.origin))
|
|
64
|
+
if not dirs:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"could not resolve extension package {name!r}: no package directory"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
patterns = _platform_patterns()
|
|
70
|
+
matches = set()
|
|
71
|
+
for d in dirs:
|
|
72
|
+
for pat in patterns:
|
|
73
|
+
matches.update(_glob.glob(os.path.join(d, "**", pat), recursive=True))
|
|
74
|
+
found = sorted(matches)
|
|
75
|
+
|
|
76
|
+
pat_desc = " / ".join(patterns)
|
|
77
|
+
if not found:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"no loadable extension file ({pat_desc}) found in package "
|
|
80
|
+
f"{name!r} (searched {', '.join(dirs)})"
|
|
81
|
+
)
|
|
82
|
+
if len(found) > 1:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"multiple loadable extension files found in package {name!r}: "
|
|
85
|
+
f"{', '.join(found)}; disambiguate with a literal path"
|
|
86
|
+
)
|
|
87
|
+
return found[0]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def resolve_extension_path(path, base, resolve_relative):
|
|
91
|
+
"""Resolve an extension ``path`` to a concrete file.
|
|
92
|
+
|
|
93
|
+
``base`` is the directory a relative path and the bare-name shadow probe
|
|
94
|
+
resolve against (a config file's parent dir, or the cwd for programmatic
|
|
95
|
+
entries). ``resolve_relative`` makes a relative path-looking value absolute
|
|
96
|
+
against ``base`` (config-file semantics); when false it is returned verbatim
|
|
97
|
+
(programmatic semantics).
|
|
98
|
+
"""
|
|
99
|
+
if not is_bare_name(path):
|
|
100
|
+
if resolve_relative and not os.path.isabs(path):
|
|
101
|
+
return os.path.join(base, path)
|
|
102
|
+
return path
|
|
103
|
+
local = os.path.join(base, path)
|
|
104
|
+
if os.path.isfile(local):
|
|
105
|
+
return local
|
|
106
|
+
return _resolve_package(path)
|
|
@@ -77,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
|
|
|
77
77
|
- `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
|
|
78
78
|
- `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
|
|
79
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`.
|
|
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`.
|
|
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). A `path` is either a file path or a bare **package name**; a package name is resolved from the installed package (Python `importlib` / TypeScript `node_modules`, file-first, erroring on zero or multiple matches). This works on the Python/TypeScript constructor and CLI; the Rust binary and SDK are file-path-only. Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. See [Loading extensions](../cli/config.md#loading-extensions).
|
|
81
81
|
|
|
82
82
|
### Methods
|
|
83
83
|
|
|
@@ -158,8 +158,11 @@ path = "./ext/myext.dylib"
|
|
|
158
158
|
entrypoint = "sqlite3_myext_init"
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
- **`path`** —
|
|
162
|
-
`.
|
|
161
|
+
- **`path`** — the extension's shared library: either a file path (`.so` /
|
|
162
|
+
`.dylib` / `.dll`, relative to the config file's directory) or a bare
|
|
163
|
+
**package name**. A package name is resolved from the installed package when
|
|
164
|
+
run through the pip/npm `dirsql` CLI; the standalone Rust binary is
|
|
165
|
+
file-path-only.
|
|
163
166
|
- **`entrypoint`** *(optional)* — the extension's init symbol. When omitted,
|
|
164
167
|
SQLite derives a default from the filename; set it when that default does not
|
|
165
168
|
match (for example, `sqlite-vec`'s entry point is `sqlite3_vec_init`).
|
|
@@ -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.48"
|
|
8
8
|
edition.workspace = true
|
|
9
9
|
publish = false
|
|
10
10
|
readme = "README.md"
|
|
@@ -77,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
|
|
|
77
77
|
- `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
|
|
78
78
|
- `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
|
|
79
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`.
|
|
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`.
|
|
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). A `path` is either a file path or a bare **package name**; a package name is resolved from the installed package (Python `importlib` / TypeScript `node_modules`, file-first, erroring on zero or multiple matches). This works on the Python/TypeScript constructor and CLI; the Rust binary and SDK are file-path-only. Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. See [Loading extensions](../cli/config.md#loading-extensions).
|
|
81
81
|
|
|
82
82
|
### Methods
|
|
83
83
|
|
|
@@ -158,8 +158,11 @@ path = "./ext/myext.dylib"
|
|
|
158
158
|
entrypoint = "sqlite3_myext_init"
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
- **`path`** —
|
|
162
|
-
`.
|
|
161
|
+
- **`path`** — the extension's shared library: either a file path (`.so` /
|
|
162
|
+
`.dylib` / `.dll`, relative to the config file's directory) or a bare
|
|
163
|
+
**package name**. A package name is resolved from the installed package when
|
|
164
|
+
run through the pip/npm `dirsql` CLI; the standalone Rust binary is
|
|
165
|
+
file-path-only.
|
|
163
166
|
- **`entrypoint`** *(optional)* — the extension's init symbol. When omitted,
|
|
164
167
|
SQLite derives a default from the filename; set it when that default does not
|
|
165
168
|
match (for example, `sqlite-vec`'s entry point is `sqlite3_vec_init`).
|
|
@@ -77,7 +77,7 @@ In Python, the constructor starts scanning in a background thread and returns im
|
|
|
77
77
|
- `tables` -- List of `Table` definitions. Each defines a SQLite table, a glob pattern, and an extract function.
|
|
78
78
|
- `ignore` -- Optional list of glob patterns. Files matching any ignore pattern are skipped regardless of table globs.
|
|
79
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`.
|
|
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`.
|
|
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). A `path` is either a file path or a bare **package name**; a package name is resolved from the installed package (Python `importlib` / TypeScript `node_modules`, file-first, erroring on zero or multiple matches). This works on the Python/TypeScript constructor and CLI; the Rust binary and SDK are file-path-only. Programmatic entries load first, then any `[[dirsql.extension]]` from `config`. See [Loading extensions](../cli/config.md#loading-extensions).
|
|
81
81
|
|
|
82
82
|
### Methods
|
|
83
83
|
|
|
@@ -158,8 +158,11 @@ path = "./ext/myext.dylib"
|
|
|
158
158
|
entrypoint = "sqlite3_myext_init"
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
- **`path`** —
|
|
162
|
-
`.
|
|
161
|
+
- **`path`** — the extension's shared library: either a file path (`.so` /
|
|
162
|
+
`.dylib` / `.dll`, relative to the config file's directory) or a bare
|
|
163
|
+
**package name**. A package name is resolved from the installed package when
|
|
164
|
+
run through the pip/npm `dirsql` CLI; the standalone Rust binary is
|
|
165
|
+
file-path-only.
|
|
163
166
|
- **`entrypoint`** *(optional)* — the extension's init symbol. When omitted,
|
|
164
167
|
SQLite derives a default from the filename; set it when that default does not
|
|
165
168
|
match (for example, `sqlite-vec`'s entry point is `sqlite3_vec_init`).
|
|
@@ -9,7 +9,7 @@ use std::process::ExitCode;
|
|
|
9
9
|
|
|
10
10
|
use clap::{Args, Parser, Subcommand};
|
|
11
11
|
use dirsql::cli::{AppState, PreQuery, ServerConfig, init::InitOptions, serve_with_state};
|
|
12
|
-
use dirsql::{DirSQL, Row, Table};
|
|
12
|
+
use dirsql::{DirSQL, Extension, Row, Table};
|
|
13
13
|
|
|
14
14
|
#[derive(Debug, Parser)]
|
|
15
15
|
#[command(
|
|
@@ -41,6 +41,19 @@ struct Cli {
|
|
|
41
41
|
/// TCP port to bind. Used when no subcommand is given.
|
|
42
42
|
#[arg(long, default_value_t = 7117)]
|
|
43
43
|
port: u16,
|
|
44
|
+
|
|
45
|
+
/// Load a SQLite extension by literal path, overriding a TOML config's
|
|
46
|
+
/// `[[dirsql.extension]]` entries. Repeatable. Format: `<path>` or
|
|
47
|
+
/// `<path>::<entrypoint>`.
|
|
48
|
+
///
|
|
49
|
+
/// Intended for the language launcher (pip/npm), not end users: the
|
|
50
|
+
/// launcher resolves config extensions — including bare **package names**,
|
|
51
|
+
/// which need an interpreter this compiled binary lacks (see #227) — and
|
|
52
|
+
/// passes the resolved literal paths here. When any are present, the TOML
|
|
53
|
+
/// config's own extension entries are not loaded (the launcher already
|
|
54
|
+
/// merged and resolved them).
|
|
55
|
+
#[arg(long = "extension")]
|
|
56
|
+
extension: Vec<String>,
|
|
44
57
|
}
|
|
45
58
|
|
|
46
59
|
#[derive(Debug, Subcommand)]
|
|
@@ -155,12 +168,46 @@ fn load_state(cli: &Cli) -> AppState {
|
|
|
155
168
|
}
|
|
156
169
|
};
|
|
157
170
|
|
|
158
|
-
|
|
171
|
+
// Launcher-resolved extensions (`--extension`) override the TOML config's
|
|
172
|
+
// own `[[dirsql.extension]]` entries: the launcher has already merged and
|
|
173
|
+
// resolved them (including package names the compiled binary can't resolve;
|
|
174
|
+
// #227), so build from the config but suppress its extension loading and
|
|
175
|
+
// supply the resolved literal paths instead.
|
|
176
|
+
let build = if cli.extension.is_empty() {
|
|
177
|
+
DirSQL::from_config_path(&resolved)
|
|
178
|
+
} else {
|
|
179
|
+
DirSQL::builder()
|
|
180
|
+
.config(&resolved)
|
|
181
|
+
.extensions(parse_extension_specs(&cli.extension))
|
|
182
|
+
.suppress_config_extensions(true)
|
|
183
|
+
.build()
|
|
184
|
+
};
|
|
185
|
+
match build {
|
|
159
186
|
Ok(db) => AppState::Ready(db),
|
|
160
187
|
Err(err) => AppState::Unavailable(format!("failed to load config: {err}")),
|
|
161
188
|
}
|
|
162
189
|
}
|
|
163
190
|
|
|
191
|
+
/// Parse `--extension` specs (`<path>` or `<path>::<entrypoint>`) into
|
|
192
|
+
/// [`Extension`]s. Splitting on the first `::` keeps a path that itself
|
|
193
|
+
/// contains `::` unambiguous only after the entrypoint boundary — entrypoints
|
|
194
|
+
/// are C identifiers, so the first `::` is the boundary.
|
|
195
|
+
fn parse_extension_specs(specs: &[String]) -> Vec<Extension> {
|
|
196
|
+
specs
|
|
197
|
+
.iter()
|
|
198
|
+
.map(|spec| match spec.split_once("::") {
|
|
199
|
+
Some((path, entrypoint)) => Extension {
|
|
200
|
+
path: PathBuf::from(path),
|
|
201
|
+
entrypoint: Some(entrypoint.to_string()),
|
|
202
|
+
},
|
|
203
|
+
None => Extension {
|
|
204
|
+
path: PathBuf::from(spec),
|
|
205
|
+
entrypoint: None,
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
.collect()
|
|
209
|
+
}
|
|
210
|
+
|
|
164
211
|
/// Extract the server-wide `pre-query` hook from the config, if any.
|
|
165
212
|
///
|
|
166
213
|
/// Returns `None` when the config is absent, unresolvable, unparsable, or
|
|
@@ -243,6 +290,28 @@ async fn wait_for_shutdown() -> std::io::Result<()> {
|
|
|
243
290
|
mod tests {
|
|
244
291
|
use super::*;
|
|
245
292
|
|
|
293
|
+
#[test]
|
|
294
|
+
fn parse_extension_specs_handles_bare_path_and_entrypoint() {
|
|
295
|
+
let specs = vec![
|
|
296
|
+
"/abs/vec0.so".to_string(),
|
|
297
|
+
"/abs/spellfix.so::sqlite3_spellfix_init".to_string(),
|
|
298
|
+
];
|
|
299
|
+
let exts = parse_extension_specs(&specs);
|
|
300
|
+
assert_eq!(exts.len(), 2);
|
|
301
|
+
assert_eq!(exts[0].path, PathBuf::from("/abs/vec0.so"));
|
|
302
|
+
assert!(exts[0].entrypoint.is_none());
|
|
303
|
+
assert_eq!(exts[1].path, PathBuf::from("/abs/spellfix.so"));
|
|
304
|
+
assert_eq!(exts[1].entrypoint.as_deref(), Some("sqlite3_spellfix_init"));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#[test]
|
|
308
|
+
fn parse_extension_specs_splits_on_first_double_colon() {
|
|
309
|
+
let specs = vec!["/a.so::init::extra".to_string()];
|
|
310
|
+
let exts = parse_extension_specs(&specs);
|
|
311
|
+
assert_eq!(exts[0].path, PathBuf::from("/a.so"));
|
|
312
|
+
assert_eq!(exts[0].entrypoint.as_deref(), Some("init::extra"));
|
|
313
|
+
}
|
|
314
|
+
|
|
246
315
|
#[test]
|
|
247
316
|
fn default_files_table_declares_filesystem_fact_columns_over_recursive_glob() {
|
|
248
317
|
// The zero-config fallback table is pure data: a fixed DDL naming only
|
|
@@ -920,6 +920,7 @@ pub struct DirSQLBuilder {
|
|
|
920
920
|
ignore: Vec<String>,
|
|
921
921
|
extensions: Vec<Extension>,
|
|
922
922
|
config_path: Option<PathBuf>,
|
|
923
|
+
suppress_config_extensions: bool,
|
|
923
924
|
persist: bool,
|
|
924
925
|
persist_path: Option<PathBuf>,
|
|
925
926
|
poll_interval: Option<Duration>,
|
|
@@ -987,6 +988,20 @@ impl DirSQLBuilder {
|
|
|
987
988
|
self
|
|
988
989
|
}
|
|
989
990
|
|
|
991
|
+
/// Suppress loading of a config file's `[[dirsql.extension]]` entries.
|
|
992
|
+
///
|
|
993
|
+
/// The core resolves config-file extension paths only literally (relative
|
|
994
|
+
/// to the config's parent). A launcher that resolves extensions itself —
|
|
995
|
+
/// e.g. by **package name**, which needs an interpreter the compiled core
|
|
996
|
+
/// lacks (Python `importlib`, Node `require.resolve`; see #227) — sets this
|
|
997
|
+
/// and supplies the already-resolved literal paths via
|
|
998
|
+
/// [`extensions`](Self::extensions) instead, so the config's own extension
|
|
999
|
+
/// entries are not loaded a second time.
|
|
1000
|
+
pub fn suppress_config_extensions(mut self, suppress: bool) -> Self {
|
|
1001
|
+
self.suppress_config_extensions = suppress;
|
|
1002
|
+
self
|
|
1003
|
+
}
|
|
1004
|
+
|
|
990
1005
|
/// Enable persistent on-disk storage. When `true`, the SQLite database is
|
|
991
1006
|
/// written to `<root>/.dirsql/cache.db` (override via
|
|
992
1007
|
/// [`persist_path`](Self::persist_path)) so subsequent startups only
|
|
@@ -1025,6 +1040,7 @@ impl DirSQLBuilder {
|
|
|
1025
1040
|
mut ignore,
|
|
1026
1041
|
mut extensions,
|
|
1027
1042
|
config_path,
|
|
1043
|
+
suppress_config_extensions,
|
|
1028
1044
|
mut persist,
|
|
1029
1045
|
mut persist_path,
|
|
1030
1046
|
poll_interval,
|
|
@@ -1058,17 +1074,22 @@ impl DirSQLBuilder {
|
|
|
1058
1074
|
|
|
1059
1075
|
// Resolve config-supplied extension paths against the config
|
|
1060
1076
|
// file's parent directory (absolute paths pass through). Appended
|
|
1061
|
-
// after any programmatically-supplied extensions.
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1077
|
+
// after any programmatically-supplied extensions. Skipped entirely
|
|
1078
|
+
// when the caller has pre-resolved the config's extensions itself
|
|
1079
|
+
// (e.g. a launcher resolving package names) and supplied them via
|
|
1080
|
+
// `.extensions(...)` — see `suppress_config_extensions`.
|
|
1081
|
+
if !suppress_config_extensions {
|
|
1082
|
+
for ext in cfg.extensions {
|
|
1083
|
+
let path = if ext.path.is_absolute() {
|
|
1084
|
+
ext.path
|
|
1085
|
+
} else {
|
|
1086
|
+
cfg_parent.join(&ext.path)
|
|
1087
|
+
};
|
|
1088
|
+
extensions.push(Extension {
|
|
1089
|
+
path,
|
|
1090
|
+
entrypoint: ext.entrypoint,
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1072
1093
|
}
|
|
1073
1094
|
|
|
1074
1095
|
if cfg.persist {
|
|
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
|
|
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
|
|
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
|
{dirsql-0.3.46 → dirsql-0.3.48}/packages/python/docs/tests/integration/language-flag.spec.ts
RENAMED
|
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
|
|
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
|