dirsql 0.3.67__tar.gz → 0.3.69__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 (135) hide show
  1. {dirsql-0.3.67 → dirsql-0.3.69}/Cargo.lock +1 -1
  2. {dirsql-0.3.67 → dirsql-0.3.69}/PKG-INFO +1 -9
  3. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/Cargo.toml +1 -1
  4. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/e2e-attestation.json +2 -2
  5. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/src/lib.rs +134 -18
  6. {dirsql-0.3.67 → dirsql-0.3.69}/pyproject.toml +7 -1
  7. {dirsql-0.3.67 → dirsql-0.3.69}/Cargo.toml +0 -0
  8. {dirsql-0.3.67 → dirsql-0.3.69}/README.md +0 -0
  9. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/__init__.py +0 -0
  10. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/_async.py +0 -0
  11. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/_dirsql.pyi +0 -0
  12. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/cli/__init__.py +0 -0
  13. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/cli/binary_path.py +0 -0
  14. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/cli/is_windows.py +0 -0
  15. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/cli/main.py +0 -0
  16. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/cli/resolve_config_extensions.py +0 -0
  17. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/py.typed +0 -0
  18. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/resolve_config_extensions.py +0 -0
  19. {dirsql-0.3.67 → dirsql-0.3.69}/dirsql/resolve_extension.py +0 -0
  20. {dirsql-0.3.67 → dirsql-0.3.69}/docs/.claude/CLAUDE.md +0 -0
  21. {dirsql-0.3.67 → dirsql-0.3.69}/docs/.vitepress/config.ts +0 -0
  22. {dirsql-0.3.67 → dirsql-0.3.69}/docs/.vitepress/theme/index.ts +0 -0
  23. {dirsql-0.3.67 → dirsql-0.3.69}/docs/.vitepress/theme/lang.ts +0 -0
  24. {dirsql-0.3.67 → dirsql-0.3.69}/docs/AGENTS.md +0 -0
  25. {dirsql-0.3.67 → dirsql-0.3.69}/docs/explanation.md +0 -0
  26. {dirsql-0.3.67 → dirsql-0.3.69}/docs/getting-started.md +0 -0
  27. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/columns-from-paths.md +0 -0
  28. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/define-tables.md +0 -0
  29. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/embed.md +0 -0
  30. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/extract-from-contents.md +0 -0
  31. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/load-extension.md +0 -0
  32. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/persist.md +0 -0
  33. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/react-to-changes.md +0 -0
  34. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/search-by-meaning.md +0 -0
  35. {dirsql-0.3.67 → dirsql-0.3.69}/docs/howto/skip-files.md +0 -0
  36. {dirsql-0.3.67 → dirsql-0.3.69}/docs/index.md +0 -0
  37. {dirsql-0.3.67 → dirsql-0.3.69}/docs/migrations.md +0 -0
  38. {dirsql-0.3.67 → dirsql-0.3.69}/docs/package.json +0 -0
  39. {dirsql-0.3.67 → dirsql-0.3.69}/docs/playwright.config.ts +0 -0
  40. {dirsql-0.3.67 → dirsql-0.3.69}/docs/pnpm-lock.yaml +0 -0
  41. {dirsql-0.3.67 → dirsql-0.3.69}/docs/pnpm-workspace.yaml +0 -0
  42. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/cli.md +0 -0
  43. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/columns.md +0 -0
  44. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/config.md +0 -0
  45. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/hooks.md +0 -0
  46. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/http-api.md +0 -0
  47. {dirsql-0.3.67 → dirsql-0.3.69}/docs/reference/sdk.md +0 -0
  48. {dirsql-0.3.67 → dirsql-0.3.69}/docs/tests/integration/home.spec.ts +0 -0
  49. {dirsql-0.3.67 → dirsql-0.3.69}/docs/tests/integration/language-flag.spec.ts +0 -0
  50. {dirsql-0.3.67 → dirsql-0.3.69}/docs/tests/integration/sidebar.spec.ts +0 -0
  51. {dirsql-0.3.67 → dirsql-0.3.69}/docs/tests/unit/config.test.ts +0 -0
  52. {dirsql-0.3.67 → dirsql-0.3.69}/docs/tests/unit/lang.test.ts +0 -0
  53. {dirsql-0.3.67 → dirsql-0.3.69}/docs/vitest.config.ts +0 -0
  54. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/README.md +0 -0
  55. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/conftest.py +0 -0
  56. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/.claude/CLAUDE.md +0 -0
  57. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/.vitepress/config.ts +0 -0
  58. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  59. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  60. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/AGENTS.md +0 -0
  61. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/explanation.md +0 -0
  62. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/getting-started.md +0 -0
  63. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/columns-from-paths.md +0 -0
  64. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/define-tables.md +0 -0
  65. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/embed.md +0 -0
  66. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/extract-from-contents.md +0 -0
  67. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/load-extension.md +0 -0
  68. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/persist.md +0 -0
  69. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/react-to-changes.md +0 -0
  70. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/search-by-meaning.md +0 -0
  71. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/howto/skip-files.md +0 -0
  72. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/index.md +0 -0
  73. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/migrations.md +0 -0
  74. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/package.json +0 -0
  75. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/playwright.config.ts +0 -0
  76. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/pnpm-lock.yaml +0 -0
  77. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/pnpm-workspace.yaml +0 -0
  78. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/cli.md +0 -0
  79. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/columns.md +0 -0
  80. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/config.md +0 -0
  81. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/hooks.md +0 -0
  82. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/http-api.md +0 -0
  83. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/reference/sdk.md +0 -0
  84. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  85. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  86. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  87. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/tests/unit/config.test.ts +0 -0
  88. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  89. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/docs/vitest.config.ts +0 -0
  90. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/__init__.py +0 -0
  91. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/binding/__init__.py +0 -0
  92. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/conftest.py +0 -0
  93. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/e2e/__init__.py +0 -0
  94. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/integration/__init__.py +0 -0
  95. {dirsql-0.3.67 → dirsql-0.3.69}/packages/python/tests/smoke/__init__.py +0 -0
  96. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/Cargo.toml +0 -0
  97. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/README.md +0 -0
  98. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/benches/db_bench.rs +0 -0
  99. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/benches/differ_bench.rs +0 -0
  100. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/benches/matcher_bench.rs +0 -0
  101. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/benches/scanner_bench.rs +0 -0
  102. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/explanation.md +0 -0
  103. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/getting-started.md +0 -0
  104. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  105. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/define-tables.md +0 -0
  106. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/embed.md +0 -0
  107. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  108. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/load-extension.md +0 -0
  109. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/persist.md +0 -0
  110. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/react-to-changes.md +0 -0
  111. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  112. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/howto/skip-files.md +0 -0
  113. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/index.md +0 -0
  114. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/migrations.md +0 -0
  115. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/cli.md +0 -0
  116. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/columns.md +0 -0
  117. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/config.md +0 -0
  118. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/hooks.md +0 -0
  119. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/http-api.md +0 -0
  120. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/docs/reference/sdk.md +0 -0
  121. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/bin/dirsql.rs +0 -0
  122. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/cli/init.rs +0 -0
  123. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/cli/mod.rs +0 -0
  124. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/cli/router.rs +0 -0
  125. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/cli/serialize.rs +0 -0
  126. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/cli/server.rs +0 -0
  127. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/command.rs +0 -0
  128. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/config.rs +0 -0
  129. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/db.rs +0 -0
  130. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/differ.rs +0 -0
  131. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/lib.rs +0 -0
  132. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/matcher.rs +0 -0
  133. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/persist.rs +0 -0
  134. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/scanner.rs +0 -0
  135. {dirsql-0.3.67 → dirsql-0.3.69}/packages/rust/src/watcher.rs +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.67"
503
+ version = "0.3.69"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,14 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.67
4
- Requires-Dist: pytest>=8 ; extra == 'dev'
5
- Requires-Dist: pytest-describe>=2 ; extra == 'dev'
6
- Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
7
- Requires-Dist: pytest-cov>=5 ; extra == 'dev'
8
- Requires-Dist: ruff>=0.4 ; extra == 'dev'
9
- Requires-Dist: maturin>=1.0 ; extra == 'dev'
10
- Requires-Dist: ty==0.0.42 ; extra == 'dev'
11
- Provides-Extra: dev
3
+ Version: 0.3.69
12
4
  Summary: Ephemeral SQL index over a local directory
13
5
  Keywords: sql,filesystem,directory,sqlite,index
14
6
  Author: Kevin Scott
@@ -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.67"
7
+ version = "0.3.69"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783215074,
3
+ "ran_at": 1783257990,
4
4
  "exit_code": 0,
5
- "commit": "c5d752734907f27497b28b3448bad19247c3482e"
5
+ "commit": "cb8d663ecb72a808f4ab99589af16949e9449f3b"
6
6
  }
@@ -257,57 +257,93 @@ mod python {
257
257
  PyRuntimeError::new_err(e.to_string())
258
258
  }
259
259
 
260
- fn row_event_to_py(py: Python<'_>, event: &RowEvent) -> PyResult<PyRowEvent> {
261
- Ok(match event {
260
+ /// Pure, GIL-free intermediate for a row event. [`row_event_to_plain`]
261
+ /// builds it from a core [`RowEvent`] (unit-testable without a Python
262
+ /// interpreter); [`row_event_to_py`] then marshals it into the
263
+ /// Python-facing [`PyRowEvent`] (the GIL step). Splitting the two keeps the
264
+ /// variant -> action / field-selection mapping testable at the unit tier,
265
+ /// mirroring the napi binding's pure `row_event_to_js`. The value-level
266
+ /// `Row -> PyDict` conversion stays GIL-bound (binding-tier covered).
267
+ struct PlainRowEvent {
268
+ table: Option<String>,
269
+ action: &'static str,
270
+ row: Option<Row>,
271
+ old_row: Option<Row>,
272
+ error: Option<String>,
273
+ file_path: String,
274
+ }
275
+
276
+ fn row_event_to_plain(event: &RowEvent) -> PlainRowEvent {
277
+ match event {
262
278
  RowEvent::Insert {
263
279
  table,
264
280
  row,
265
281
  file_path,
266
- } => PyRowEvent {
282
+ } => PlainRowEvent {
267
283
  table: Some(table.clone()),
268
- action: "insert".to_string(),
269
- row: Some(value_row_to_py_dict(py, row)?),
284
+ action: "insert",
285
+ row: Some(row.clone()),
270
286
  old_row: None,
271
287
  error: None,
272
- file_path: Some(file_path.clone()),
288
+ file_path: file_path.clone(),
273
289
  },
274
290
  RowEvent::Update {
275
291
  table,
276
292
  old_row,
277
293
  new_row,
278
294
  file_path,
279
- } => PyRowEvent {
295
+ } => PlainRowEvent {
280
296
  table: Some(table.clone()),
281
- action: "update".to_string(),
282
- row: Some(value_row_to_py_dict(py, new_row)?),
283
- old_row: Some(value_row_to_py_dict(py, old_row)?),
297
+ action: "update",
298
+ row: Some(new_row.clone()),
299
+ old_row: Some(old_row.clone()),
284
300
  error: None,
285
- file_path: Some(file_path.clone()),
301
+ file_path: file_path.clone(),
286
302
  },
287
303
  RowEvent::Delete {
288
304
  table,
289
305
  row,
290
306
  file_path,
291
- } => PyRowEvent {
307
+ } => PlainRowEvent {
292
308
  table: Some(table.clone()),
293
- action: "delete".to_string(),
294
- row: Some(value_row_to_py_dict(py, row)?),
309
+ action: "delete",
310
+ row: Some(row.clone()),
295
311
  old_row: None,
296
312
  error: None,
297
- file_path: Some(file_path.clone()),
313
+ file_path: file_path.clone(),
298
314
  },
299
315
  RowEvent::Error {
300
316
  table,
301
317
  file_path,
302
318
  error,
303
- } => PyRowEvent {
319
+ } => PlainRowEvent {
304
320
  table: table.clone(),
305
- action: "error".to_string(),
321
+ action: "error",
306
322
  row: None,
307
323
  old_row: None,
308
324
  error: Some(error.clone()),
309
- file_path: Some(file_path.to_string_lossy().to_string()),
325
+ file_path: file_path.to_string_lossy().to_string(),
310
326
  },
327
+ }
328
+ }
329
+
330
+ fn row_event_to_py(py: Python<'_>, event: &RowEvent) -> PyResult<PyRowEvent> {
331
+ let plain = row_event_to_plain(event);
332
+ Ok(PyRowEvent {
333
+ table: plain.table,
334
+ action: plain.action.to_string(),
335
+ row: plain
336
+ .row
337
+ .as_ref()
338
+ .map(|r| value_row_to_py_dict(py, r))
339
+ .transpose()?,
340
+ old_row: plain
341
+ .old_row
342
+ .as_ref()
343
+ .map(|r| value_row_to_py_dict(py, r))
344
+ .transpose()?,
345
+ error: plain.error,
346
+ file_path: Some(plain.file_path),
311
347
  })
312
348
  }
313
349
 
@@ -381,4 +417,84 @@ mod python {
381
417
  m.add_class::<PyRowEvent>()?;
382
418
  Ok(())
383
419
  }
420
+
421
+ #[cfg(test)]
422
+ mod tests {
423
+ use super::*;
424
+
425
+ fn one_row() -> HashMap<String, Value> {
426
+ HashMap::from([("k".to_string(), Value::Integer(7))])
427
+ }
428
+
429
+ #[test]
430
+ fn plain_insert_maps_row_and_action() {
431
+ let p = row_event_to_plain(&RowEvent::Insert {
432
+ table: "t".into(),
433
+ row: one_row(),
434
+ file_path: "/f".into(),
435
+ });
436
+ assert_eq!(p.action, "insert");
437
+ assert_eq!(p.table.as_deref(), Some("t"));
438
+ assert_eq!(
439
+ p.row.as_ref().and_then(|r| r.get("k")),
440
+ Some(&Value::Integer(7))
441
+ );
442
+ assert!(p.old_row.is_none());
443
+ assert!(p.error.is_none());
444
+ assert_eq!(p.file_path, "/f");
445
+ }
446
+
447
+ #[test]
448
+ fn plain_update_carries_old_and_new() {
449
+ let mut new = one_row();
450
+ new.insert("k".to_string(), Value::Integer(9));
451
+ let p = row_event_to_plain(&RowEvent::Update {
452
+ table: "t".into(),
453
+ old_row: one_row(),
454
+ new_row: new,
455
+ file_path: "/f".into(),
456
+ });
457
+ assert_eq!(p.action, "update");
458
+ assert_eq!(
459
+ p.row.as_ref().and_then(|r| r.get("k")),
460
+ Some(&Value::Integer(9))
461
+ );
462
+ assert_eq!(
463
+ p.old_row.as_ref().and_then(|r| r.get("k")),
464
+ Some(&Value::Integer(7))
465
+ );
466
+ }
467
+
468
+ #[test]
469
+ fn plain_delete_has_row_no_old() {
470
+ let p = row_event_to_plain(&RowEvent::Delete {
471
+ table: "t".into(),
472
+ row: one_row(),
473
+ file_path: "/f".into(),
474
+ });
475
+ assert_eq!(p.action, "delete");
476
+ assert!(p.row.is_some());
477
+ assert!(p.old_row.is_none());
478
+ }
479
+
480
+ #[test]
481
+ fn plain_error_has_no_row_and_optional_table() {
482
+ let p = row_event_to_plain(&RowEvent::Error {
483
+ table: None,
484
+ file_path: std::path::PathBuf::from("/f"),
485
+ error: "boom".into(),
486
+ });
487
+ assert_eq!(p.action, "error");
488
+ assert!(p.table.is_none());
489
+ assert!(p.row.is_none());
490
+ assert!(p.old_row.is_none());
491
+ assert_eq!(p.error.as_deref(), Some("boom"));
492
+ assert_eq!(p.file_path, "/f");
493
+ }
494
+
495
+ #[test]
496
+ fn extract_error_displays_inner() {
497
+ assert_eq!(ExtractError("bad".to_string()).to_string(), "bad");
498
+ }
499
+ }
384
500
  }
@@ -19,7 +19,13 @@ repository = "https://github.com/thekevinscott/dirsql"
19
19
  keywords = ["sql", "filesystem", "directory", "sqlite", "index"]
20
20
  readme = "README.md"
21
21
 
22
- [project.optional-dependencies]
22
+ # Dev/test dependencies live in a PEP 735 dependency-group (not a
23
+ # `[project.optional-dependencies]` extra) so a plain `uv sync` installs them
24
+ # by default -- the reusable testing-conventions coverage/mutation jobs run
25
+ # `uv sync` with no `--extra`, and our colocated suite needs pytest-describe /
26
+ # pytest-asyncio to collect at all. Every in-repo flow is uv-based and now uses
27
+ # plain `uv sync`; the old `pip install .[dev]` extra is intentionally dropped.
28
+ [dependency-groups]
23
29
  dev = [
24
30
  "pytest>=8",
25
31
  "pytest-describe>=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