Skip to main content

bidsval

Validate BIDS datasets in pure Python.

bidsval reads the official BIDS schema and checks datasets against the rules it contains. It runs in-process with no external runtime, returns typed (pydantic) results, and validates a whole dataset, a single subject, a single file, or a single expression. Every published BIDS schema version ships inside the package, so it works offline and you choose the version with one argument.

Because the schema drives everything, bidsval covers all of BIDS - anatomical, functional, diffusion, fieldmaps, perfusion, EEG, MEG, iEEG, behavioural, PET, microscopy, motion, NIRS, MRS - not a fixed set of modalities. Point it at a newer schema and the newer rules apply with no code change.

Verified against the reference Deno bids-validator across the full bids-examples corpus and real MRI/EEG/MEG/PET datasets, at a matched schema, with zero false positives. It covers filename and path legality, file integrity, sidecar field presence and value types, associated files, tabular columns (types, order, uniqueness), the inheritance principle, and dataset-level checks. The remaining gaps are HED and symlink validation; see the comparison and "Roadmap".

Install

pip install bidsval

That is all you need: the bundled BIDS schemas and the content readers (nibabel, pandas, mne) come with it, so it works offline. Python 3.10 to 3.14.

For development, from a clone:

git clone https://github.com/karellopez/bidsval
cd bidsval
pip install -e ".[dev]"   # editable install, plus the test and lint tooling

Validate a dataset

From Python:

import bidsval

report = bidsval.validate("/path/to/dataset")
report.is_valid            # False if there are any errors
report.counts              # {'error': 3, 'warning': 27, 'ignore': 0}
for verdict in report.files:
    for issue in verdict.issues:
        print(issue.severity.value, issue.code, verdict.path, issue.message)

# Narrower granularity:
bidsval.validate_subject("/path/to/dataset", "sub-01")
bidsval.validate_file("/path/to/dataset", "sub-01/anat/sub-01_T1w.nii.gz")

From the command line:

# Text summary to the terminal (exits non-zero on errors, so it drops into CI):
bidsval validate /path/to/dataset

# Validate one subject (NIfTI headers are read by default):
bidsval validate /path/to/dataset --subject sub-01

# Pick the output type (independent of where it goes):
bidsval validate /path/to/dataset --out-type json     # JSON to stdout
bidsval validate /path/to/dataset --out-type sarif    # SARIF to stdout (CI / IDE code scanning)

# Write report files: --out-dir holds report.<ext> for each selected type:
bidsval validate /path/to/dataset --out-type html --out-dir reports/   # reports/report.html
bidsval validate /path/to/dataset --out-type all  --out-dir reports/   # report.txt/.json/.sarif/.html
bidsval validate /path/to/dataset --out-type all                       # the same, into the current dir

# Show only the severities you care about (does not change pass/fail):
bidsval validate /path/to/dataset --show error           # errors only (default shows all)

Flags: --schema <version|url|path>, --subject sub-01, --no-headers (skip NIfTI header reading; headers are on by default, needs nibabel), --out-type text|json|sarif|html|all (alias --output-type; default text; one type prints to stdout, while all and multi-format sets write report.<ext> into --out-dir, or the current directory if it is omitted), --out-dir DIR, --show error,warning,ignore,all (filter displayed findings; default all), --max-rows N, --filenames-only, --list-schemas, -v.

Choose a schema version

Every published BIDS schema is bundled, and any other version or a URL is fetched and cached. One argument selects the schema; everything downstream is unchanged:

bidsval.validate("/data", schema="1.10.0")               # a bundled version
bidsval.validate("/data", schema="latest")               # the development tip (fetched)
bidsval.validate("/data", schema="https://.../schema.json")  # any URL (fetched + cached)
bidsval.validate("/data", schema="/path/to/schema.json") # a local dereferenced schema.json
bidsval.validate("/data", schema="/path/to/src/schema")  # a YAML schema source directory
bidsval.available_versions()                             # bundled: ['1.8.0', ... '1.11.1']
bidsval validate /data --schema 1.9.0
bidsval validate /data --schema latest
bidsval validate /data --schema https://bids-specification.readthedocs.io/en/v1.10.0/schema.json
bidsval schema --schema 1.10.0      # show the versions a selector resolves to

Evaluate a single expression

The expression engine is usable on its own - handy for understanding a rule or checking one condition:

from bidsval import evaluate_string

evaluate_string("suffix == 'T1w'", {"suffix": "T1w"})                       # True
evaluate_string("nifti_header.dim[0] == 3", {"nifti_header": {"dim": [4]}}) # False
bidsval eval "suffix == 'T1w'" --context '{"suffix": "T1w"}'

Ask what BIDS declares for a kind of file

Validation answers "is this dataset correct", which needs the dataset to exist. The same schema can answer the earlier question a metadata form or a conversion template asks: what does BIDS declare for a file of this kind, at what level, of what type.

from bidsval import schema

for field in schema.sidecar_fields("pet", "pet"):
    if field.is_required:
        print(field.name, field.type, field.unit, field.description[:60])

schema.field_applies("EEGReference", "eeg", "eeg")     # True
schema.field_applies("EEGReference", "anat", "T1w")    # False

{f.name: f.level for f in schema.dataset_description_fields()}["Name"]  # "required"

This is not a lookup of rules.sidecars.<datatype>. BIDS files sidecar rules by selector expression, so most MRI metadata sits under modality == "mri" and a datatype-keyed reading finds 6 fields for anat/T1w where the schema declares 76. See schema introspection.

How it works

  • The schema is the engine. The BIDS schema expresses validation logic as expressions: selectors that decide when a rule applies (suffix == 'T1w') and checks that must hold (nifti_header.dim[0] == 3). bidsval reads the schema's vocabulary (datatypes, entities, suffixes, extensions) and rules, builds a context for each file, and evaluates the rules against it. No BIDS terms are hardcoded.
  • Parsing comes from bidsschematools; bidsval adds the evaluator that walks the syntax tree (no eval/exec), the file/context layers, and the rule loop.
  • A finding is reported only when a rule produces a determinate failure. When the context cannot determine a rule (for example a check that needs a content layer not yet built), the rule is skipped rather than guessed, so the validator does not emit false errors.
  • Results are pydantic models, ready to serialise to JSON or bind to a GUI.

Layout

Module Responsibility
bidsval.schema Resolve a selector to one schema object; read BIDS vocabulary from it; answer what the standard declares for a kind of file. The only version-aware code.
bidsval.files Index a dataset's files (FileTree).
bidsval.context Build the per-file context: entities, datatype, inheritance-merged sidecar, associated files, loaded content.
bidsval.expr Evaluate BIDS schema expressions against a context.
bidsval.rules Apply the schema's checks, sidecar fields (presence + value type), and tabular-column rules; plus bespoke checks.
bidsval.validate validate / validate_subject / validate_file.
bidsval.render Render a report as text / JSON / SARIF / HTML.
bidsval.issues / bidsval.report Typed findings and results.
bidsval.cli The bidsval command.

Done: the schema engine and the file/context/rule layers; dataset/subject/file validation; filename and path legality (with .bidsignore); file-integrity checks; sidecar field presence and full value-type checks; the associations layer (events, bval/bvec, channels, ASL, coordsystem, atlas, ...); tabular checks (types, order, uniqueness, additional columns); inheritance checks; dataset-level checks; CITATION.cff; derivatives recursion; bundled + URL + latest schema selection; text / JSON / SARIF / HTML output.

Roadmap

Filename/path legality, file integrity, the cross-file and tabular checks (including full value-type checking and type redefinition), inheritance checks, CITATION.cff, derivatives recursion, and the coordsystems/atlas-description aggregates are all in (see comparison vs the Deno reference validator for full coverage). Remaining:

  1. Deferred reference checks: HED (needs a HED validator dependency) and symlink checks (the annex-symlink tension). Only the gzip/ome/tiff content-header aggregates are still unbuilt.
  2. The ahead-of-market features: requirement-level completeness per subject, reasoned waivers, explain mode, and one-click fixes (provenance and fix hints are already on every finding).

Documentation

The docs live in docs/ on GitHub:

Develop

pytest                                  # unit suite, incl. the schema expression oracle
BIDSVAL_REAL_DATA=1 pytest tests/test_real_data.py   # real-data validation (if datasets present)
ruff check src tests

License

MIT. See LICENSE.

Release files for bidsval 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bidsval 0.1.1
File Size Uploaded
bidsval-0.1.1.tar.gz 706.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bidsval 0.1.1
File Interpreter ABI Platform
bidsval-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / bidsval-0.1.1.tar.gz

Download URL bidsval-0.1.1.tar.gz
Size 706.7 kB
Tags Source
SHA-256 checksum
How to use checksums
0875e23959fc722976b1e257ad75a37f91c5e76b301c898a9929b9b39f830cbc
BLAKE2b-256 checksum
How to use checksums
a17fe5f39af8ff2deebb9fc6d4b1c5e3bfb1d59345e16b42552bfb57f592a40a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / bidsval-0.1.1-py3-none-any.whl

Download URL bidsval-0.1.1-py3-none-any.whl
Size 697.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fa0f3df7936bae40a79b5792028beb8c2d7e9f92129efb573e75308aed07eb2d
BLAKE2b-256 checksum
How to use checksums
8c605339931d3791bd06bc13d9da8d9e00de0ba2ae6dd73070e5e1bef4eab18c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

0.0.5

2 release files

0.0.4

1 release file

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page