Skip to content

feat: generalize directory discovery - #111

Open
kaitj wants to merge 6 commits into
feat/generalize-ents/phase2from
feat/generalize-ents/phase3
Open

feat: generalize directory discovery#111
kaitj wants to merge 6 commits into
feat/generalize-ents/phase2from
feat/generalize-ents/phase3

Conversation

@kaitj

@kaitj kaitj commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

This PR:

Phase 3 of the generalized entities refactor, building on top of phase 2 (#109). A refactor that facilitates BIDS indexing for any root entity type (subject, template, cohort), rather than hardcoded sub-* directories. Discovery/detection internals replaced with entity-generic ones, ands BIDS value they depended on are derived from the BIDS schema at call time instead of module-level constants.

  • Entity-generic discovery_is_bids_subject_dir_is_bids_entity_dir, _find_bids_subject_dirs_find_bids_entity_dirs, _index_bids_subject_dir_index_bids_entity_dir (all private); new _resolve_entity_dirs() orchestrator; _BIDS_SUBJECT_DIR_PATTERN / _BIDS_DATATYPE_PATTERN removed.
  • Schema-driven detection_is_bids_file / _is_bids_json_sidecar match any entity prefix; JSON data-file suffixes come from get_json_data_suffixes(), so ~300+ JSON data files (channels, events, motion, …) now index correctly.
  • .bidsignore support — root-level only, one glob per line (# comments/blanks skipped), matched against both the full relative path and the bare filename.
  • Public API (additive, backward-compatible)index_dataset() gains filters (entity key → glob pattern(s)) and deprecates include_subjects; find_bids_datasets() / batch_index_dataset() accept an optional schema; new index columns dataset_name, dataset_type, bids_version.
  • CLI--filter ENTITY=PATTERN (repeatable) and --schema; --subjects deprecated in favor of --filter sub=....
  • Tests — updated for the new private signatures; new coverage for entity-dir discovery, .bidsignore, the new index columns, and the schema/entity helpers; shared adapter and bidsignore_dataset fixtures.

Behavioral note: the index now includes JSON data files previously misclassified as sidecars, so table row counts are larger than on main.

Type of Change

  • Bug fix
  • Enhancement / feature
  • Documentation
  • Other

Related Issue(s)


Stack created with GitHub Stacks CLIGive Feedback 💬

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Benchmark Results

Local index (1 workers) Remote index (4 workers) Bold query Metadata query Subject query
feat/generalize-ents/phase3 250.717 (249.941 ± 2.219) ms 302.279 (302.132 ± 1.342) s 363.631 (393.755 ± 86.723) µs 412.043 (406.789 ± 32.717) µs 53.094 (58.389 ± 13.107) µs
main 246.581 (246.458 ± 1.291) ms 301.670 (301.675 ± 1.392) s 450.042 (465.375 ± 70.489) µs 429.193 (742.537 ± 945.957) µs 50.404 (53.229 ± 9.495) µs
v2.3.1 246.686 (246.679 ± 1.072) ms 300.716 (300.567 ± 1.741) s 416.059 (426.851 ± 95.662) µs 395.746 (405.061 ± 35.279) µs 47.749 (50.218 ± 6.677) µs
feat/generalize-ents/phase3 vs main ratio ⚪ 1.017 ⚪ 1.002 🟢 0.808 ⚪ 0.960 🔴 1.053
feat/generalize-ents/phase3 vs v2.3.1 ratio ⚪ 1.016 ⚪ 1.005 🟢 0.874 ⚪ 1.041 🔴 1.112

median (mean ± std)

🔴 Slower   ⚪ No change (<5 %)   🟢 Faster

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
__init__.py100100% 
__main__.py86891%127, 185, 187–188, 193, 223, 227, 231
_entities.py149299%70, 367
_indexing.py3191695%70, 238, 247–248, 437–438, 455–457, 460–462, 464, 670, 769, 773
_logging.py31487%31, 38, 40–41
_metadata.py49492%40–41, 67, 72
_pathlib.py21386%19, 21, 23
_schema.py120199%298
_version.py110100% 
pybids
   __init__.py40100% 
   _bidsfile.py381366%67–69, 73–75, 79–81, 85–87, 91
   _layout.py1664573%68, 77, 86, 109, 119–120, 123, 143–144, 158–159, 173–174, 177–181, 185, 187–188, 191–192, 219, 224, 238, 315–317, 379–384, 386, 389–394, 396, 450, 470
   _utils.py14564%47–51
TOTAL101810190% 

Tests Skipped Failures Errors Time
221 0 💤 0 ❌ 0 🔥 24.875s ⏱️

@kaitj
kaitj marked this pull request as ready for review August 20, 2026 18:14
@kaitj
kaitj force-pushed the feat/generalize-ents/phase3 branch from 261e8af to 5704f41 Compare August 27, 2026 20:28
@kaitj
kaitj force-pushed the feat/generalize-ents/phase3 branch from 5704f41 to 0a5a1f2 Compare August 27, 2026 20:30
Replace hardcoded constants with schema-derived values in preparation for
entity-generic indexing (Phase 3 of the generalize-entities effort).

- Remove `_BIDS_SUBJECT_DIR_PATTERN` regex — will be replaced by schema-
  derived entity patterns from `get_entity_regex()` in subsequent commits
- Derive `_BIDS_JSON_SIDECAR_EXCEPTION_SUFFIXES` from the BIDS schema via
  `get_json_data_suffixes()` instead of hardcoding `{"coordsystem"}` — now
  picks up all text-only JSON data file suffixes (coordsystem, events,
  electrodes, scans, etc.)
- Add `_get_dataset_type()` helper that reads `DatasetType` from
  `dataset_description.json`, falls back to `"derivative"` inside nested
  parent directories, and defaults to `"raw"`
Replace all subject-specific detection functions with entity-generic ones
that derive patterns from the BIDS schema at call time, rather than relying
on module-level constants frozen to the default schema.

Key changes:
- Remove `_BIDS_SUBJECT_DIR_PATTERN`, `_BIDS_ROOT_ENTITY_DIR_PATTERN`,
  `_BIDS_JSON_SIDECAR_EXCEPTION_SUFFIXES` — no module-level constant should
  call `load_bids_schema()` since it freezes values to the default schema
- Add `_compile_entity_dir_pattern()` to compile regex from entity prefixes
  and format patterns
- Generalize `_is_bids_subject_dir` → `_isis_bids_entity_dir`, `_contains_bids_subject_dirs` → `_contains_bids_entity_dirs`,
  `_find_bids_subject_dirs` → `_find_bids_entity_dirs`,
  `_index_bids_subject_dir` → `_index_bids_entity_dir`
- Add `_resolve_entity_dirs()` orchestrator that tries primary root entity
  types first and falls back to all known types
- Thread `BIDSSchemaAdapter` through the indexing pipeline so all detection
  functions use the user-specified schema rather than a default
- `find_bids_datasets()` and `_is_bids_dataset()` accept an optional
  `schema` parameter for schema-aware dataset discovery
- Generalize `_is_bids_json_sidecar()` to derive exception suffixes from
  `get_json_data_suffixes()` instead of hardcoded `{"coordsystem"}`,
  correctly recognizing ~300 additional JSON data files (channels,
  motion, events, etc.) that were previously filtered out as sidecars
- Update all tests for new function signatures and revised row counts
- Replace hardcoded _BIDS_DATATYPE_PATTERN regex with
  _build_datatype_pattern(adapter), built at runtime from the schema's
  directory entities and format patterns (lru_cache'd)
- _parse_bids_datatype now takes an adapter instead of using the
  module-level constant
- parse_bids_entities accepts optional `schema` kwarg; the cached
  implementation _cache_parse_bids_entities now takes the adapter as a
  cache-key parameter so results are per-schema
- clear_schema_caches() also clears _build_datatype_pattern and
  _cache_parse_bids_entities
- Thread the adapter through _indexing.py call sites
- _load_bidsignore_patterns(root) reads root-level .bidsignore,
  skipping blank lines and # comments (lru_cache'd on root string)
- _is_bidsignored(path, root) matches each pattern against both the
  full relative path and the bare filename, so patterns like
  sub-A01_*bold* work for files in datatype subdirectories
- _index_bids_entity_dir skips ignored files via a combined
  `not _is_bids_file(...) or _is_bidsignored(...)` guard
- test_batch_index_dataset count updated: 10052 → 9616
  (5 bids-examples datasets ship a .bidsignore)
- 7 new tests covering pattern parsing, comment/blank skipping,
  absent-file inclusion, and both matching modes
The five .bidsignore tests each rebuilt a near-identical minimal
dataset inline. Move that setup into a shared `bidsignore_dataset`
fixture in conftest.py (alongside the existing symlink_dataset
fixture) and refactor the tests to use it.

test_bidsignore_ignores_comments_and_blanks now asserts 2 files are
indexed (up from 1): the shared fixture carries both a T1w and a bold
file, so a comments/blanks-only .bidsignore provably excludes nothing.
A pass over to clean up comments and docstrings, with no behavior changes:

- Add structured Args/Returns docstrings to the public get_arrow_schema
  and get_column_names (dropping the stale TODO in the latter).
- Remove vague or code-restating inline comments and merge a few that
  spanned multiple lines.
- Trim redundant free-floating prose from a few docstrings.
- Remove group header comments in tests
@kaitj
kaitj force-pushed the feat/generalize-ents/phase3 branch from 0a5a1f2 to b77d580 Compare September 2, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generalize entities [Phase 3]

1 participant