dirsql 0.4.44__tar.gz → 0.4.46__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.4.44 → dirsql-0.4.46}/Cargo.lock +2 -2
- {dirsql-0.4.44 → dirsql-0.4.46}/PKG-INFO +1 -2
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/_dirsql.pyi +19 -0
- dirsql-0.4.46/dirsql/plan_entry_path.py +20 -0
- dirsql-0.4.46/dirsql/read_config.py +15 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/resolve_configs_extension_specs.py +17 -15
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/resolve_extension.py +2 -2
- dirsql-0.4.46/dirsql/resolve_package.py +37 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/Cargo.toml +1 -1
- dirsql-0.4.46/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +3 -0
- dirsql-0.4.46/packages/python/e2e-attestations/claude-1120-py-ext.json +7 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/src/lib.rs +211 -2
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/Cargo.toml +1 -1
- {dirsql-0.4.44 → dirsql-0.4.46}/pyproject.toml +0 -4
- dirsql-0.4.44/dirsql/has_bare_name.py +0 -14
- dirsql-0.4.44/dirsql/is_bare_name.py +0 -14
- dirsql-0.4.44/dirsql/load_extension_entries.py +0 -28
- dirsql-0.4.44/dirsql/platform_patterns.py +0 -12
- dirsql-0.4.44/dirsql/resolve_config_extensions.py +0 -39
- dirsql-0.4.44/dirsql/resolve_entries.py +0 -20
- dirsql-0.4.44/dirsql/resolve_package.py +0 -53
- dirsql-0.4.44/dirsql/toml_module.py +0 -21
- {dirsql-0.4.44 → dirsql-0.4.46}/Cargo.toml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/README.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/_async.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/main.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/dirsql/py.typed +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/AGENTS.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/explanation.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/getting-started.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/embed.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/persist.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/query-json.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/index.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/package.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/plugins.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/cli.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/docs/reference/sdk.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/MIGRATIONS.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/README.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/conftest.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/query-json.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/plugins.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/cli.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/testing-conventions.toml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/MIGRATIONS.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/README.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/query-json.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/plugins.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/cli.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/repl.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/run.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/cli/table.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/db.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/extension_resolution.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/functions.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/launcher.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/lib.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/parsed_cache.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/parsed_vtab.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/progress.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/sql_literal.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/vtab.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/vtab_scaffold.rs +0 -0
- {dirsql-0.4.44 → dirsql-0.4.46}/packages/rust/src/watcher.rs +0 -0
|
@@ -552,7 +552,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
|
|
552
552
|
|
|
553
553
|
[[package]]
|
|
554
554
|
name = "dirsql"
|
|
555
|
-
version = "0.4.
|
|
555
|
+
version = "0.4.46"
|
|
556
556
|
dependencies = [
|
|
557
557
|
"assert_cmd",
|
|
558
558
|
"axum",
|
|
@@ -597,7 +597,7 @@ dependencies = [
|
|
|
597
597
|
|
|
598
598
|
[[package]]
|
|
599
599
|
name = "dirsql-py-ext"
|
|
600
|
-
version = "0.4.
|
|
600
|
+
version = "0.4.46"
|
|
601
601
|
dependencies = [
|
|
602
602
|
"dirsql",
|
|
603
603
|
"pyo3",
|
|
@@ -78,4 +78,23 @@ class DirSQL:
|
|
|
78
78
|
def _start_watcher(self) -> None: ...
|
|
79
79
|
def _poll_events(self, timeout_ms: int) -> list[RowEvent]: ...
|
|
80
80
|
|
|
81
|
+
class ExtensionPlanEntry:
|
|
82
|
+
"""One planned ``[[dirsql.extension]]`` entry. Exactly one of ``path`` and
|
|
83
|
+
``package`` is set: a ``path`` is ready to load, a ``package`` must be
|
|
84
|
+
located with ``importlib`` -- unless ``shadow`` names an existing file,
|
|
85
|
+
which takes precedence over the package."""
|
|
86
|
+
|
|
87
|
+
path: str | None
|
|
88
|
+
package: str | None
|
|
89
|
+
shadow: str | None
|
|
90
|
+
entrypoint: str | None
|
|
91
|
+
|
|
92
|
+
@override
|
|
93
|
+
def __repr__(self) -> str: ...
|
|
94
|
+
|
|
81
95
|
def config_paths_from_argv(argv: list[str]) -> list[str]: ...
|
|
96
|
+
def plan_config_extensions(
|
|
97
|
+
configs: list[tuple[str, str | None]],
|
|
98
|
+
) -> list[ExtensionPlanEntry] | None: ...
|
|
99
|
+
def select_loadable(name: str, dirs: list[str], candidates: list[str]) -> str: ...
|
|
100
|
+
def is_bare_name(path: str) -> bool: ...
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""The loadable path one planned extension entry resolves to."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from .resolve_package import _resolve_package
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _plan_entry_path(entry):
|
|
11
|
+
"""Resolve a planned entry to a concrete file.
|
|
12
|
+
|
|
13
|
+
A literal entry is already resolved. A package entry prefers a same-named
|
|
14
|
+
file shadowing it next to the config, then the installed package.
|
|
15
|
+
"""
|
|
16
|
+
if entry.path is not None:
|
|
17
|
+
return entry.path
|
|
18
|
+
if os.path.isfile(entry.shadow):
|
|
19
|
+
return entry.shadow
|
|
20
|
+
return _resolve_package(entry.package)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Reading one TOML config off disk for the core's extension planner."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _read_config(config_path):
|
|
7
|
+
"""Return the config's text, or ``None`` when it cannot be read.
|
|
8
|
+
|
|
9
|
+
A missing or unreadable config is left for the core to report.
|
|
10
|
+
"""
|
|
11
|
+
try:
|
|
12
|
+
with open(config_path, encoding="utf-8") as f:
|
|
13
|
+
return f.read()
|
|
14
|
+
except OSError:
|
|
15
|
+
return None
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"""Resolution of several TOML configs' ``[[dirsql.extension]]`` entries, in order.
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The planning -- which configs parse, whether any entry names a package rather
|
|
4
|
+
than a file, and what each literal path resolves to against its own config's
|
|
5
|
+
directory -- lives in the Rust core (``_dirsql.plan_config_extensions``). This
|
|
6
|
+
module supplies the two host-specific halves the core cannot have: reading the
|
|
7
|
+
files, and locating an installed package with ``importlib``.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
from __future__ import annotations
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from .
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
from ._dirsql import plan_config_extensions
|
|
15
|
+
from .plan_entry_path import _plan_entry_path
|
|
16
|
+
from .read_config import _read_config
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
def resolve_configs_extension_specs(config_paths):
|
|
@@ -24,13 +28,11 @@ def resolve_configs_extension_specs(config_paths):
|
|
|
24
28
|
the resolved list. Returns ``None`` when no config uses a package name,
|
|
25
29
|
leaving every config's loading to the core.
|
|
26
30
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
sources = [(os.path.abspath(p), _read_config(p)) for p in config_paths]
|
|
32
|
+
plan = plan_config_extensions(sources)
|
|
33
|
+
if plan is None:
|
|
29
34
|
return None
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
entries, base = item
|
|
35
|
-
specs.extend(_resolve_entries(entries, base))
|
|
36
|
-
return specs
|
|
35
|
+
return [
|
|
36
|
+
{"path": _plan_entry_path(entry), "entrypoint": entry.entrypoint}
|
|
37
|
+
for entry in plan
|
|
38
|
+
]
|
|
@@ -10,12 +10,12 @@ package name reaches the package machinery:
|
|
|
10
10
|
2. **Bare name** -- a same-named local file under ``base`` *shadows* the
|
|
11
11
|
package; otherwise the package dir is located via
|
|
12
12
|
:func:`importlib.util.find_spec` and the current platform's loadable is
|
|
13
|
-
|
|
13
|
+
picked from inside it.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
import os
|
|
17
17
|
|
|
18
|
-
from .
|
|
18
|
+
from ._dirsql import is_bare_name
|
|
19
19
|
from .resolve_package import _resolve_package
|
|
20
20
|
|
|
21
21
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Location of an extension package's loadable file for this platform.
|
|
2
|
+
|
|
3
|
+
``importlib`` finds the package's directories; the core picks the one loadable
|
|
4
|
+
file inside them (and raises on zero or several -- the caller must then
|
|
5
|
+
disambiguate with a literal path).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import glob as _glob
|
|
9
|
+
import importlib.util
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from ._dirsql import select_loadable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _resolve_package(name):
|
|
16
|
+
"""Locate ``name``'s package dir and pick its platform loadable file."""
|
|
17
|
+
try:
|
|
18
|
+
spec = importlib.util.find_spec(name)
|
|
19
|
+
except (ImportError, ValueError) as exc:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"could not resolve extension package {name!r}: {exc}"
|
|
22
|
+
) from exc
|
|
23
|
+
if spec is None:
|
|
24
|
+
raise ValueError(f"could not resolve extension package {name!r}: not installed")
|
|
25
|
+
|
|
26
|
+
dirs = list(spec.submodule_search_locations or [])
|
|
27
|
+
if not dirs and spec.origin and spec.origin not in ("built-in", "frozen"):
|
|
28
|
+
dirs.append(os.path.dirname(spec.origin))
|
|
29
|
+
if not dirs:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"could not resolve extension package {name!r}: no package directory"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
candidates = []
|
|
35
|
+
for d in dirs:
|
|
36
|
+
candidates.extend(_glob.glob(os.path.join(d, "**", "*"), recursive=True))
|
|
37
|
+
return select_loadable(name, dirs, candidates)
|
|
@@ -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.4.
|
|
7
|
+
version = "0.4.46"
|
|
8
8
|
# abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
|
|
9
9
|
# `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
|
|
10
10
|
# (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
**Changed**
|
|
2
|
+
|
|
3
|
+
- **Extension resolution now plans in the Rust core.** `resolve_configs_extension_specs` reads each config off disk and hands `(path, contents)` pairs to `dirsql._dirsql.plan_config_extensions`; `_resolve_package` hands the located package directories and their files to `dirsql._dirsql.select_loadable`; `resolve_extension_path` classifies through `dirsql._dirsql.is_bare_name`. Python keeps only the two host-specific halves — reading the files, and `importlib.util.find_spec`. Seven internal modules go away (`is_bare_name`, `has_bare_name`, `load_extension_entries`, `resolve_entries`, `platform_patterns`, `resolve_config_extensions`, `toml_module`) against two added (`read_config`, `plan_entry_path`), and with them the `tomli` dependency on Python 3.10. No change to the published API or CLI behavior. (#1120)
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
#[cfg(feature = "extension-module")]
|
|
14
14
|
mod python {
|
|
15
|
+
use ::dirsql::extension_resolution::{self, ConfigSource, PlanEntry};
|
|
15
16
|
use ::dirsql::{DirSQL, Extension, Row, RowEvent, Table, Value};
|
|
16
|
-
use pyo3::exceptions::{PyOverflowError, PyRuntimeError};
|
|
17
|
+
use pyo3::exceptions::{PyOverflowError, PyRuntimeError, PyValueError};
|
|
17
18
|
use pyo3::prelude::*;
|
|
18
19
|
use pyo3::types::{PyBool, PyByteArray, PyBytes, PyDict, PyInt, PyList};
|
|
19
20
|
use std::collections::HashMap;
|
|
20
|
-
use std::path::PathBuf;
|
|
21
|
+
use std::path::{Path, PathBuf};
|
|
21
22
|
use std::time::Duration;
|
|
22
23
|
|
|
23
24
|
/// A table definition. Mirrors `dirsql::Table` but holds a Python
|
|
@@ -469,6 +470,115 @@ mod python {
|
|
|
469
470
|
dirsql::launcher::config_paths_from_argv(&argv)
|
|
470
471
|
}
|
|
471
472
|
|
|
473
|
+
/// The loadable-file suffixes a CPython host recognizes.
|
|
474
|
+
const PY_SUFFIXES: &[&str] = &[".so", ".dylib", ".dll", ".pyd"];
|
|
475
|
+
|
|
476
|
+
/// The subset this platform actually builds, used when picking a
|
|
477
|
+
/// package's loadable out of everything installed beside it.
|
|
478
|
+
#[cfg(target_os = "macos")]
|
|
479
|
+
const PLATFORM_SUFFIXES: &[&str] = &[".dylib"];
|
|
480
|
+
#[cfg(target_os = "windows")]
|
|
481
|
+
const PLATFORM_SUFFIXES: &[&str] = &[".dll", ".pyd"];
|
|
482
|
+
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
483
|
+
const PLATFORM_SUFFIXES: &[&str] = &[".so"];
|
|
484
|
+
|
|
485
|
+
/// One planned `[[dirsql.extension]]` entry.
|
|
486
|
+
///
|
|
487
|
+
/// Exactly one of `path` and `package` is set: a `path` is ready to load,
|
|
488
|
+
/// a `package` must be located with `importlib` first — unless `shadow`
|
|
489
|
+
/// names an existing file, which takes precedence over the package.
|
|
490
|
+
#[pyclass(name = "ExtensionPlanEntry", frozen)]
|
|
491
|
+
struct PyExtensionPlanEntry {
|
|
492
|
+
#[pyo3(get)]
|
|
493
|
+
path: Option<String>,
|
|
494
|
+
#[pyo3(get)]
|
|
495
|
+
package: Option<String>,
|
|
496
|
+
#[pyo3(get)]
|
|
497
|
+
shadow: Option<String>,
|
|
498
|
+
#[pyo3(get)]
|
|
499
|
+
entrypoint: Option<String>,
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
#[pymethods]
|
|
503
|
+
impl PyExtensionPlanEntry {
|
|
504
|
+
fn __repr__(&self) -> String {
|
|
505
|
+
format!(
|
|
506
|
+
"ExtensionPlanEntry(path={:?}, package={:?}, shadow={:?}, entrypoint={:?})",
|
|
507
|
+
self.path, self.package, self.shadow, self.entrypoint
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
fn plan_entry_to_py(entry: PlanEntry) -> PyExtensionPlanEntry {
|
|
513
|
+
match entry {
|
|
514
|
+
PlanEntry::Literal { path, entrypoint } => PyExtensionPlanEntry {
|
|
515
|
+
path: Some(display(&path)),
|
|
516
|
+
package: None,
|
|
517
|
+
shadow: None,
|
|
518
|
+
entrypoint,
|
|
519
|
+
},
|
|
520
|
+
PlanEntry::Package {
|
|
521
|
+
name,
|
|
522
|
+
shadow,
|
|
523
|
+
entrypoint,
|
|
524
|
+
} => PyExtensionPlanEntry {
|
|
525
|
+
path: None,
|
|
526
|
+
package: Some(name),
|
|
527
|
+
shadow: Some(display(&shadow)),
|
|
528
|
+
entrypoint,
|
|
529
|
+
},
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
fn display(path: &Path) -> String {
|
|
534
|
+
path.to_string_lossy().into_owned()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// Plan several configs' `[[dirsql.extension]]` entries.
|
|
538
|
+
///
|
|
539
|
+
/// `configs` pairs each config's absolute path with its contents, or
|
|
540
|
+
/// `None` when the launcher could not read it. `None` back means no entry
|
|
541
|
+
/// names a package, so the core's own config loading handles them all.
|
|
542
|
+
#[pyfunction]
|
|
543
|
+
fn plan_config_extensions(
|
|
544
|
+
configs: Vec<(String, Option<String>)>,
|
|
545
|
+
) -> Option<Vec<PyExtensionPlanEntry>> {
|
|
546
|
+
let sources: Vec<ConfigSource<'_>> = configs
|
|
547
|
+
.iter()
|
|
548
|
+
.map(|(path, contents)| ConfigSource {
|
|
549
|
+
path: Path::new(path),
|
|
550
|
+
contents: contents.as_deref(),
|
|
551
|
+
})
|
|
552
|
+
.collect();
|
|
553
|
+
let plan = extension_resolution::plan_config_extensions(&sources, PY_SUFFIXES)?;
|
|
554
|
+
Some(plan.into_iter().map(plan_entry_to_py).collect())
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/// Pick package `name`'s single loadable file out of `candidates`.
|
|
558
|
+
#[pyfunction]
|
|
559
|
+
fn select_loadable(name: &str, dirs: Vec<String>, candidates: Vec<String>) -> PyResult<String> {
|
|
560
|
+
selected_loadable(name, dirs, candidates).map_err(PyValueError::new_err)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/// The interpreter-free half of [`select_loadable`], so it stays unit-testable.
|
|
564
|
+
fn selected_loadable(
|
|
565
|
+
name: &str,
|
|
566
|
+
dirs: Vec<String>,
|
|
567
|
+
candidates: Vec<String>,
|
|
568
|
+
) -> Result<String, String> {
|
|
569
|
+
let dirs: Vec<PathBuf> = dirs.into_iter().map(PathBuf::from).collect();
|
|
570
|
+
let candidates: Vec<PathBuf> = candidates.into_iter().map(PathBuf::from).collect();
|
|
571
|
+
extension_resolution::select_loadable(name, &dirs, &candidates, PLATFORM_SUFFIXES)
|
|
572
|
+
.map(|path| display(&path))
|
|
573
|
+
.map_err(|err| err.to_string())
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/// Whether an extension entry names a package rather than a file.
|
|
577
|
+
#[pyfunction]
|
|
578
|
+
fn is_bare_name(path: &str) -> bool {
|
|
579
|
+
extension_resolution::is_bare_name(path, PY_SUFFIXES)
|
|
580
|
+
}
|
|
581
|
+
|
|
472
582
|
#[pymodule]
|
|
473
583
|
#[pyo3(name = "_dirsql")]
|
|
474
584
|
fn py_dirsql_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
@@ -479,6 +589,10 @@ mod python {
|
|
|
479
589
|
m.add_class::<PyScanFailure>()?;
|
|
480
590
|
m.add_function(wrap_pyfunction!(run_cli, m)?)?;
|
|
481
591
|
m.add_function(wrap_pyfunction!(config_paths_from_argv, m)?)?;
|
|
592
|
+
m.add_function(wrap_pyfunction!(plan_config_extensions, m)?)?;
|
|
593
|
+
m.add_function(wrap_pyfunction!(select_loadable, m)?)?;
|
|
594
|
+
m.add_function(wrap_pyfunction!(is_bare_name, m)?)?;
|
|
595
|
+
m.add_class::<PyExtensionPlanEntry>()?;
|
|
482
596
|
Ok(())
|
|
483
597
|
}
|
|
484
598
|
|
|
@@ -561,6 +675,101 @@ mod python {
|
|
|
561
675
|
assert_eq!(OnFileError("bad".to_string()).to_string(), "bad");
|
|
562
676
|
}
|
|
563
677
|
|
|
678
|
+
#[test]
|
|
679
|
+
fn planning_returns_none_when_no_entry_names_a_package() {
|
|
680
|
+
let plan = plan_config_extensions(vec![(
|
|
681
|
+
"/cfg/.dirsql.toml".into(),
|
|
682
|
+
Some("[[dirsql.extension]]\npath = \"vec0.so\"\n".into()),
|
|
683
|
+
)]);
|
|
684
|
+
assert!(plan.is_none());
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
#[test]
|
|
688
|
+
fn planning_marshals_a_literal_against_its_own_config_directory() {
|
|
689
|
+
let plan = plan_config_extensions(vec![(
|
|
690
|
+
"/cfg/.dirsql.toml".into(),
|
|
691
|
+
Some(
|
|
692
|
+
"[[dirsql.extension]]\npath = \"ext/vec0.so\"\nentrypoint = \"init\"\n\n[[dirsql.extension]]\npath = \"sqlite_vec\"\n"
|
|
693
|
+
.into(),
|
|
694
|
+
),
|
|
695
|
+
)])
|
|
696
|
+
.expect("a bare name makes the SDK intervene");
|
|
697
|
+
|
|
698
|
+
assert_eq!(plan[0].path.as_deref(), Some("/cfg/ext/vec0.so"));
|
|
699
|
+
assert_eq!(plan[0].package, None);
|
|
700
|
+
assert_eq!(plan[0].shadow, None);
|
|
701
|
+
assert_eq!(plan[0].entrypoint.as_deref(), Some("init"));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
#[test]
|
|
705
|
+
fn planning_marshals_a_package_with_its_shadow_probe() {
|
|
706
|
+
let plan = plan_config_extensions(vec![(
|
|
707
|
+
"/cfg/.dirsql.toml".into(),
|
|
708
|
+
Some("[[dirsql.extension]]\npath = \"sqlite_vec\"\n".into()),
|
|
709
|
+
)])
|
|
710
|
+
.expect("a bare name makes the SDK intervene");
|
|
711
|
+
|
|
712
|
+
assert_eq!(plan[0].path, None);
|
|
713
|
+
assert_eq!(plan[0].package.as_deref(), Some("sqlite_vec"));
|
|
714
|
+
assert_eq!(plan[0].shadow.as_deref(), Some("/cfg/sqlite_vec"));
|
|
715
|
+
assert_eq!(plan[0].entrypoint, None);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
#[test]
|
|
719
|
+
fn an_unreadable_config_is_skipped_rather_than_reported() {
|
|
720
|
+
assert!(plan_config_extensions(vec![("/cfg/.dirsql.toml".into(), None)]).is_none());
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
#[test]
|
|
724
|
+
fn plan_entry_repr_names_every_field() {
|
|
725
|
+
let entry = plan_entry_to_py(PlanEntry::Package {
|
|
726
|
+
name: "vec".into(),
|
|
727
|
+
shadow: "/cfg/vec".into(),
|
|
728
|
+
entrypoint: Some("init".into()),
|
|
729
|
+
});
|
|
730
|
+
assert_eq!(
|
|
731
|
+
entry.__repr__(),
|
|
732
|
+
"ExtensionPlanEntry(path=None, package=Some(\"vec\"), shadow=Some(\"/cfg/vec\"), entrypoint=Some(\"init\"))"
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
#[cfg(target_os = "macos")]
|
|
737
|
+
const EXPECTED_PATTERNS: &str = "*.dylib";
|
|
738
|
+
#[cfg(target_os = "windows")]
|
|
739
|
+
const EXPECTED_PATTERNS: &str = "*.dll / *.pyd";
|
|
740
|
+
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
741
|
+
const EXPECTED_PATTERNS: &str = "*.so";
|
|
742
|
+
|
|
743
|
+
#[test]
|
|
744
|
+
fn select_loadable_returns_the_single_match() {
|
|
745
|
+
let found = selected_loadable(
|
|
746
|
+
"vec",
|
|
747
|
+
vec!["/pkg".into()],
|
|
748
|
+
vec!["/pkg/__init__.py".into(), "/pkg/vec0.so".into()],
|
|
749
|
+
);
|
|
750
|
+
assert_eq!(found, Ok("/pkg/vec0.so".to_string()));
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
#[test]
|
|
754
|
+
fn select_loadable_carries_the_cores_message_verbatim() {
|
|
755
|
+
let err = selected_loadable("vec", vec!["/pkg".into()], vec![])
|
|
756
|
+
.expect_err("no loadable file");
|
|
757
|
+
assert_eq!(
|
|
758
|
+
err,
|
|
759
|
+
format!(
|
|
760
|
+
"no loadable extension file ({EXPECTED_PATTERNS}) found in package 'vec' (searched /pkg)"
|
|
761
|
+
)
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
#[test]
|
|
766
|
+
fn is_bare_name_applies_the_cpython_suffix_list() {
|
|
767
|
+
assert!(is_bare_name("sqlite_vec"));
|
|
768
|
+
assert!(!is_bare_name("vec0.pyd"));
|
|
769
|
+
assert!(is_bare_name("vec0.node"));
|
|
770
|
+
assert!(!is_bare_name("ext/vec0"));
|
|
771
|
+
}
|
|
772
|
+
|
|
564
773
|
#[test]
|
|
565
774
|
fn config_paths_from_argv_forwards_to_the_core_scan() {
|
|
566
775
|
assert_eq!(
|
|
@@ -6,7 +6,7 @@ description = "Ephemeral SQL index over a local directory"
|
|
|
6
6
|
# version stays for the binding crates (publish = false). The literal
|
|
7
7
|
# intentionally lags the published version: putitoutthere rewrites it to
|
|
8
8
|
# the planned release version at build time and never commits it back.
|
|
9
|
-
version = "0.4.
|
|
9
|
+
version = "0.4.46"
|
|
10
10
|
edition.workspace = true
|
|
11
11
|
# Literal `license` (not `license.workspace = true`) because
|
|
12
12
|
# putitoutthere's preflight check reads `[package].license` directly
|
|
@@ -19,10 +19,6 @@ repository = "https://github.com/thekevinscott/dirsql"
|
|
|
19
19
|
keywords = ["sql", "filesystem", "directory", "sqlite", "index"]
|
|
20
20
|
readme = "README.md"
|
|
21
21
|
dependencies = [
|
|
22
|
-
# `tomllib` is stdlib only on 3.11+; `resolve_config_extensions` falls back
|
|
23
|
-
# to the backport it was derived from. Drop this the day the floor reaches
|
|
24
|
-
# 3.11 again.
|
|
25
|
-
'tomli>=2; python_version < "3.11"',
|
|
26
22
|
# The console script runs the CLI in-process through this (#738); without
|
|
27
23
|
# it `dirsql.cli.main` cannot import.
|
|
28
24
|
"bin-shim>=0.1",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"""Whether any of a config's extension entries names a package."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from .is_bare_name import is_bare_name
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _has_bare_name(entries):
|
|
9
|
-
return any(
|
|
10
|
-
isinstance(e, dict)
|
|
11
|
-
and isinstance(e.get("path"), str)
|
|
12
|
-
and is_bare_name(e["path"])
|
|
13
|
-
for e in entries
|
|
14
|
-
)
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"""Whether an extension ``path`` names a package rather than a file."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
# Suffixes that mark a value as "already a file path", so package resolution
|
|
6
|
-
# is never attempted.
|
|
7
|
-
_LOADABLE_SUFFIXES = (".so", ".dylib", ".dll", ".pyd")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def is_bare_name(path):
|
|
11
|
-
"""True when ``path`` is a bare package name rather than a file path."""
|
|
12
|
-
if os.sep in path or (os.altsep and os.altsep in path):
|
|
13
|
-
return False
|
|
14
|
-
return not path.endswith(_LOADABLE_SUFFIXES)
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Reading one TOML config's ``[[dirsql.extension]]`` array off disk."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
|
|
7
|
-
from .toml_module import _toml
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _load_extension_entries(config_path):
|
|
11
|
-
"""Return ``(entries, base_dir)`` for a config's ``[[dirsql.extension]]``.
|
|
12
|
-
|
|
13
|
-
``None`` when the config is missing, unreadable/malformed, or declares no
|
|
14
|
-
extension array -- the caller should leave such configs to the core.
|
|
15
|
-
"""
|
|
16
|
-
if not os.path.isfile(config_path):
|
|
17
|
-
return None
|
|
18
|
-
try:
|
|
19
|
-
with open(config_path, "rb") as f:
|
|
20
|
-
doc = _toml.load(f)
|
|
21
|
-
except (OSError, _toml.TOMLDecodeError):
|
|
22
|
-
# Leave a malformed / unreadable config for the core to report.
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
entries = (doc.get("dirsql") or {}).get("extension")
|
|
26
|
-
if not isinstance(entries, list):
|
|
27
|
-
return None
|
|
28
|
-
return entries, os.path.dirname(os.path.abspath(config_path))
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""The loadable-file glob pattern(s) the running platform uses."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def _platform_patterns():
|
|
7
|
-
"""Loadable-file glob(s) for the current platform."""
|
|
8
|
-
if sys.platform == "darwin":
|
|
9
|
-
return ("*.dylib",)
|
|
10
|
-
if sys.platform == "win32":
|
|
11
|
-
return ("*.dll", "*.pyd")
|
|
12
|
-
return ("*.so",)
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"""SDK-side resolution of a TOML config's ``[[dirsql.extension]]`` entries.
|
|
2
|
-
|
|
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.
|
|
9
|
-
|
|
10
|
-
Shared by the ``DirSQL`` constructor (``config=`` path) and the CLI launcher
|
|
11
|
-
(which converts the resolved specs into ``--extension`` flags), both of which
|
|
12
|
-
reach it through :mod:`dirsql.resolve_configs_extension_specs`.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
from .has_bare_name import _has_bare_name
|
|
18
|
-
from .load_extension_entries import _load_extension_entries
|
|
19
|
-
from .resolve_entries import _resolve_entries
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def resolve_config_extension_specs(config_path):
|
|
23
|
-
"""Resolve a TOML config's ``[[dirsql.extension]]`` entries to literal paths.
|
|
24
|
-
|
|
25
|
-
Returns a list of ``{"path", "entrypoint"}`` dicts -- every entry resolved
|
|
26
|
-
via :func:`resolve_extension_path` against the config file's parent
|
|
27
|
-
directory -- when at least one entry's ``path`` is a bare package name.
|
|
28
|
-
Returns ``None`` when the caller should not intervene: the config is
|
|
29
|
-
missing, malformed, declares no extensions, or uses only literal paths --
|
|
30
|
-
leaving the core's own loading (and error reporting) untouched. Raises if
|
|
31
|
-
a package name cannot be resolved.
|
|
32
|
-
"""
|
|
33
|
-
loaded = _load_extension_entries(config_path)
|
|
34
|
-
if loaded is None:
|
|
35
|
-
return None
|
|
36
|
-
entries, base = loaded
|
|
37
|
-
if not _has_bare_name(entries):
|
|
38
|
-
return None
|
|
39
|
-
return _resolve_entries(entries, base)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""Resolution of one config's ``[[dirsql.extension]]`` entries to literal paths."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from .resolve_extension import resolve_extension_path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _resolve_entries(entries, base):
|
|
9
|
-
specs = []
|
|
10
|
-
for e in entries:
|
|
11
|
-
entrypoint = e.get("entrypoint")
|
|
12
|
-
specs.append(
|
|
13
|
-
{
|
|
14
|
-
"path": resolve_extension_path(
|
|
15
|
-
e["path"], base=base, resolve_relative=True
|
|
16
|
-
),
|
|
17
|
-
"entrypoint": entrypoint if isinstance(entrypoint, str) else None,
|
|
18
|
-
}
|
|
19
|
-
)
|
|
20
|
-
return specs
|