From 4c3a6238a495d5973d071c52385d9805fd457a80 Mon Sep 17 00:00:00 2001 From: Peter Van Dyken Date: Thu, 18 Apr 2024 11:01:21 -0400 Subject: [PATCH 1/2] Update tutorial for new bidsapp --- docs/bids_app/config.md | 1 + docs/running_snakebids/overview.md | 1 + docs/tutorial/step8/config.yml | 60 ++++++------------------------ docs/tutorial/step8/run.py | 16 ++++---- docs/tutorial/tutorial.md | 24 +++++++----- 5 files changed, 36 insertions(+), 66 deletions(-) diff --git a/docs/bids_app/config.md b/docs/bids_app/config.md index fe96f460..73fe84b1 100644 --- a/docs/bids_app/config.md +++ b/docs/bids_app/config.md @@ -144,6 +144,7 @@ A list of analysis levels in the BIDS app. Typically, this will include particip A mapping from the name of each ``analysis_level`` to the list of rules or files to be run for that analysis level. +(parse-args-config)= ### `parse_args` A dictionary of command-line parameters to make available as part of the BIDS app. Each item of the mapping is passed to [argparse's `add_argument` function](#argparse.ArgumentParser.add_argument). A number of default entries are present in a new snakebids project's config file that structure the BIDS app's CLI, but additional command-line arguments can be added as necessary. diff --git a/docs/running_snakebids/overview.md b/docs/running_snakebids/overview.md index 7048ce37..0a659371 100644 --- a/docs/running_snakebids/overview.md +++ b/docs/running_snakebids/overview.md @@ -1,3 +1,4 @@ +(running-snakebids)= # Running Snakebids Once you've specified a snakebids app with a config file and one or more workflow files, you're ready to invoke your snakebids app with the standard BIDS app CLI. diff --git a/docs/tutorial/step8/config.yml b/docs/tutorial/step8/config.yml index 9008a5ce..6cc40c78 100644 --- a/docs/tutorial/step8/config.yml +++ b/docs/tutorial/step8/config.yml @@ -1,10 +1,5 @@ bids_dir: 'data' -fwhm: - - 5 - - 10 - - 15 - - 20 pybids_inputs: bold: @@ -19,52 +14,21 @@ pybids_inputs: - task - run +analysis_levels: + - participant targets_by_analysis_level: participant: - '' # if '', then the first rule is run -analysis_levels: &analysis_levels - - participant - parse_args: - bids_dir: - help: | - The directory with the input dataset formatted according to the BIDS - standard. - - output_dir: - help: | - The directory where the output files should be stored. If you are running - group level analysis this folder should be prepopulated with the results - of the participant level analysis. - - analysis_level: - help: Level of the analysis that will be performed. - choices: *analysis_levels - - --participant_label: - help: | - The label(s) of the participant(s) that should be analyzed. The label - corresponds to sub- from the BIDS spec (so it does not - include "sub-"). If this parameter is not provided all subjects should be - analyzed. Multiple participants can be specified with a space separated - list. - nargs: '+' - - --exclude_participant_label: - help: | - The label(s) of the participant(s) that should be excluded. The label - corresponds to sub- from the BIDS spec (so it does not - include "sub-"). If this parameter is not provided all subjects should be - analyzed. Multiple participants can be specified with a space separated - list. - nargs: '+' - - --derivatives: - help: | - Path(s) to a derivatives dataset, for folder(s) that contains multiple - derivatives datasets. - default: False - type: Path - nargs: '+' + --fwhm: + help: > + Set the full-width-half-maximum values that should be used for smoothing. + type: int + nargs: + + default: + - 5 + - 10 + - 15 + - 20 diff --git a/docs/tutorial/step8/run.py b/docs/tutorial/step8/run.py index ff84e516..68b4cef0 100755 --- a/docs/tutorial/step8/run.py +++ b/docs/tutorial/step8/run.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -import os -from snakebids.app import SnakeBidsApp +from pathlib import Path +from snakebids import bidsapp, plugins - -def main(): - app = SnakeBidsApp(os.path.abspath(os.path.dirname(__file__))) - app.run_snakemake() - +app = bidsapp.app( + [ + plugins.SnakemakeBidsApp(Path(__file__).resolve().parent), + ] +) if __name__ == "__main__": - main() + app.run() diff --git a/docs/tutorial/tutorial.md b/docs/tutorial/tutorial.md index 76a34693..20f33cfd 100644 --- a/docs/tutorial/tutorial.md +++ b/docs/tutorial/tutorial.md @@ -454,7 +454,7 @@ For reference, here is the updated config file and Snakefile after these changes Now that we have pybids parsing to dynamically configure our workflow inputs based on our BIDS dataset, we are ready to turn our workflow into a [BIDS App](http://bids-apps.neuroimaging.io/). BIDS Apps are command-line apps with a standardized interface (e.g. three required positional arguments: ``bids_directory``, ``output_directory``, and ``analysis_level``). -We do this in snakebids by creating an executable python script, which uses the {class}`SnakeBidsApp ` class from {mod}`snakebids.app` to run snakemake. An example of this `run.py` script is shown below. +We do this in snakebids by creating a python script containing a {func}`bidsapp.app ` built with the Snakemake integration plugin {class}`SnakemakeBidsApp `. An example of this `run.py` script is shown below. ```{literalinclude} step8/run.py :language: python @@ -462,7 +462,15 @@ We do this in snakebids by creating an executable python script, which uses the :caption: run.py ``` -However, we will first need to add some additional information to our config file, mainly to define how to parse command-line arguments for this app. This is done with a new `parse_args` dict in the config: +This creates a bidsapp with all the standard arguments. The usage will look something like this: + +```bash +./run.py INPUT_DATASET OUTPUT_DATASET [participant|group] [--derivatives] [--participant-label LABEL...] +``` + +A more complete description of bidsapp usage can be found at . + +Additional argument can be added using the `config.yml`. For instance, we can turn our `fwhm` setting into a CLI parameter with the following: ```{literalinclude} step8/config.yml :language: yaml @@ -472,21 +480,17 @@ However, we will first need to add some additional information to our config fil :lineno-match: ``` -The above is standard boilerplate for any BIDS app. You can also define any new command-line arguments you wish. Snakebids uses the {mod}`argparse` module, and each entry in this `parse_args` dict thus becomes a call to {meth}`add_argument() ` from {class}`argparse.ArgumentParser`. When you run the workflow, snakebids adds the named argument values to the config dict, so your workflow can make use of it as if you had manually added the variable to your configfile. +Snakebids uses the {mod}`argparse` module, and each entry in this `parse_args` dict becomes a call to {meth}`add_argument() ` from {class}`argparse.ArgumentParser`. When you run the workflow, snakebids adds the named argument values to the config dict, so your workflow can make use of it as if you had manually added the variable to your configfile. See for more details. -Arguments that will receive paths should be given the item `type: Path`, as is done for `--derivatives` in the example above. Without this annotation, paths given to keyword arguments will be interpreted relative to the output directory. Indicating `type: Path` will tell Snakebids to first resolve the path according to your current working directory. - -```{note} -`bids_dir` and `output_dir` are always resolved relative to the current directory and do not need any extra annotation. -``` +In the above example, snakebids will automatically insert a key called `fwhm` into the snakemake `config` containing the values provided from the command line (or the default, if no values are provided). -BIDS apps also have a required `analysis_level` positional argument, so there are some config variables to set this as well. The analysis levels are in an `analysis_levels` list in the config, and also as keys in a `targets_by_analysis_level` dict, which can be used to map each analysis level to the name of a target rule: +The `analysis_level` positional argument can also be modified in the config. The available levels are in an `analysis_levels` list in the config. Specific snakemake targets can be mapped to these levels in the `targets_by_analysis_level` dict: ```{literalinclude} step8/config.yml :language: yaml :caption: config.yml :linenos: -:start-at: targets_by_analysis_level +:start-at: analysis_levels :end-before: parse_args :lineno-match: ``` From 1ce4bfc1ca51a3a5a7fe083b4c9deaaae9a73232 Mon Sep 17 00:00:00 2001 From: Peter Van Dyken Date: Thu, 18 Apr 2024 11:28:08 -0400 Subject: [PATCH 2/2] Add migration guide --- docs/migration/0.11_to_0.12.md | 32 ++++++++++++++++++++++++++++++++ docs/migration/0.5_to_0.8.md | 6 +++++- docs/migration/0.7_to_0.8.md | 5 ++++- docs/migration/index.md | 1 + 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 docs/migration/0.11_to_0.12.md diff --git a/docs/migration/0.11_to_0.12.md b/docs/migration/0.11_to_0.12.md new file mode 100644 index 00000000..646662c8 --- /dev/null +++ b/docs/migration/0.11_to_0.12.md @@ -0,0 +1,32 @@ +(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. + +If you haven't heavily modified your `run.py` file, you can transition simply by replacing it with the following: + +```python +#!/usr/bin/env python3 +from pathlib import Path + +from snakebids import bidsapp, plugins + +app = bidsapp.app( + [ + plugins.SnakemakeBidsApp(Path(__file__).resolve().parent), + plugins.BidsValidator(), + plugins.Version(distribution=""), + ] +) + + +def get_parser(): + """Exposes parser for sphinx doc generation, cwd is the docs dir.""" + return app.build_parser().parser + + +if __name__ == "__main__": + app.run() +``` + +The snakemake workflow will work in exactly the same way. diff --git a/docs/migration/0.5_to_0.8.md b/docs/migration/0.5_to_0.8.md index b33acaff..0cecacd1 100644 --- a/docs/migration/0.5_to_0.8.md +++ b/docs/migration/0.5_to_0.8.md @@ -1,4 +1,8 @@ -# 0.5 to 0.8 +# 0.5 to 0.8+ + +```{note} +Be sure to also [migrate](0.11_to_0.12.md) your `run.py` file to the new snakebids 0.12 syntax! +``` Starting in version 0.8, {func}`snakebids.generate_inputs()` returns a {class}`BidsInputs ` object instead of a {class}`dict `. This requires a change in the way info is accessed. The previous {class}`dict ` had top-level keys such as `"input_lists"`. After selecting such a key, you would pass the name of a component to get the information sought: diff --git a/docs/migration/0.7_to_0.8.md b/docs/migration/0.7_to_0.8.md index d4bd61a4..71210b7d 100644 --- a/docs/migration/0.7_to_0.8.md +++ b/docs/migration/0.7_to_0.8.md @@ -1,4 +1,4 @@ -# 0.7 to 0.8 +# 0.7 to 0.8+ ````{warning} If your code still has bits like this: @@ -12,6 +12,9 @@ config.update(generate_inputs( Check out the [pre-0.6 migration guide](/migration/0.5_to_0.8) to a guide on how to upgrade! ```` +```{note} +Be sure to also [migrate](0.11_to_0.12.md) your `run.py` file to the new snakebids 0.12 syntax! +``` ## Default return of {func}`~snakebids.generate_inputs()` diff --git a/docs/migration/index.md b/docs/migration/index.md index 2093fe25..539c9d1d 100644 --- a/docs/migration/index.md +++ b/docs/migration/index.md @@ -7,4 +7,5 @@ Snakebids has rapidly evolved over the last few versions, resulting in a number 0.5_to_0.8 0.7_to_0.8 +0.11_to_0.12 ```