Skip to content

Commit

Permalink
feat: add gitfile option to make it possible to use local git repos w…
Browse files Browse the repository at this point in the history
…hen importing modules (snakemake#1376)

* feat: add gitfile option to make it possible to use local git repos when importing modules.

* refactor: clean up code.

* fix: provide path/url when retrieving schema.

* style: make black formatting happy.

* test: add test cases for using a local git repo

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* Update sourcecache.py

* fix: update tests

* make black formatting happy

* add missing expected-results

* fix failing tests

* feat: make it possible to use relative repo path

* style: make black happy

Co-authored-by: Johannes Köster <johannes.koester@tu-dortmund.de>
  • Loading branch information
Smeds and johanneskoester committed Aug 25, 2022
1 parent f0ec73d commit 1a3b91f
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 5 deletions.
15 changes: 11 additions & 4 deletions snakemake/sourcecache.py
Expand Up @@ -149,14 +149,21 @@ def __init__(
self.path = path

def get_path_or_uri(self):
return "git+file://{}/{}@{}".format(self.repo_path, self.path, self.ref)
return "git+file://{}/{}@{}".format(
os.path.abspath(self.repo_path), self.path, self.ref
)

def join(self, path):
path = os.path.normpath("/".join((self.path, path)))
if ON_WINDOWS:
# convert back to URL separators
# (win specific separators are introduced by normpath above)
path = path.replace("\\", "/")
return LocalGitFile(
self.repo_path,
"/".join((self.path, path)),
path,
tag=self.tag,
ref=self.ref,
ref=self._ref,
commit=self.commit,
)

Expand All @@ -166,7 +173,7 @@ def get_basedir(self):
path=os.path.dirname(self.path),
tag=self.tag,
commit=self.commit,
ref=self.ref,
ref=self._ref,
)

def is_persistently_cacheable(self):
Expand Down
2 changes: 1 addition & 1 deletion snakemake/utils.py
Expand Up @@ -61,7 +61,7 @@ def validate(data, schema, set_default=True):

if isinstance(schemafile, LocalSourceFile) and not schemafile.isabs() and workflow:
# if workflow object is not available this has not been started from a workflow
schemafile = workflow.current_basedir.join(schemafile)
schemafile = workflow.current_basedir.join(schemafile.get_path_or_uri())

source = (
workflow.sourcecache.open(schemafile)
Expand Down
1 change: 1 addition & 0 deletions snakemake/workflow.py
Expand Up @@ -257,6 +257,7 @@ def __init__(
_globals["gather"] = Gather()
_globals["github"] = sourcecache.GithubFile
_globals["gitlab"] = sourcecache.GitlabFile
_globals["gitfile"] = sourcecache.LocalGitFile

self.vanilla_globals = dict(_globals)
self.modifier_stack = [WorkflowModifier(self, globals=_globals)]
Expand Down
7 changes: 7 additions & 0 deletions tests/common.py
Expand Up @@ -16,6 +16,7 @@
import pytest
import glob
import subprocess
import tarfile

from snakemake import snakemake
from snakemake.shell import shell
Expand Down Expand Up @@ -92,6 +93,12 @@ def get_expected_files(results_dir):
]


def untar_folder(tar_file, output_path):
if not os.path.isdir(output_path):
with tarfile.open(tar_file) as tar:
tar.extractall(path=output_path)


def run(
path,
shouldfail=False,
Expand Down
21 changes: 21 additions & 0 deletions tests/test_module_local_git/Snakefile
@@ -0,0 +1,21 @@
from snakemake.utils import min_version

min_version("6.1.1")

configfile: "config.yaml"

import os

module_path = os.path.join(os.getcwd(), "repo/module")

module local_git_module:
snakefile:
gitfile(module_path, path="workflow/Snakefile", tag="v0.0.1")
config:
config

use rule * from local_git_module as local_git_module_*

rule all:
input:
"test.txt"
20 changes: 20 additions & 0 deletions tests/test_module_local_git/Snakefile_main_missing_rule_and_schema
@@ -0,0 +1,20 @@
from snakemake.utils import min_version
import os

min_version("6.1.1")

configfile: "config.yaml"

module_path = os.path.join(os.getcwd(), "repo/module")

module local_git_module:
snakefile:
gitfile(module_path, path="workflow/Snakefile", tag="main")
config:
config

use rule * from local_git_module as local_git_module_*

rule all:
input:
"test.txt"
20 changes: 20 additions & 0 deletions tests/test_module_local_git/Snakefile_missing_rule
@@ -0,0 +1,20 @@
from snakemake.utils import min_version
import os

min_version("6.1.1")

configfile: "config.yaml"

module_path = os.path.join(os.getcwd(), "repo/module")

module local_git_module:
snakefile:
gitfile(module_path, path="workflow/Snakefile", tag="e0.0.1")
config:
config

use rule * from local_git_module as local_git_module_*

rule all:
input:
"test.txt"
20 changes: 20 additions & 0 deletions tests/test_module_local_git/Snakefile_missing_schema
@@ -0,0 +1,20 @@
from snakemake.utils import min_version
import os

min_version("6.1.1")

configfile: "config.yaml"

module_path = os.path.join(os.getcwd(), "repo/module")

module local_git_module:
snakefile:
gitfile(module_path, path="workflow/Snakefile", tag="e0.0.2")
config:
config

use rule * from local_git_module as local_git_module_*

rule all:
input:
"test.txt"
17 changes: 17 additions & 0 deletions tests/test_module_local_git/Snakefile_relative
@@ -0,0 +1,17 @@
from snakemake.utils import min_version

min_version("6.1.1")

configfile: "config.yaml"

module local_git_module:
snakefile:
gitfile("repo/module", path="workflow/Snakefile", tag="v0.0.1")
config:
config

use rule * from local_git_module as local_git_module_*

rule all:
input:
"test.txt"
3 changes: 3 additions & 0 deletions tests/test_module_local_git/config.yaml
@@ -0,0 +1,3 @@
samples: samples.tsv

units: units.tsv
Empty file.
Empty file.
Empty file.
Binary file added tests/test_module_local_git/module.tar.gz
Binary file not shown.
2 changes: 2 additions & 0 deletions tests/test_module_local_git/samples.tsv
@@ -0,0 +1,2 @@
sample_name alias group platform purity
a a a ILLUMINA
2 changes: 2 additions & 0 deletions tests/test_module_local_git/units.tsv
@@ -0,0 +1,2 @@
sample_name unit_name fq1 fq2 sra adapters
a a data/a.1.fq data/a.2.fq
60 changes: 60 additions & 0 deletions tests/tests.py
Expand Up @@ -1667,6 +1667,66 @@ def test_module_complex2():
run(dpath("test_module_complex2"), dryrun=True)


@skip_on_windows
def test_module_use_local_git_repo():
untar_folder(
dpath("test_module_local_git/module.tar.gz"),
dpath("test_module_local_git/repo/module"),
)
run(dpath("test_module_local_git"), dryrun=True)


@skip_on_windows
def test_module_use_local_relative_git_repo():
untar_folder(
dpath("test_module_local_git/module.tar.gz"),
dpath("test_module_local_git/repo/module"),
)
run(dpath("test_module_local_git"), snakefile="Snakefile_relative", dryrun=True)


@skip_on_windows
def test_module_use_local_git_repo_missing_rule():
untar_folder(
dpath("test_module_local_git/module.tar.gz"),
dpath("test_module_local_git/repo/module"),
)
run(
dpath("test_module_local_git"),
snakefile="Snakefile_missing_rule",
dryrun=True,
shouldfail=True,
)


@skip_on_windows
def test_module_use_local_git_repo_missing_schema():
untar_folder(
dpath("test_module_local_git/module.tar.gz"),
dpath("test_module_local_git/repo/module"),
)
run(
dpath("test_module_local_git"),
snakefile="Snakefile_missing_schema",
dryrun=True,
shouldfail=True,
)


@skip_on_windows
def test_module_use_local_git_repo_missing_rule_and_schema():
untar_folder(
dpath("test_module_local_git/module.tar.gz"),
dpath("test_module_local_git/repo/module"),
)
run(
dpath("test_module_local_git"),
snakefile="Snakefile_main_missing_rule_and_schema",
dryrun=True,
shouldfail=True,
)


@skip_on_windows
def test_module_no_prefixing_modified_paths():
run(
Expand Down

0 comments on commit 1a3b91f

Please sign in to comment.