bids2table

bids2table

CI Docs codecov Ruff Python3 License

Index BIDS datasets fast, locally or in the cloud.

Installation

Install the core package using pip:

pip install bids2table

Variants

Depending on your use case, you may need extra dependencies. Choose the option that matches your use case:

If you want to... Run this command
Add cloud storage support (S3, GCS) pip install bids2table[cloud]
Enable pybids compatibility pip install bids2table[pybids]
Install everything pip install bids2table[cloud,pybids]
Warning

Deprecation Warning: Previous versions used bids2table[s3] for cloud support. While the s3 extra still works for now, it will be removed upon release of 2.4.x. Please update your installation scripts to use [cloud].

Development Version

To test out the absolute latest features directly from the main branch, install directly from GitHub:

pip install "bids2table[cloud,pybids] @ git+https://github.com/childmindresearch/bids2table.git"

Usage

To run these examples, you will need to clone the bids-examples repo.

git clone -b 1.9.0 https://github.com/bids-standard/bids-examples.git

Finding BIDS datasets

You can search a directory for valid BIDS datasets using b2t2 find

b2t2 find bids-examples | head -n 10
bids-examples/asl002
bids-examples/ds002
bids-examples/ds005
bids-examples/asl005
bids-examples/ds051
bids-examples/eeg_rishikesh
bids-examples/asl004
bids-examples/asl003
bids-examples/ds003
bids-examples/eeg_cbm

Indexing datasets from the command line

Indexing datasets is done with b2t2 index. Here we index a single example dataset, saving the output as a parquet file.

b2t2 index -o ds102.parquet bids-examples/ds102
ds102: 100%|███████████████████████████████████████| 26/26 [00:00<00:00, 154.12it/s, sub=26, N=130]

You can also index a list of datasets. Note that each iteration in the progress bar represents one dataset.

b2t2 index -o bids-examples.parquet bids-examples/*
100%|████████████████████████████████████████████| 87/87 [00:00<00:00, 113.59it/s, ds=None, N=9727]

You can pipe the output of b2t2 find to b2t2 index to create an index of all datasets under a root directory.

b2t2 find bids-examples | b2t2 index -o bids-examples.parquet
97it [00:01, 96.05it/s, ds=ieeg_filtered_speech, N=10K]

The resulting index will include both top-level datasets (as in the previous command) as well nested derivatives datasets.

Filtering files

Use --filter to index only files matching an entity key and value pattern. The syntax is ENTITY=PATTERN, where PATTERN is a literal value or a glob (e.g. --filter sub=01 or --filter sub=0*):

b2t2 index -o ds102_sub01.parquet --filter sub=01 bids-examples/ds102

Repeat the flag to add more filters. Multiple patterns for the same entity are combined with OR, and different entities with AND — the example below indexes sub-01's bold and events files:

b2t2 index -o ds.parquet --filter suffix=bold --filter suffix=events --filter sub=01 bids-examples/ds102
Warning

Note: --subjects is deprecated; use --filter sub=... instead. It still works but emits a DeprecationWarning.

Indexing datasets hosted on S3

bids2table supports indexing datasets hosted on S3 via cloudpathlib. To use this functionality, make sure to install bids2table with the s3 extra. Or you can also just install cloudpathlib directly

pip install cloudpathlib[s3]

As an example, here we index all datasets on OpenNeuro

b2t2 index -o openneuro.parquet \
  -j 8 --use-threads s3://openneuro.org/ds*
100%|█████████████████████████████████████| 1408/1408 [12:25<00:00,  1.89it/s, ds=ds006193, N=1.2M]

Using 8 threads, we can index all ~1400 OpenNeuro datasets (1.2M files) in less than 15 minutes.

Indexing datasets from python

You can also index datasets using the Python API.

import bids2table as b2t2
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

# Index a single dataset.
tab = b2t2.index_dataset("bids-examples/ds102")

# Find and index a batch of datasets.
tabs = b2t2.batch_index_dataset(
    b2t2.find_bids_datasets("bids-examples"),
)
tab = pa.concat_tables(tabs)

# Index a dataset on S3.
tab = b2t2.index_dataset("s3://openneuro.org/ds000224")

# Save as parquet.
pq.write_table(tab, "ds000224.parquet")

# Convert to a pandas dataframe.
df = tab.to_pandas(types_mapper=pd.ArrowDtype)

Indexing with a custom BIDS schema

By default, bids2table uses the BIDS schema bundled with bidsschematools. Pass a schema= argument to index_dataset, batch_index_dataset, get_arrow_schema, get_column_names, or validate_bids_entities to use a different schema. The argument may be a path to a schema directory, a string URI accepted by bidsschematools.schema.load_schema, or a pre-loaded bidsschematools.types.Namespace.

import bidsschematools.schema
import bids2table as b2t2

# Use a pre-loaded schema (e.g. when indexing several datasets that share one).
schema = bidsschematools.schema.load_schema()
tab = b2t2.index_dataset("bids-examples/ds102", schema=schema)

# Or pass a path to a custom schema directory.
tab = b2t2.index_dataset("/data/ds001", schema="/path/to/custom-schema")

Different schema arguments may be used for different calls within the same process; per-call schemas propagate to worker processes when max_workers > 0.

From the command line, pass the same schema directory or file to --schema:

b2t2 index -o ds.parquet --schema /path/to/custom-schema bids-examples/ds102
 1""".. include:: ../README.md"""  # noqa: D415
 2
 3__all__ = [
 4    "SchemaSpec",
 5    "batch_index_dataset",
 6    "clear_schema_caches",
 7    "cloudpathlib_is_available",
 8    "find_bids_datasets",
 9    "format_bids_path",
10    "get_arrow_schema",
11    "get_column_names",
12    "index_dataset",
13    "load_bids_metadata",
14    "parse_bids_entities",
15    "validate_bids_entities",
16]
17
18import importlib.util
19
20if importlib.util.find_spec("pandas"):
21    __all__.append("pybids")
22
23from bids2table._entities import (
24    format_bids_path,
25    parse_bids_entities,
26    validate_bids_entities,
27)
28from bids2table._indexing import (
29    batch_index_dataset,
30    clear_schema_caches,
31    find_bids_datasets,
32    get_arrow_schema,
33    get_column_names,
34    index_dataset,
35)
36from bids2table._metadata import load_bids_metadata
37from bids2table._pathlib import cloudpathlib_is_available
38from bids2table._schema import SchemaSpec
39from bids2table._version import *  # noqa: F403 - import all of generated module
SchemaSpec = bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None
def batch_index_dataset( roots: Sequence[str | pathlib.Path | cloudpathlib.cloudpath.CloudPath], max_workers: int | None = 0, executor_cls: type[concurrent.futures.process.ProcessPoolExecutor | concurrent.futures.thread.ThreadPoolExecutor] = <class 'concurrent.futures.process.ProcessPoolExecutor'>, *, filters: dict[str, str | list[str]] | None = None, show_progress: bool = False, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> Generator[pyarrow.lib.Table, None, None]:
343def batch_index_dataset(
344    roots: Sequence[str | PathT],
345    max_workers: int | None = 0,
346    executor_cls: type[ProcessPoolExecutor | ThreadPoolExecutor] = ProcessPoolExecutor,
347    *,
348    filters: dict[str, str | list[str]] | None = None,
349    show_progress: bool = False,
350    schema: SchemaSpec = None,
351) -> Generator[pa.Table, None, None]:
352    """Index a batch of BIDS datasets.
353
354    Args:
355        roots: List of BIDS dataset root directories.
356        max_workers: Number of indexing processes to run in parallel. Setting
357            `max_workers=0` (the default) uses the main process only. Setting
358            `max_workers=None` starts as many workers as there are available CPUs. See
359            `concurrent.futures.ProcessPoolExecutor` for details.
360        executor_cls: Executor class to use for parallel indexing.
361        filters: Dict mapping entity keys to glob patterns or lists of patterns.
362        show_progress: Show progress bar.
363        schema: Optional `SchemaSpec`. `None` uses the default BIDS schema.
364
365    Yields:
366        An Arrow table index for each BIDS dataset.
367    """
368    func = partial(_batch_index_func, filters=filters, schema=schema)
369    file_count = 0
370    for dataset, table in (
371        pbar := tqdm(
372            _pmap(func, roots, max_workers, executor_cls=executor_cls),
373            total=len(roots) if isinstance(roots, Sequence) else None,
374            disable=show_progress not in {True, "dataset"},
375        )
376    ):
377        file_count += len(table)
378        pbar.set_postfix({"ds": dataset, "N": _hfmt(file_count)}, refresh=False)
379        yield table

Index a batch of BIDS datasets.

Arguments:
  • roots: List of BIDS dataset root directories.
  • max_workers: Number of indexing processes to run in parallel. Setting max_workers=0 (the default) uses the main process only. Setting max_workers=None starts as many workers as there are available CPUs. See concurrent.futures.ProcessPoolExecutor for details.
  • executor_cls: Executor class to use for parallel indexing.
  • filters: Dict mapping entity keys to glob patterns or lists of patterns.
  • show_progress: Show progress bar.
  • schema: Optional SchemaSpec. None uses the default BIDS schema.
Yields:

An Arrow table index for each BIDS dataset.

def clear_schema_caches() -> None:
136def clear_schema_caches() -> None:
137    """Clear the LRU caches of the schema-dependent lookup functions.
138
139    Call to force recomputation when cached inputs change, or to release
140    memory after a large indexing run.
141
142    This also clears ``bidsschematools.schema.load_schema``'s own cache,
143    since a path-based schema is ultimately resolved through it; without that,
144    a changed schema file would be served from that cache even after the
145    bids2table caches above are cleared.
146    """
147    bidsschematools.schema.load_schema.cache_clear()
148    _load_from_path.cache_clear()
149    entity_arrow_schema.cache_clear()
150    _lookups_from_arrow.cache_clear()
151    _build_datatype_pattern.cache_clear()
152    _cache_parse_bids_entities.cache_clear()
153    _is_bids_dataset.cache_clear()
154    _get_bids_dataset.cache_clear()
155    _load_bidsignore_patterns.cache_clear()
156    _read_dataset_description.cache_clear()
157    get_entity_directory_order.cache_clear()
158    get_json_data_suffixes.cache_clear()
159    get_file_entity_prefixes.cache_clear()

Clear the LRU caches of the schema-dependent lookup functions.

Call to force recomputation when cached inputs change, or to release memory after a large indexing run.

This also clears bidsschematools.schema.load_schema's own cache, since a path-based schema is ultimately resolved through it; without that, a changed schema file would be served from that cache even after the bids2table caches above are cleared.

def cloudpathlib_is_available() -> bool:
38def cloudpathlib_is_available() -> bool:
39    """Check if cloudpathlib is available.
40
41    Returns:
42        ``True`` if the ``cloud`` extra (cloudpathlib) is installed, else ``False``.
43    """
44    return _CLOUDPATHLIB_AVAILABLE

Check if cloudpathlib is available.

Returns:

True if the cloud extra (cloudpathlib) is installed, else False.

def find_bids_datasets( root: str | pathlib.Path | cloudpathlib.cloudpath.CloudPath, exclude: str | list[str] | None = None, maxdepth: int | None = None, *, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> Generator[pathlib.Path | cloudpathlib.cloudpath.CloudPath, None, None]:
218def find_bids_datasets(
219    root: str | PathT,
220    exclude: str | list[str] | None = None,
221    maxdepth: int | None = None,
222    *,
223    schema: SchemaSpec = None,
224) -> Generator[PathT, None, None]:
225    """Find all BIDS datasets under a root directory.
226
227    Args:
228        root: Root path to begin search.
229        exclude: Glob pattern or list of patterns matching sub-directory names to
230            exclude from the search.
231        maxdepth: Maximum depth to search.
232        schema: BIDS schema specification to use. If ``None``, uses the bundled
233            default schema.
234
235    Yields:
236        Root paths of all BIDS datasets under `root`.
237    """
238    root = as_path(root)
239    adapter = load_bids_schema(schema)
240
241    if isinstance(exclude, str):
242        exclude = [exclude]
243    elif exclude is None:
244        exclude = []
245    exclude_patterns = [re.compile(fnmatch.translate(pat)) for pat in exclude]
246
247    entry_count = 1
248    ds_count = 0
249
250    if _is_bids_dataset(root, adapter):
251        ds_count += 1
252        yield root
253
254    stack = [(root, 0)]
255
256    while stack:
257        top, depth = stack.pop()
258
259        inside_bids = _is_bids_dataset(top, adapter)
260        depth += 1
261
262        for entry in top.iterdir():
263            entry_count += 1
264
265            if any(re.fullmatch(pat, entry.name) for pat in exclude_patterns):
266                continue
267
268            if _is_bids_dataset(entry, adapter):
269                ds_count += 1
270                yield entry
271
272            # Checks if we should descend into this directory (not reach final depth).
273            descend = maxdepth is None or depth < maxdepth
274            # Heuristic checks whether the filename looks like a (visible) directory.
275            descend = descend and not (entry.suffix or entry.name.startswith("."))
276            # Only descend into specific subdirectories of BIDS directories.
277            descend = descend and (
278                not inside_bids or entry.name in _BIDS_NESTED_PARENT_DIRNAMES
279            )
280            # Check if actually a directory (slow, so short-circuit if possible).
281            if descend and entry.is_dir():
282                stack.append((entry, depth))

Find all BIDS datasets under a root directory.

Arguments:
  • root: Root path to begin search.
  • exclude: Glob pattern or list of patterns matching sub-directory names to exclude from the search.
  • maxdepth: Maximum depth to search.
  • schema: BIDS schema specification to use. If None, uses the bundled default schema.
Yields:

Root paths of all BIDS datasets under root.

def format_bids_path( entities: dict[str, typing.Any], int_format: str = '%d', schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> pathlib.Path:
215def format_bids_path(
216    entities: dict[str, Any], int_format: str = "%d", schema: SchemaSpec = None
217) -> Path:
218    """Construct a formatted BIDS path from an entities dict.
219
220    Directory entities (e.g. ``sub``, ``ses``, ``tpl``) become path
221    directories and are also repeated in the file name, per the BIDS
222    convention. The special entities are handled by their role: ``datatype``
223    is the innermost directory, while ``suffix`` and ``ext`` are appended to
224    the file name. All other entities are formatted into the file name.
225
226    Args:
227        entities: dict mapping BIDS entity names to values.
228        int_format: format string for integer (index) BIDS values.
229        schema: optional BIDS schema. If ``None``, uses the default schema.
230
231    Returns:
232        A formatted `Path` instance.
233    """
234    adapter = load_bids_schema(schema)
235    dir_order = get_entity_directory_order(adapter)
236    special = {
237        cfg.get("name", entity)
238        for entity, cfg in adapter.entity_schema.items()
239        if cfg.get("format") == "special"
240    }
241
242    name_parts = []
243    for name, value in entities.items():
244        if name not in special:
245            if isinstance(value, int):
246                value = int_format % value
247            name_parts.append(f"{name}-{value}")
248    name = "_".join(name_parts)
249
250    if suffix := entities.get("suffix"):
251        name += f"_{suffix}"
252    if ext := entities.get("ext"):
253        name += ext
254
255    # Prepend parent directories, innermost to outermost.
256    path = Path(name)
257    if datatype := entities.get("datatype"):
258        path = Path(datatype) / path
259    for dir_entity in reversed(dir_order):
260        if dir_entity in entities:
261            path = Path(f"{dir_entity}-{entities[dir_entity]}") / path
262    return path

Construct a formatted BIDS path from an entities dict.

Directory entities (e.g. sub, ses, tpl) become path directories and are also repeated in the file name, per the BIDS convention. The special entities are handled by their role: datatype is the innermost directory, while suffix and ext are appended to the file name. All other entities are formatted into the file name.

Arguments:
  • entities: dict mapping BIDS entity names to values.
  • int_format: format string for integer (index) BIDS values.
  • schema: optional BIDS schema. If None, uses the default schema.
Returns:

A formatted Path instance.

def get_arrow_schema( *, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None | bids2table._schema.BIDSSchemaAdapter = None) -> pyarrow.lib.Schema:
162def get_arrow_schema(*, schema: SchemaSpec | BIDSSchemaAdapter = None) -> pa.Schema:
163    """Get Arrow schema of the BIDS dataset index.
164
165    Args:
166        schema: BIDS schema specification to use. If ``None``, uses the bundled
167            default schema.
168
169    Returns:
170        The PyArrow schema of the BIDS dataset index.
171    """
172    adapter = (
173        schema if isinstance(schema, BIDSSchemaAdapter) else load_bids_schema(schema)
174    )
175    entity_schema = entity_arrow_schema(adapter)
176    index_fields = {
177        name: pa.field(name, cfg["dtype"], metadata=cfg["metadata"])
178        for name, cfg in _INDEX_ARROW_FIELDS.items()
179    }
180    fields = [
181        index_fields["dataset"],
182        *entity_schema,
183        index_fields["extra_entities"],
184        index_fields["dataset_name"],
185        index_fields["dataset_type"],
186        index_fields["bids_version"],
187        index_fields["root"],
188        index_fields["path"],
189    ]
190    metadata = {
191        **entity_schema.metadata,
192        b"bids2table_version": version.encode(),
193    }
194    return pa.schema(fields, metadata=metadata)

Get Arrow schema of the BIDS dataset index.

Arguments:
  • schema: BIDS schema specification to use. If None, uses the bundled default schema.
Returns:

The PyArrow schema of the BIDS dataset index.

def get_column_names( *, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> type[enum.StrEnum]:
197def get_column_names(*, schema: SchemaSpec = None) -> type[enum.StrEnum]:
198    """Get an enum of the BIDS index columns.
199
200    Args:
201        schema: BIDS schema specification to use. If ``None``, uses the bundled
202            default schema.
203
204    Returns:
205        A ``str`` enum class whose members are the index column names.
206    """
207    arrow_schema = get_arrow_schema(schema=schema)
208    items = []
209    for f in arrow_schema:
210        name = f.metadata[b"name"].decode()
211        items.append((name, name))
212
213    BIDSColumn = enum.StrEnum("BIDSColumn", items)  # noqa: N806 - class type
214    BIDSColumn.__doc__ = "Enum of BIDS index column names."
215    return BIDSColumn

Get an enum of the BIDS index columns.

Arguments:
  • schema: BIDS schema specification to use. If None, uses the bundled default schema.
Returns:

A str enum class whose members are the index column names.

def index_dataset( root: str | pathlib.Path | cloudpathlib.cloudpath.CloudPath, include_subjects: str | list[str] | None = None, *, filters: dict[str, str | list[str]] | None = None, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> pyarrow.lib.Table:
285def index_dataset(
286    root: str | PathT,
287    include_subjects: str | list[str] | None = None,
288    *,
289    filters: dict[str, str | list[str]] | None = None,
290    schema: SchemaSpec = None,
291) -> pa.Table:
292    """Index a BIDS dataset.
293
294    Args:
295        root: BIDS dataset root directory.
296        include_subjects: Glob pattern or list of patterns for matching subjects to
297            include in the index. .. deprecated:: Use ``filters={'sub': ...}`` instead.
298        filters: Dict mapping entity keys to glob patterns or lists of patterns.
299        schema: BIDS schema specification to use. If ``None``, uses the bundled
300            default schema.
301
302    Returns:
303        An Arrow table index of the BIDS dataset.
304    """
305    root = as_path(root)
306
307    if include_subjects is not None:
308        warnings.warn(
309            "include_subjects is deprecated; use filters={'sub': ...} instead.",
310            DeprecationWarning,
311            stacklevel=2,
312        )
313        if filters is None:
314            filters = {}
315        filters["sub"] = include_subjects
316
317    adapter = load_bids_schema(schema)
318    arrow_schema = get_arrow_schema(schema=adapter)
319
320    dataset, _ = _get_bids_dataset(root)
321    if dataset is None:
322        _logger.warning(f"Path {root} is not a valid BIDS dataset directory.")
323        return pa.Table.from_pylist([], schema=arrow_schema)
324
325    entity_dirs = _resolve_entity_dirs(root, adapter=adapter, filters=filters)
326    entity_dirs = sorted(entity_dirs, key=lambda p: p.name)
327    if len(entity_dirs) == 0:
328        _logger.warning(f"Path {root} contains no matching entity dirs.")
329        return pa.Table.from_pylist([], schema=arrow_schema)
330
331    tables = []
332    file_count = 0
333    for entity_dir in entity_dirs:
334        prefix = entity_dir.name.split("-")[0]
335        _, table = _index_bids_entity_dir(
336            entity_dir, prefix, adapter, arrow_schema, dataset, filters
337        )
338        tables.append(table)
339        file_count += len(table)
340    return pa.concat_tables(tables).combine_chunks()

Index a BIDS dataset.

Arguments:
  • root: BIDS dataset root directory.
  • include_subjects: Glob pattern or list of patterns for matching subjects to include in the index. .. deprecated:: Use filters={'sub': ...} instead.
  • filters: Dict mapping entity keys to glob patterns or lists of patterns.
  • schema: BIDS schema specification to use. If None, uses the bundled default schema.
Returns:

An Arrow table index of the BIDS dataset.

def load_bids_metadata( path: str | pathlib.Path | cloudpathlib.cloudpath.CloudPath, *, inherit: bool = True) -> dict[str, typing.Any]:
12def load_bids_metadata(path: str | PathT, *, inherit: bool = True) -> dict[str, Any]:
13    """Load the full JSON sidecar metadata for a BIDS file.
14
15    Sidecar files are loaded according to the inheritance principle in top-down order.
16
17    Args:
18        path: BIDS file path
19        inherit: Load the full metadata according to inheritance. Otherwise, load only
20            the first JSON sidecar found in the bottom-up search.
21
22    Returns:
23        A sidecar metadata dictionary.
24    """
25    path = as_path(path)
26    entities = _cache_parse_bids_entities(path)
27    query = dict(entities, ext=".json")
28
29    metadata = {}
30
31    parent = path.parent
32    if inherit:
33        sidecars = reversed(list(_find_bids_parents(parent, query)))
34    else:
35        sidecars = [next(_find_bids_parents(parent, query))]
36
37    for path in sidecars:
38        try:
39            data = _load_json(path)
40            metadata.update(data)
41        except (json.JSONDecodeError, TypeError):
42            continue
43    return metadata

Load the full JSON sidecar metadata for a BIDS file.

Sidecar files are loaded according to the inheritance principle in top-down order.

Arguments:
  • path: BIDS file path
  • inherit: Load the full metadata according to inheritance. Otherwise, load only the first JSON sidecar found in the bottom-up search.
Returns:

A sidecar metadata dictionary.

def parse_bids_entities( path: str | pathlib.Path, *, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> dict[str, str]:
73def parse_bids_entities(
74    path: str | Path, *, schema: SchemaSpec = None
75) -> dict[str, str]:
76    """Parse entities from BIDS file path.
77
78    Parses all BIDS filename `"{key}-{value}"` entities as well as special entities:
79    datatype, suffix, ext (extension). Does not validate entities or cast to types.
80
81    Args:
82        path: BIDS path to parse.
83        schema: Optional BIDS schema. If ``None``, uses the default schema.
84
85    Returns:
86        A dict mapping BIDS entity keys to values.
87
88    Raises:
89        TypeError: If `schema` is not a valid `SchemaSpec`.
90        ValueError: If the schema contains no directory entities.
91    """
92    if isinstance(path, str):
93        path = Path(path)
94    adapter = load_bids_schema(schema)
95    return _cache_parse_bids_entities(path, adapter)

Parse entities from BIDS file path.

Parses all BIDS filename "{key}-{value}" entities as well as special entities: datatype, suffix, ext (extension). Does not validate entities or cast to types.

Arguments:
  • path: BIDS path to parse.
  • schema: Optional BIDS schema. If None, uses the default schema.
Returns:

A dict mapping BIDS entity keys to values.

Raises:
  • TypeError: If schema is not a valid SchemaSpec.
  • ValueError: If the schema contains no directory entities.
def validate_bids_entities( entities: dict[str, typing.Any], *, schema: bidsschematools.types.namespace.Namespace | str | pathlib.Path | cloudpathlib.cloudpath.CloudPath | None = None) -> tuple[dict[str, str | int], dict[str, typing.Any]]:
136def validate_bids_entities(
137    entities: dict[str, Any], *, schema: SchemaSpec = None
138) -> tuple[dict[str, BIDSValue], dict[str, Any]]:
139    """Validate BIDS entities against a BIDS schema.
140
141    Args:
142        entities: dict mapping BIDS keys to unvalidated entities
143        schema: optional `SchemaSpec` (`Namespace | str | PathT | None`).
144            `None` uses the default BIDS schema bundled with bidsschematools.
145
146    Returns:
147        `(valid_entities, extra_entities)` — valid entities cast to the
148        declared type, plus any leftover entries that did not match a
149        known entity or failed validation.
150    """
151    adapter = load_bids_schema(schema)
152    pa_schema = entity_arrow_schema(adapter)
153    return _pyarrow_validate_entities(entities, pa_schema=pa_schema)

Validate BIDS entities against a BIDS schema.

Arguments:
  • entities: dict mapping BIDS keys to unvalidated entities
  • schema: optional SchemaSpec (Namespace | str | PathT | None). None uses the default BIDS schema bundled with bidsschematools.
Returns:

(valid_entities, extra_entities) — valid entities cast to the declared type, plus any leftover entries that did not match a known entity or failed validation.