Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 107 additions & 37 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ boutiques = "^0.5.25"
more-itertools = ">=8,<10"
cached-property = "^1.5.2"
pvandyken-deprecated = "0.0.3"
importlib_metadata = [ { version = "==1.4", python = "<3.8" } ]

# Below are non-direct dependencies (i.e. dependencies of other depenencies)
# specified to ensure a version with a pre-built wheel is installed depending
Expand Down
33 changes: 32 additions & 1 deletion snakebids/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
import snakemake
from snakemake.io import load_configfile

if sys.version_info >= (3, 8):
from importlib import metadata
else:
import importlib_metadata as metadata

from snakebids.cli import (
SnakebidsArgs,
add_dynamic_args,
Expand Down Expand Up @@ -70,6 +75,26 @@ def wrapper(self: "SnakeBidsApp"):
return wrapper


def _get_app_version(self: SnakeBidsApp) -> str | None:
"""Attempt to get the app version, returning None if we can't.

This will succeed only if the following conditions are true:

1. The Snakebids app is a distribution package installed in the current
environment.
2. The app's distribution package has the same name as this
SnakeBidsApp's snakemake_dir
"""
try:
return metadata.version(self.snakemake_dir.name)
except metadata.PackageNotFoundError:
logger.warning(
"App version not found; will be recorded in output as 'unknown'. "
"If this is unexpected, please contact the app maintainer."
)
return None


@attr.define(slots=False)
class SnakeBidsApp:
"""Snakebids app with config and arguments.
Expand Down Expand Up @@ -130,6 +155,7 @@ class SnakeBidsApp:
lambda self: load_configfile(self.snakemake_dir / self.configfile_path),
takes_self=True,
)
version: Optional[str] = attr.Factory(_get_app_version, takes_self=True)
args: Optional[SnakebidsArgs] = None

def run_snakemake(self) -> None:
Expand Down Expand Up @@ -207,7 +233,12 @@ def run_snakemake(self) -> None:
# Write the config file
write_config_file(
config_file=new_config_file,
data=app.config,
data=dict(
app.config,
snakemake_version=metadata.version("snakemake"),
snakebids_version=metadata.version("snakebids"),
app_version=app.version or "unknown",
),
force_overwrite=True,
)

Expand Down
26 changes: 26 additions & 0 deletions snakebids/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
from ..app import SnakeBidsApp
from .mock.config import config

if sys.version_info >= (3, 8):
from importlib import metadata
else:
import importlib_metadata as metadata


@pytest.fixture
def app(mocker: MockerFixture):
Expand Down Expand Up @@ -178,6 +183,9 @@ def test_runs_in_correct_mode(
"pybidsdb_reset": True,
"snakefile": Path("Snakefile"),
"output_dir": outputdir.resolve(),
"snakemake_version": metadata.version("snakemake"),
"snakebids_version": metadata.version("snakebids"),
"app_version": "unknown", # not installing a snakebids app here
}
)
if root == "app" and not tail:
Expand Down Expand Up @@ -297,6 +305,24 @@ def plugin(my_app: SnakeBidsApp):

assert app.foo == "bar" # type: ignore

def test_get_app_version_no_package(self, app: SnakeBidsApp):
assert app.version is None

def test_get_app_version_package(self, mocker: MockerFixture):
metadata_pkg = (
"importlib.metadata" if sys.version_info >= (3, 8) else "importlib_metadata"
)
mock = mocker.patch(f"{metadata_pkg}.version", return_value="0.1.0")
app = SnakeBidsApp(
Path("my_app"),
snakefile_path=Path("Snakefile"),
configfile_path=Path("mock/config.yaml"),
config=copy.deepcopy(config),
)

assert app.version == "0.1.0"
mock.assert_called_once_with("my_app")


class TestGenBoutiques:
def test_boutiques_descriptor(self, tmp_path: Path, app: SnakeBidsApp):
Expand Down
Loading