From 1814b6b2a26d6fb8b4f00b08f712dc78539d77e2 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 30 Jul 2025 08:35:16 -0400 Subject: [PATCH 1/5] chore: Depend on attrs, latest bidsschematools --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d976209..aeae20c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,8 @@ classifiers = [ ] requires-python = ">=3.9" dependencies = [ - "bidsschematools >=1.0", + "attrs >=24.1", + "bidsschematools >= 1.0.10", ] [project.optional-dependencies] From b3eabafbdddda437aa3e78ad0c0f0cae0c2b4508 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 30 Jul 2025 08:36:25 -0400 Subject: [PATCH 2/5] chore: Move test dependencies into dependency group --- pyproject.toml | 15 +++++++++------ tox.ini | 3 ++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aeae20c..5b525c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,12 +27,6 @@ dependencies = [ ] [project.optional-dependencies] -test = [ - "pytest >=8", - "pytest-cov >=5", - "coverage[toml] >=7.2", - "datalad >=1.1", -] cli = [ "typer >=0.15", ] @@ -141,3 +135,12 @@ inline-quotes = "single" [tool.ruff.format] quote-style = "single" + +[dependency-groups] +test = [ + "pytest >=8", + "pytest-cov >=5", + "coverage[toml] >=7.2", + "datalad >=1.1", + "cattrs>=24.1.3", +] diff --git a/tox.ini b/tox.ini index 0e50beb..e32c9a0 100644 --- a/tox.ini +++ b/tox.ini @@ -45,8 +45,9 @@ pass_env = CLICOLOR CLICOLOR_FORCE extras = - test cli +dependency_groups = + test uv_resolution = min: lowest-direct deps = From 82cf503a409809fbe64d788c3a8b3b58aa52c242 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 30 Jul 2025 08:38:09 -0400 Subject: [PATCH 3/5] rf: Add lazy _typing module to avoid typing import --- src/bids_validator/types/_typings.py | 19 +++++++++++++++++++ src/bids_validator/types/files.py | 18 ++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 src/bids_validator/types/_typings.py diff --git a/src/bids_validator/types/_typings.py b/src/bids_validator/types/_typings.py new file mode 100644 index 0000000..dd42a78 --- /dev/null +++ b/src/bids_validator/types/_typings.py @@ -0,0 +1,19 @@ +__all__ = ( + 'Self', + 'TYPE_CHECKING', + 'Any', +) + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Any, Self +else: + + def __getattr__(name: str): + if name in __all__: + import typing + + return getattr(typing, name) + + msg = f'Module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) diff --git a/src/bids_validator/types/files.py b/src/bids_validator/types/files.py index 2b4d731..18f04e4 100644 --- a/src/bids_validator/types/files.py +++ b/src/bids_validator/types/files.py @@ -1,14 +1,16 @@ """Types for working with file trees.""" +from __future__ import annotations + import os import posixpath import stat from functools import cached_property from pathlib import Path -from typing import Union import attrs -from typing_extensions import Self # PY310 + +from . import _typings as t __all__ = ('FileTree',) @@ -58,7 +60,7 @@ def is_symlink(self) -> bool: return stat.S_ISLNK(_stat.st_mode) -def as_direntry(obj: os.PathLike) -> Union[os.DirEntry, UserDirEntry]: +def as_direntry(obj: os.PathLike) -> os.DirEntry | UserDirEntry: """Convert PathLike into DirEntry-like object.""" if isinstance(obj, os.DirEntry): return obj @@ -69,10 +71,10 @@ def as_direntry(obj: os.PathLike) -> Union[os.DirEntry, UserDirEntry]: class FileTree: """Represent a FileTree with cached metadata.""" - direntry: Union[os.DirEntry, UserDirEntry] = attrs.field(repr=False, converter=as_direntry) - parent: Union['FileTree', None] = attrs.field(repr=False, default=None) + direntry: os.DirEntry | UserDirEntry = attrs.field(repr=False, converter=as_direntry) + parent: FileTree | None = attrs.field(repr=False, default=None) is_dir: bool = attrs.field(default=False) - children: dict[str, 'FileTree'] = attrs.field(repr=False, factory=dict) + children: dict[str, FileTree] = attrs.field(repr=False, factory=dict) name: str = attrs.field(init=False) def __attrs_post_init__(self): @@ -85,8 +87,8 @@ def __attrs_post_init__(self): def read_from_filesystem( cls, direntry: os.PathLike, - parent: Union['FileTree', None] = None, - ) -> Self: + parent: FileTree | None = None, + ) -> t.Self: """Read a FileTree from the filesystem. Uses :func:`os.scandir` to walk the directory tree. From 07649a9bcd30a0b6e8f61002b78311a792ebe91d Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 30 Jul 2025 12:53:55 -0400 Subject: [PATCH 4/5] chore: Refine ruff rules --- pyproject.toml | 10 ++++++++-- tests/types/test_files.py | 2 -- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b525c7..2e9f399 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,10 @@ exclude = ".*" [tool.ruff] line-length = 99 -extend-exclude = ["_version.py"] +extend-exclude = [ + "_version.py", + "tests/data", +] [tool.ruff.lint] extend-select = [ @@ -131,7 +134,10 @@ inline-quotes = "single" [tool.ruff.lint.extend-per-file-ignores] "setup.py" = ["D"] -"*/test_*.py" = ["S101"] +"*/test_*.py" = [ + "S101", + "D", +] [tool.ruff.format] quote-style = "single" diff --git a/tests/types/test_files.py b/tests/types/test_files.py index 229994e..fc9d247 100644 --- a/tests/types/test_files.py +++ b/tests/types/test_files.py @@ -1,5 +1,3 @@ -# ruff: noqa: D100 - import attrs from bids_validator.types.files import FileTree From a354d5aa6b4bfcb8d70ec9e9c003e79a58067df6 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 30 Jul 2025 14:36:39 -0400 Subject: [PATCH 5/5] chore: Install git-annex from PyPI --- .github/workflows/build-test-deploy.yml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml index 105d704..2deeaf8 100644 --- a/.github/workflows/build-test-deploy.yml +++ b/.github/workflows/build-test-deploy.yml @@ -83,20 +83,8 @@ jobs: python-version: ${{ matrix.python-version }} allow-prereleases: true - - name: Install git-annex ubuntu - if: matrix.os == 'ubuntu-latest' - run: sudo apt-get update && sudo apt-get install git-annex - - - name: Install git-annex macos - if: matrix.os == 'macos-latest' - run: brew install git-annex - - - name: Install git-annex windows - if: matrix.os == 'windows-latest' - uses: crazy-max/ghaction-chocolatey@v3 - with: - args: install git-annex --yes --ignore-checksums - continue-on-error: true # This can fail for stupid reasons ¯\_(ツ)_/¯ + - name: Install git-annex + run: uv tool install git-annex - name: Show software versions run: |