Skip to content

Commit

Permalink
Merge pull request #308 from tkkuehn/provenance
Browse files Browse the repository at this point in the history
Save more provenance information
  • Loading branch information
tkkuehn authored Aug 8, 2023
2 parents c7e3f7c + f08dee1 commit e73c908
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 38 deletions.
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

0 comments on commit e73c908

Please sign in to comment.