Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/maint/1.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
effigies committed Nov 14, 2024
2 parents 729df89 + e859546 commit 6922930
Show file tree
Hide file tree
Showing 38 changed files with 252 additions and 103 deletions.
21 changes: 15 additions & 6 deletions .github/workflows/schemacode_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ on:
push:
branches:
- "master"
- "maint/*"
tags:
- "schema-*"
pull_request:
branches:
- "master"
- "maint/*"
- "*"

concurrency:
Expand Down Expand Up @@ -36,7 +39,9 @@ jobs:
run: pip install --upgrade tools/schemacode[all]
if: ${{ startsWith(github.ref, 'refs/tags/schema-') }}
- name: "Build archive on tag"
run: pytest tools/schemacode/bidsschematools -k make_archive
run: |
python -m pytest -k make_archive
working-directory: tools/schemacode
env:
BIDSSCHEMATOOLS_RELEASE: 1
if: ${{ startsWith(github.ref, 'refs/tags/schema-') }}
Expand Down Expand Up @@ -87,14 +92,15 @@ jobs:
- name: "Run tests"
run: |
python -m pytest -vs --pyargs bidsschematools -m "not validate_schema" \
--cov-append --cov-report=xml --cov=bidsschematools --doctest-modules
python -m pytest -vs --doctest-modules -m "not validate_schema" \
--cov-append --cov-report=xml --cov-report=term --cov=src/bidsschematools
working-directory: tools/schemacode

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: unit_${{ matrix.os }}_${{ matrix.python-version }}
path: coverage.xml
path: tools/schemacode/coverage.xml
if: success()

publish:
Expand Down Expand Up @@ -143,13 +149,16 @@ jobs:
python -m pip install -e ./tools/schemacode[all]
- name: Run schema validation tests
run: python -m pytest --pyargs bidsschematools -m "validate_schema" --cov-append --cov-report=xml --cov=bidsschematools
run: |
python -m pytest -vs --doctest-modules -m "not validate_schema" \
--cov-append --cov-report=xml --cov-report=term --cov=src/bidsschematools
working-directory: tools/schemacode

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: schema_validation
path: coverage.xml
path: tools/schemacode/coverage.xml
if: success()

upload_to_codecov:
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
ci:
skip: [shellcheck]
exclude: 'tools/schemacode/bidsschematools/tests/data/broken_dataset_description.json'
exclude: 'tools/schemacode/src/bidsschematools/tests/data/broken_dataset_description.json'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
Expand Down Expand Up @@ -89,7 +89,7 @@ repos:
- types-jsonschema
- jsonschema
- httpx
args: ["tools/schemacode/bidsschematools"]
args: ["tools/schemacode/src"]
pass_filenames: false
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
Expand Down
1 change: 1 addition & 0 deletions tools/schemacode/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
recursive-include tests *
57 changes: 0 additions & 57 deletions tools/schemacode/bidsschematools/__main__.py

This file was deleted.

1 change: 0 additions & 1 deletion tools/schemacode/bidsschematools/data/metaschema.json

This file was deleted.

1 change: 0 additions & 1 deletion tools/schemacode/bidsschematools/data/schema

This file was deleted.

26 changes: 12 additions & 14 deletions tools/schemacode/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,17 @@ bst = "bidsschematools.__main__:cli"
Homepage = "https://github.com/bids-standard/bids-specification"

[tool.setuptools.dynamic]
version = {file = "bidsschematools/data/schema/SCHEMA_VERSION"}
version = {file = "src/bidsschematools/data/schema/SCHEMA_VERSION"}

[tool.setuptools.package-data]
bidsschematools = [
"data/metaschema.json",
"data/schema/BIDS_VERSION",
"data/schema/SCHEMA_VERSIO",
"data/schema/SCHEMA_VERSION",
"data/schema/**/*.yaml",
"tests/data/**/*",
"tests/data/**/.bidsignore"
"tests/data/*"
]

[tool.setuptools.packages.find]
include = ["bidsschematools*"]

[tool.black]
line-length = 99
include = '\.pyi?$'
Expand All @@ -93,13 +89,6 @@ exclude = '''
)
'''

[tool.coverage.run]
omit = [
"*/*/tests/*",
"**/tests/*"
]
parallel = true

[tool.isort]
multi_line_output = 3
profile = "black"
Expand All @@ -113,3 +102,12 @@ markers = [
]
minversion = "6.0"
xfail_strict = true

[tool.coverage.paths]
source = [
"src/bidsschematools",
"**/site-packages/bidsschematools"
]

[tool.coverage.run]
parallel = true
189 changes: 189 additions & 0 deletions tools/schemacode/src/bidsschematools/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import json
import logging
import os
import re
import sys
from itertools import chain

import click

if sys.version_info < (3, 9):
from importlib_resources import files
else:
from importlib.resources import files

from .rules import regexify_filename_rules
from .schema import export_schema, load_schema
from .validator import _bidsignore_check


@click.group()
@click.option("-v", "--verbose", count=True)
@click.option("-q", "--quiet", count=True)
def cli(verbose, quiet):
"""BIDS Schema Tools"""
verbose = verbose - quiet
logging.getLogger("bidsschematools").setLevel(logging.WARNING - verbose * 10)


@cli.command()
@click.option("--schema")
@click.option("--output", default="-")
@click.pass_context
def export(ctx, schema, output):
"""Export BIDS schema to JSON document"""
logger = logging.getLogger("bidsschematools")
schema = load_schema(schema)
text = export_schema(schema)
if output == "-":
logger.debug("Writing to stdout")
print(text)
else:
output = os.path.abspath(output)
logger.debug(f"Writing to {output}")
with open(output, "w") as fobj:
fobj.write(text)


@cli.command()
@click.option("--output", default="-")
@click.pass_context
def export_metaschema(ctx, output):
"""Export BIDS schema to JSON document"""
metaschema = files("bidsschematools.data").joinpath("metaschema.json").read_text()
if output == "-":
print(metaschema, end="")
else:
output = os.path.abspath(output)
with open(output, "w") as fobj:
fobj.write(metaschema)


@cli.command("pre-receive-hook")
@click.option("--schema", "-s", type=click.Path(), help="Path to the BIDS schema")
@click.option(
"--input", "-i", "input_", default="-", type=click.Path(), help="Input file (default: stdin)"
)
@click.option(
"--output",
"-o",
"output",
default="-",
type=click.Path(),
help="Output file (default: stdout)",
)
def pre_receive_hook(schema, input_, output):
"""Validate filenames from a list of files against the BIDS schema
The expected input takes the following form:
```
bids-hook-v2
{"Name": "My dataset", "BIDSVersion": "1.9.0", "DatasetType": "raw"}
ignore-pattern1
...
ignore-patternN
0001
.datalad/config
.gitattributes
CHANGES
README
dataset_description.json
participants.tsv
sub-01/anat/sub-01_T1w.nii.gz
...
```
The header identifies the protocol version. For protocol ``bids-hook-v2``,
the second line MUST be the dataset_description.json file, with any newlines removed.
The following lines, up to the line containing "0001", are ignore patterns
from the .bidsignore file. The lines following "0001" are the filenames to
be validated.
This is intended to be used in a git pre-receive hook.
"""
logger = logging.getLogger("bidsschematools")
schema = load_schema(schema)

# Slurp inputs for now; we can think about streaming later
if input_ == "-":
stream = sys.stdin
else:
stream = open(input_)

first_line = next(stream)
if first_line == "bids-hook-v2\n":
# V2 format: header line, description JSON, followed by legacy format
description_str = next(stream)
fail = False
try:
description: dict = json.loads(description_str)
except json.JSONDecodeError:
fail = True
if fail or not isinstance(description, dict):
logger.critical("Protocol error: invalid JSON in description")
logger.critical(
"Dataset description must be one JSON object, written to a single line"
)
logger.critical("Received: %s", description_str)
stream.close()
sys.exit(2)
else:
# Legacy: ignore patterns, followed by "0001", followed by filenames
stream = chain([first_line], stream)
description = {}

dataset_type = description.get("DatasetType", "raw")
logger.info("Dataset type: %s", dataset_type)

ignore = []
for line in stream:
if line == "0001\n":
break
ignore.append(line.strip())
logger.info("Ignore patterns found: %d", len(ignore))

all_rules = chain.from_iterable(
regexify_filename_rules(group, schema, level=2)
for group in (schema.rules.files.common, schema.rules.files.raw)
)
if dataset_type == "derivative":
all_rules = chain(
all_rules,
regexify_filename_rules(schema.rules.files.derivatives, schema, level=2),
)

regexes = [rule["regex"] for rule in all_rules]
# XXX Hack for phenotype files - this can be removed once we
# have a schema definition for them
regexes.append(r"phenotype/.*\.(tsv|json)")

output = sys.stdout if output == "-" else open(output, "w")

rc = 0
any_files = False
valid_files = 0
with output:
for filename in stream:
if not any_files:
logger.debug("Validating files, first file: %s", filename)
any_files = True
filename = filename.strip()
if any(_bidsignore_check(pattern, filename, "") for pattern in ignore):
continue
if not any(re.match(regex, filename) for regex in regexes):
print(filename, file=output)
rc = 1
else:
valid_files += 1

if valid_files == 0:
logger.error("No files to validate")
rc = 2

stream.close()
sys.exit(rc)


if __name__ == "__main__":
cli()
Loading

0 comments on commit 6922930

Please sign in to comment.