Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ public-API binding, so no drift.
- `RowEvent` is a frozen class with attribute access (`event.action`, `event.row`).
- `AsyncDirSQL` is a pure-Python wrapper using `asyncio.to_thread`.
- Watch low-level methods are prefixed with `_` (private convention).
- Ships PEP 561 type information (`py.typed` + `dirsql/_dirsql.pyi`) so downstream consumers see types for `DirSQL`, `Table`, `RowEvent`. Parity-restoring: Rust types come from Rust source, TypeScript types from generated `.d.ts`, Python from the bundled stub. The stub MUST be updated in lockstep with `packages/python/src/lib.rs`.
- Ships PEP 561 type information (`py.typed` + `dirsql/_dirsql.pyi`) so downstream consumers see types for `DirSQL`, `Table`, `RowEvent`, `ScanFailure`. Parity-restoring: Rust types come from Rust source, TypeScript types from generated `.d.ts`, Python from the bundled stub. The stub MUST be updated in lockstep with `packages/python/src/lib.rs`.

### Rust
- Uses `snake_case` for all identifiers.
Expand All @@ -429,7 +429,7 @@ public-API binding, so no drift.
- `AsyncDirSQL` uses tokio and `OnceCell` internally.
- Watch returns `futures_channel::mpsc::UnboundedReceiver<RowEvent>` implementing `Stream`.
- All fallible operations return `Result<T, DirSqlError>`. Statements classified as writes by SQLite's `sqlite3_stmt_readonly` surface as the unit variant `DirSqlError::WriteForbidden`; in the Python/TS bindings the same condition is a `RuntimeError` / `Error` with a "read-only" message.
- **Parity restored (dirsql#715).** All three SDKs expose the files a scan could not index: Rust `DirSQL::scan_failures() -> &[OnFileFailure]`, Python `await db.scan_failures()` (a list of `ScanFailure` with `.path` / `.message`), TypeScript `await db.scanFailures()` (`ScanFailure[]` with `path` / `message`). A hook that fails, or a row the table rejects, skips that file rather than the scan; the list is how a caller learns the index is incomplete and which files are missing. Empty after a clean scan. The CLI additionally reports skips on stderr (capped at ten, then `... and N more`) and exits `23` — CLI-only by nature, since a library has no exit code. `--allow-skipped` remains unbuilt and is tracked in #715.
- **Parity restored (dirsql#715).** All three SDKs expose the files a scan could not index: Rust `DirSQL::scan_failures() -> &[OnFileFailure]`, Python `await db.scan_failures()` (a list of `ScanFailure` with `.path` / `.message`, importable as `from dirsql import ScanFailure`), TypeScript `await db.scanFailures()` (`ScanFailure[]` with `path` / `message`, exported from the package barrel). A hook that fails, or a row the table rejects, skips that file rather than the scan; the list is how a caller learns the index is incomplete and which files are missing. Empty after a clean scan. The CLI additionally reports skips on stderr (capped at ten, then `... and N more`) and exits `23` — CLI-only by nature, since a library has no exit code. `--allow-skipped` remains unbuilt and is tracked in #715.

### TypeScript
- Uses `camelCase` for method names.
Expand Down
7 changes: 7 additions & 0 deletions packages/python/changelog.d/2026-09-18-export-scan-failure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
**Added**

`ScanFailure` is now importable from the package root (`from dirsql import
ScanFailure`), so the objects `await db.scan_failures()` returns can be
annotated without reaching into `dirsql._dirsql`. The bundled type stub now
also declares `ScanFailure`, `DirSQL.scan_failures`, `run_cli`, and
`Table.on_file`, which it had been missing. No runtime behavior changes.
4 changes: 2 additions & 2 deletions packages/python/dirsql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Also available for Rust (crates.io: ``dirsql``) and TypeScript (npm: ``dirsql``).
"""

from dirsql._dirsql import Table, RowEvent, __version__
from dirsql._dirsql import Table, RowEvent, ScanFailure, __version__
from dirsql._async import DirSQL

__all__ = ["DirSQL", "Table", "RowEvent", "__version__"]
__all__ = ["DirSQL", "Table", "RowEvent", "ScanFailure", "__version__"]
11 changes: 9 additions & 2 deletions packages/python/dirsql/__init___test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@
relative so the barrel under test is the unit, not an external collaborator.
"""

from . import DirSQL, RowEvent, Table, __all__, __version__
from . import DirSQL, RowEvent, ScanFailure, Table, __all__, __version__


def describe_public_barrel():
def it_exposes_exactly_the_declared_public_names():
assert set(__all__) == {"DirSQL", "Table", "RowEvent", "__version__"}
assert set(__all__) == {
"DirSQL",
"Table",
"RowEvent",
"ScanFailure",
"__version__",
}

def it_re_exports_the_runtime_values():
assert DirSQL is not None
assert Table is not None
assert RowEvent is not None
assert ScanFailure is not None
assert isinstance(__version__, str)
12 changes: 12 additions & 0 deletions packages/python/dirsql/_dirsql.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Table:
name: str
ddl: str
glob: str
on_file: Callable[[str], list[Row]]
strict: bool

def __init__(
Expand All @@ -58,6 +59,15 @@ class RowEvent:
@override
def __repr__(self) -> str: ...

class ScanFailure:
"""One file the initial scan could not index, with the hook's own error."""

path: str
message: str

@override
def __repr__(self) -> str: ...

class DirSQL:
"""Synchronous binding class. ``dirsql._async.DirSQL`` wraps it."""

Expand All @@ -75,6 +85,7 @@ class DirSQL:
suppress_config_extensions: bool = False,
) -> None: ...
def query(self, sql: str) -> list[Row]: ...
def scan_failures(self) -> list[ScanFailure]: ...
def _start_watcher(self) -> None: ...
def _poll_events(self, timeout_ms: int) -> list[RowEvent]: ...

Expand All @@ -92,6 +103,7 @@ class ExtensionPlanEntry:
@override
def __repr__(self) -> str: ...

def run_cli(argv: list[str]) -> int: ...
def config_paths_from_argv(argv: list[str]) -> list[str]: ...
def plan_config_extensions(
configs: list[tuple[str, str | None]],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"command": "just test-e2e",
"ran_at": 1789750809,
"exit_code": 0,
"commit": "333d87fd6e4338e2cddddd9db1954eb63318e04b",
"branch": "claude/1140-scan-failure-export"
}
Loading