diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 00000000..b2316674 --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,25 @@ +# Codespell configuration is within pyproject.toml +--- +name: Codespell + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Annotate locations with typos + uses: codespell-project/codespell-problem-matcher@v1 + - name: Codespell + uses: codespell-project/actions-codespell@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a11ed77..819797f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,3 +31,10 @@ repos: language: system exclude: ^docs types_or: [cython, pyi, python] + - repo: https://github.com/codespell-project/codespell + # Configuration for codespell is in pyproject.toml + rev: v2.2.6 + hooks: + - id: codespell + additional_dependencies: + - tomli diff --git a/README.md b/README.md index f89e6601..caa80542 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Snakebids is a Python package that extends [Snakemake](https://snakemake.github. Snakebids includes all of the features of Snakemake, including flexible configuration, parallel execution, and Docker/Singularity support, plus: -- **Built-in support for BIDS datasets**: Seamless workflow functionality with a wide range of BIDS datasets, accomodating various levels of complexity. +- **Built-in support for BIDS datasets**: Seamless workflow functionality with a wide range of BIDS datasets, accommodating various levels of complexity. - **BIDS App Creation**: Provide command-line invocations of your workflow following BIDS App guidelines, ensuring reproducibility and enhancing accessibility of your workflow. - **BIDS Path Construction**: Easy, flexible construction of valid BIDS paths following BIDS guiding principles, promoting data organization and sharing. - **Plugin System**: Extend the functionality of Snakebids by creating and using plugins to meet your workflow's needs. diff --git a/docs/api/paths.rst b/docs/api/paths.rst index 0efefc1f..05152508 100644 --- a/docs/api/paths.rst +++ b/docs/api/paths.rst @@ -16,7 +16,7 @@ Specs .. py:currentmodule:: snakebids.paths.specs -BIDS specs control the formatting of paths produced by the :func:`~snakebids.bids` function. They specify the order of recognized entities, placing ``ses-X`` after ``sub-Y``, for instance, no matter what order they are specified in the function. Unrecognized entitites are placed in the order specified in the function call. +BIDS specs control the formatting of paths produced by the :func:`~snakebids.bids` function. They specify the order of recognized entities, placing ``ses-X`` after ``sub-Y``, for instance, no matter what order they are specified in the function. Unrecognized entities are placed in the order specified in the function call. Because of this, each addition of entities to the spec presents a potentially breaking change. Suppose an entity called ``foo`` were added to the spec. Calls to :func:`~snakebids.bids` with ``foo`` as an argument would place the entity at the end of the path: diff --git a/docs/migration/0.11_to_0.12.md b/docs/migration/0.11_to_0.12.md index 646662c8..b55ac3de 100644 --- a/docs/migration/0.11_to_0.12.md +++ b/docs/migration/0.11_to_0.12.md @@ -1,7 +1,7 @@ (migrate-to-bidsapp)= # 0.11 to 0.12+ -Snakebids 0.12 introduces a [new, more flexible module](#snakebids.bidsapp) for creating bidsapps. This affects the syntax of the `run.py` file. Older versions used the {class}`snakebids.app.SnakeBidsApp` class to initialize the bidsapp, and this method will still work for the forseeable future. Switching to the new syntax will give access to new plugins and integrations and ensure long term support. +Snakebids 0.12 introduces a [new, more flexible module](#snakebids.bidsapp) for creating bidsapps. This affects the syntax of the `run.py` file. Older versions used the {class}`snakebids.app.SnakeBidsApp` class to initialize the bidsapp, and this method will still work for the foreseeable future. Switching to the new syntax will give access to new plugins and integrations and ensure long term support. If you haven't heavily modified your `run.py` file, you can transition simply by replacing it with the following: diff --git a/docs/migration/0.7_to_0.8.md b/docs/migration/0.7_to_0.8.md index 71210b7d..fe4c6087 100644 --- a/docs/migration/0.7_to_0.8.md +++ b/docs/migration/0.7_to_0.8.md @@ -19,7 +19,7 @@ Be sure to also [migrate](0.11_to_0.12.md) your `run.py` file to the new snakebi ## Default return of {func}`~snakebids.generate_inputs()` -V0.8 switches the default return value of {func}`~snakebids.generate_inputs()` from {class}`~snakebids.BidsDatasetDict` to {class}`~snakebids.BidsDataset`. Legacy code still relying on the old dictionary can avoid the update by setting the `use_bids_inputs` pararmeter in {func}`~snakebids.generate_inputs()` to `False`: +V0.8 switches the default return value of {func}`~snakebids.generate_inputs()` from {class}`~snakebids.BidsDatasetDict` to {class}`~snakebids.BidsDataset`. Legacy code still relying on the old dictionary can avoid the update by setting the `use_bids_inputs` parameter in {func}`~snakebids.generate_inputs()` to `False`: ```python config.update(generate_inputs( diff --git a/pyproject.toml b/pyproject.toml index 450b0c2d..8e147a64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ pvandyken-deprecated = "0.0.4" # Need this until py39 importlib-resources = ">=5.12.0" -# Below are non-direct dependencies (i.e. dependencies of other depenencies) +# Below are non-direct dependencies (i.e. dependencies of other dependencies) # specified to ensure a version with a pre-built wheel is installed depending # on the python version being used. numpy = [ @@ -83,7 +83,7 @@ pytest = "^8" pytest-mock = "^3.7.0" poethepoet = "^0.27" pre-commit = "^3.0.0" -# a mkinit dep has the 'platform_system == "Windows"' as a marker on an incompatible dependeny +# a mkinit dep has the 'platform_system == "Windows"' as a marker on an incompatible dependency # (pydantic<2.0 cf copier), so set the inverse as a marker here so mkinit can # still be resolved mkinit = { version="^1.1.0", markers = "platform_system != 'Windows'" } @@ -308,3 +308,10 @@ more_itertools = "itx" [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false + +[tool.codespell] +# Ref: https://github.com/codespell-project/codespell#using-a-config-file +skip = '.git*,*.lock,*.css,./typings' +check-hidden = true +# ignore-regex = '' +# ignore-words-list = '' diff --git a/scripts/update_bids.py b/scripts/update_bids.py index b8d68350..dac40046 100644 --- a/scripts/update_bids.py +++ b/scripts/update_bids.py @@ -22,7 +22,7 @@ def generate_stub(mod: ModuleType, imports: list[str], funcs: Iterable[str]): mod module for which stub file should be written imports - list of imports and other statemets to appear at the beginning of the file + list of imports and other statements to appear at the beginning of the file funcs list of function declarations """ diff --git a/snakebids/app.py b/snakebids/app.py index 39a64fe8..4c38743a 100644 --- a/snakebids/app.py +++ b/snakebids/app.py @@ -119,7 +119,7 @@ def _check_deprecations(self): if self.version is not None: msg = ( "`SnakeBidsApp.version` is deprecated and no longer has any effect. To " - "explcitly set the app version, use the `snakebids.plugins.Version` " + "explicitly set the app version, use the `snakebids.plugins.Version` " "plugin" ) warnings.warn(msg, stacklevel=3) diff --git a/snakebids/bidsapp/hookspecs.py b/snakebids/bidsapp/hookspecs.py index a4503969..c8ca2cf0 100644 --- a/snakebids/bidsapp/hookspecs.py +++ b/snakebids/bidsapp/hookspecs.py @@ -72,7 +72,7 @@ def update_cli_namespace(namespace: dict[str, Any], config: dict[str, Any]): :meth:`~argparse.ArgumentParser.parse_args` (equivalent to :class:`vars(Namespace) `). Any modifications made to this :class:`dict` will be carried forward in app initialization. For instance, if - an entry is deleleted from ``namespace``, it will not be available to downstream + an entry is deleted from ``namespace``, it will not be available to downstream plugins or be copied into ``config``. Parameters diff --git a/snakebids/cli.py b/snakebids/cli.py index abbc4152..c09404a4 100644 --- a/snakebids/cli.py +++ b/snakebids/cli.py @@ -14,7 +14,7 @@ def add_dynamic_args( ) -> None: """Do nothing. - Originally added --filter- and --wildcards- argumets to the CLI. Kept + Originally added --filter- and --wildcards- arguments to the CLI. Kept as a placeholder for apps that relied on it for generating documentation. This functionality is now native to `SnakeBidsApp`. """ diff --git a/snakebids/core/datasets.py b/snakebids/core/datasets.py index 18f40a14..42458641 100644 --- a/snakebids/core/datasets.py +++ b/snakebids/core/datasets.py @@ -363,7 +363,7 @@ def expand( """Safely expand over given paths with component wildcards. Uses the entity-value combinations found in the dataset to expand over the given - paths. Extra wildcards can be specifed as keyword arguments. + paths. Extra wildcards can be specified as keyword arguments. By default, expansion over paths with extra wildcards not accounted for by the component causes an error. This prevents accidental partial expansion. To allow @@ -430,8 +430,8 @@ def filter( This method allows you to expand over a subset of your wildcards. This could be useful for extracting subjects from a specific patient group, running different - rules on different aquisitions, and any other reason you may need to filter your - data after the workflow has already started. + rules on different acquisitions, and any other reason you may need to filter + your data after the workflow has already started. Takes entities as keyword arguments assigned to values or list of values to select from the component. Only columns containing the provided entity-values @@ -468,7 +468,7 @@ class BidsComponent(BidsPartialComponent): A component is a set of data entries all corresponding to the same type of object. Entries vary over a set of entities. For example, a component may represent all the - unprocessed, T1-weighted anatomical images aqcuired from a group of 100 subjects, + unprocessed, T1-weighted anatomical images acquired from a group of 100 subjects, across 2 sessions, with three runs per session. Here, the subject, session, and run are the entities over which the component varies. Each entry in the component has a single value assigned for each of the three entities (e.g subject 002, session @@ -791,7 +791,7 @@ def input_wildcards(self) -> dict[str, MultiSelectDict[str, str]]: def as_dict(self) -> BidsDatasetDict: """Get the layout as a legacy dict. - Included primarily for backward compatability with older versions of snakebids, + Included primarily for backward compatibility with older versions of snakebids, where generate_inputs() returned a dict rather than the `BidsDataset` class Returns diff --git a/snakebids/core/input_generation.py b/snakebids/core/input_generation.py index 0077881a..097104db 100644 --- a/snakebids/core/input_generation.py +++ b/snakebids/core/input_generation.py @@ -423,7 +423,7 @@ def _gen_bids_layout( existing database. index_metadata - A boolen that determines whether to parse and index metadata + A boolean that determines whether to parse and index metadata validate A boolean that determines whether to validate the bids dataset diff --git a/snakebids/io/printing.py b/snakebids/io/printing.py index 6ae89d58..addadf1d 100644 --- a/snakebids/io/printing.py +++ b/snakebids/io/printing.py @@ -106,7 +106,7 @@ def new_col(val: str): def _find_elision(widths: list[int], excluded: slice, overflow: int) -> slice: - # Add 4 to overflow to account for elipses + # Add 4 to overflow to account for ellipses if max(sum(widths[excluded]) - 4, 0) >= overflow: return excluded span = excluded.stop - excluded.start @@ -118,7 +118,7 @@ def _find_elision(widths: list[int], excluded: slice, overflow: int) -> slice: mid = floor(num_vals / 2) # need different rules for handling exclusions of even length depending on whether - # theres an even or odd total number of values. + # there's an even or odd total number of values. left_bias = floor if num_vals % 2 else ceil right_bias = ceil if num_vals % 2 else floor diff --git a/snakebids/jinja2_ext/snakebids_version.py b/snakebids/jinja2_ext/snakebids_version.py index 714bbdf5..42845bdb 100644 --- a/snakebids/jinja2_ext/snakebids_version.py +++ b/snakebids/jinja2_ext/snakebids_version.py @@ -9,7 +9,7 @@ class SnakebidsVersionExtension(Extension): - """Retrieve the latest snakebids vesion from pypi. + """Retrieve the latest snakebids version from pypi. Stores value in the ``snakebids_version`` global variable. """ diff --git a/snakebids/plugins/base.py b/snakebids/plugins/base.py index 44495559..1ee31448 100644 --- a/snakebids/plugins/base.py +++ b/snakebids/plugins/base.py @@ -112,7 +112,7 @@ def add_argument( *name_or_flags: str, **kwargs: Unpack[AnyArgumentArgs], ) -> argparse.Action | None: - """Add argument to parser, applying prefix to dest as necesary.""" + """Add argument to parser, applying prefix to dest as necessary.""" if self.PREFIX and not kwargs["dest"].startswith(self.PREFIX): kwargs["dest"] = f"{self.PREFIX}.{kwargs['dest']}" return parser.add_argument(*name_or_flags, **kwargs) diff --git a/snakebids/plugins/bidsargs.py b/snakebids/plugins/bidsargs.py index d3152adc..a7066403 100644 --- a/snakebids/plugins/bidsargs.py +++ b/snakebids/plugins/bidsargs.py @@ -70,7 +70,7 @@ class BidsArgs(PluginBase): CLI Arguments ~~~~~~~~~~~~~ All arguments are added by default, but can be disabled using their respective - parameters. Additinally, arguments can be directly overriden by adding arguments + parameters. Additionally, arguments can be directly overridden by adding arguments to the parser before the plugin runs, using the following ``dests``: - ``bids_dir``: The input bids directory @@ -93,7 +93,7 @@ class BidsArgs(PluginBase): Overriding just one or two of the positional arguments may alter the order, preventing the app from being called correctly. Thus, if any of the positional - args are being overriden, they all should be. + args are being overridden, they all should be. Analysis levels diff --git a/snakebids/plugins/component_edit.py b/snakebids/plugins/component_edit.py index 818b1a0f..90e461e4 100644 --- a/snakebids/plugins/component_edit.py +++ b/snakebids/plugins/component_edit.py @@ -122,7 +122,7 @@ class ComponentEdit(PluginBase): 4. ``ENTITY:none`` selects all paths without the entity. 5. ``ENTITY:any`` removes filters for the entity. - CLI arguments created by this plugin cannot be overriden. + CLI arguments created by this plugin cannot be overridden. Parameters ---------- diff --git a/snakebids/plugins/pybidsdb.py b/snakebids/plugins/pybidsdb.py index 4bacb88f..3b7e146e 100644 --- a/snakebids/plugins/pybidsdb.py +++ b/snakebids/plugins/pybidsdb.py @@ -27,7 +27,7 @@ class Pybidsdb(PluginBase): CLI Arguments ~~~~~~~~~~~~~ - Two arguments are added to the CLI. These can be overriden by adding arguments + Two arguments are added to the CLI. These can be overridden by adding arguments with corresponding ``dests`` before this plugin is run: - ``plugins.pybidsdb.dir``: (:class:`~pathlib.Path`) Path of the database diff --git a/snakebids/plugins/validator.py b/snakebids/plugins/validator.py index a7c46d52..e68b52e7 100644 --- a/snakebids/plugins/validator.py +++ b/snakebids/plugins/validator.py @@ -29,7 +29,7 @@ class BidsValidator(PluginBase): InvalidBidsError is raised. An argument --skip-bids-validation is added to the CLI. This argument can be - overriden by adding an argument with dest ``plugins.validator.skip`` before this + overridden by adding an argument with dest ``plugins.validator.skip`` before this plugin runs. Two entries are added to config: diff --git a/snakebids/project_template/{{name_slug}}/config/snakebids.yml b/snakebids/project_template/{{name_slug}}/config/snakebids.yml index 9a066025..8a0c279d 100644 --- a/snakebids/project_template/{{name_slug}}/config/snakebids.yml +++ b/snakebids/project_template/{{name_slug}}/config/snakebids.yml @@ -9,7 +9,7 @@ targets_by_analysis_level: - 'all' # if '', then the first rule is run # Configure components: -# Each entry creates a new component that can be retreived within the workflow +# Each entry creates a new component that can be retrieved within the workflow # via `generate_inputs`. # Filters are used to select paths: each filter has an `entity` key and a # single value or list of values to select. diff --git a/snakebids/tests/test_datasets.py b/snakebids/tests/test_datasets.py index 2bf2d28a..59c9b5f2 100644 --- a/snakebids/tests/test_datasets.py +++ b/snakebids/tests/test_datasets.py @@ -355,7 +355,7 @@ class TestBidsComponentExpand: """ `extension` is generally excluded from the generated components because its wildcard is immediately adjacent to the previous wildcard (e.g. - sub-{subject}_{suffix}{extenions}), making it impossible to correctly parse using + sub-{subject}_{suffix}{extension}), making it impossible to correctly parse using `glob_wildcards`. Its absence does not affect the spirit of the tests. We also exclude extra_entities from the generated Components for the same reason: diff --git a/snakebids/tests/test_generate_inputs.py b/snakebids/tests/test_generate_inputs.py index 5f556b3d..4c3794bc 100644 --- a/snakebids/tests/test_generate_inputs.py +++ b/snakebids/tests/test_generate_inputs.py @@ -994,7 +994,7 @@ def path_entities(draw: st.DrawFn): valid_chars = st.characters( min_codepoint=48, max_codepoint=122, whitelist_categories=["Ll", "Lu"] ) - # We need to explicitely exclude keywords here because the current implementation + # We need to explicitly exclude keywords here because the current implementation # of glob_wildcards uses a named tuple, which doesn't allow keywords as attributes. path_text = st.text(valid_chars, min_size=1).filter( lambda s: not keyword.iskeyword(s) @@ -1821,9 +1821,9 @@ def test_exclude_participant_does_not_make_all_other_filters_regex( for comp in dataset.values() ) - # Create an extra set of paths by modifing one of the existing components to put - # foo after a set of entity values. If that filter gets changed to a regex, all - # of the suffixed decoys will get picked up by pybids + # Create an extra set of paths by modifying one of the existing components to + # put foo after a set of entity values. If that filter gets changed to a regex, + # all of the suffixed decoys will get picked up by pybids ziplist = dict(itx.first(rooted.values()).zip_lists) mut_entity = itx.first( filter(lambda e: e not in {"subject", "extension"}, ziplist) diff --git a/snakebids/utils/containers.py b/snakebids/utils/containers.py index 3379117b..5eb6956b 100644 --- a/snakebids/utils/containers.py +++ b/snakebids/utils/containers.py @@ -50,7 +50,7 @@ class ImmutableList(Sequence[_T_co], Generic[_T_co]): Unlike tuples, only a single type parameter is supported. In other words, ``ImmutableList`` cannot be specified via type hints as a fixed length sequence - containing heterogenous items. A tuple specified as ``tuple[str, int, str]`` would + containing heterogeneous items. A tuple specified as ``tuple[str, int, str]`` would be specified as ``ImmutableList[str | int]`` """ diff --git a/snakebids/utils/output.py b/snakebids/utils/output.py index 70702819..eee46fa2 100644 --- a/snakebids/utils/output.py +++ b/snakebids/utils/output.py @@ -77,8 +77,8 @@ def write_output_mode(dotfile: Path, mode: Mode) -> None: def _get_snakebids_file(outputdir: Path) -> dict[str, str] | None: """Ensure populated dir contains .snakebids file, retrieving it if it does. - First checks if outputdir doesn't exist or is completely empty, returing None if so. - If it does have data, it checks for a .snakebids file, returning its contents if + First checks if outputdir doesn't exist or is completely empty, returning None if + so. If it does have data, it checks for a .snakebids file, returning its contents if found. If no .snakebids file is found, it raises an exception. Parameters @@ -89,7 +89,7 @@ def _get_snakebids_file(outputdir: Path) -> dict[str, str] | None: Returns ------- Dict or None - None if output dir is nonexistant or empty, otherwise the contents + None if output dir is nonexistent or empty, otherwise the contents of the .snakebids file Raises diff --git a/snakebids/utils/snakemake_io.py b/snakebids/utils/snakemake_io.py index 2e28747d..36d3368f 100644 --- a/snakebids/utils/snakemake_io.py +++ b/snakebids/utils/snakemake_io.py @@ -25,7 +25,7 @@ def regex(filepattern: str) -> str: if match.group("constraint"): msg = ( "Constraint regex must be defined only in the first " - "occurence of the wildcard in a string." + "occurrence of the wildcard in a string." ) raise ValueError(msg) regex_list.append(f"(?P={wildcard})")