Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: env file usage after changes to source file handling (inspired by #1233 and #1211). #1236

Merged
merged 4 commits into from Oct 26, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions snakemake/common/__init__.py
Expand Up @@ -25,6 +25,9 @@
DYNAMIC_FILL = "__snakemake_dynamic__"
SNAKEMAKE_SEARCHPATH = str(Path(__file__).parent.parent.parent)
UUID_NAMESPACE = uuid.uuid5(uuid.NAMESPACE_URL, "https://snakemake.readthedocs.io")
NOTHING_TO_BE_DONE_MSG = (
"Nothing to be done (all requested files are present and up to date)."
)

ON_WINDOWS = platform.system() == "Windows"

Expand Down
16 changes: 11 additions & 5 deletions snakemake/deployment/containerize.py
Expand Up @@ -7,6 +7,7 @@
from snakemake.exceptions import WorkflowError
from snakemake.deployment import conda
from snakemake.logging import logger
from snakemake.sourcecache import LocalSourceFile


CONDA_ENV_PATH = "/conda-envs"
Expand All @@ -22,7 +23,11 @@ def containerize(workflow):
"Containerization of conda based workflows is not allowed if any conda env definition contains a wildcard."
)

relfile = lambda env: os.path.relpath(env.file, os.getcwd())
def relfile(env):
if isinstance(env.file, LocalSourceFile):
return os.path.relpath(env.file.get_path_or_uri(), os.getcwd())
else:
return env.file.get_path_or_uri()

envs = sorted(
set(
Expand Down Expand Up @@ -59,11 +64,12 @@ def containerize(workflow):
"\n".join(map("# {}".format, env.content.decode().strip().split("\n")))
)
get_env_cmds.append("RUN mkdir -p {}".format(prefix))
if env.file.startswith("https://"):
# get_env_cmds.append("RUN curl -sSL {} > {}".format(env.file, env_target_path))
get_env_cmds.append("ADD {} {}".format(env.file, env_target_path))
else:
if isinstance(env.file, LocalSourceFile):
get_env_cmds.append("COPY {} {}".format(env_source_path, env_target_path))
else:
get_env_cmds.append(
"ADD {} {}".format(env.file.get_path_or_uri(), env_target_path)
)

generate_env_cmds.append(
"mamba env create --prefix {} --file {} &&".format(prefix, env_target_path)
Expand Down
3 changes: 3 additions & 0 deletions snakemake/sourcecache.py
Expand Up @@ -105,6 +105,9 @@ def isabs(self):
def simplify_path(self):
return utils.simplify_path(self.path)

def __fspath__(self):
return self.path


class LocalGitFile(SourceFile):
def __init__(
Expand Down
11 changes: 7 additions & 4 deletions snakemake/workflow.py
Expand Up @@ -73,6 +73,7 @@
Scatter,
Gather,
smart_join,
NOTHING_TO_BE_DONE_MSG,
)
from snakemake.utils import simplify_path
from snakemake.checkpoints import Checkpoint, Checkpoints
Expand Down Expand Up @@ -820,7 +821,9 @@ def files(items):
)
else:
logger.info(
"Subworkflow {}: Nothing to be done.".format(subworkflow.name)
"Subworkflow {}: {}".format(
subworkflow.name, NOTHING_TO_BE_DONE_MSG
)
)
if self.subworkflows:
logger.info("Executing main workflow.")
Expand Down Expand Up @@ -955,7 +958,7 @@ def files(items):
for env in set(job.conda_env for job in dag.jobs):
if env:
print(
simplify_path(env.file),
env.file.simplify_path(),
env.container_img_url or "",
simplify_path(env.path),
sep="\t",
Expand Down Expand Up @@ -1051,13 +1054,13 @@ def files(items):
if self.mode == Mode.default:
logger.run_info("\n".join(dag.stats()))
else:
logger.info("Nothing to be done.")
logger.info(NOTHING_TO_BE_DONE_MSG)
else:
# the dryrun case
if len(dag):
logger.run_info("\n".join(dag.stats()))
else:
logger.info("Nothing to be done.")
logger.info(NOTHING_TO_BE_DONE_MSG)
return True
if quiet:
# in case of dryrun and quiet, just print above info and exit
Expand Down
74 changes: 38 additions & 36 deletions tests/common.py
Expand Up @@ -88,6 +88,7 @@ def run(
subpath=None,
no_tmpdir=False,
check_md5=True,
check_results=True,
cores=3,
set_pythonpath=True,
cleanup=True,
Expand Down Expand Up @@ -181,42 +182,43 @@ def run(
assert not success, "expected error on execution"
else:
assert success, "expected successful execution"
for resultfile in get_expected_files(results_dir):
if resultfile in [".gitignore", ".gitkeep"] or not os.path.isfile(
os.path.join(results_dir, resultfile)
):
# this means tests cannot use directories as output files
continue
targetfile = join(tmpdir, resultfile)
expectedfile = join(results_dir, resultfile)

if ON_WINDOWS:
if os.path.exists(join(results_dir, resultfile + "_WIN")):
continue # Skip test if a Windows specific file exists
if resultfile.endswith("_WIN"):
targetfile = join(tmpdir, resultfile[:-4])
elif resultfile.endswith("_WIN"):
# Skip win specific result files on Posix platforms
continue

assert os.path.exists(targetfile), 'expected file "{}" not produced'.format(
resultfile
)
if check_md5:
md5expected = md5sum(expectedfile, ignore_newlines=ON_WINDOWS)
md5target = md5sum(targetfile, ignore_newlines=ON_WINDOWS)
if md5target != md5expected:
with open(expectedfile) as expected:
expected_content = expected.read()
with open(targetfile) as target:
content = target.read()
assert (
False
), "wrong result produced for file '{resultfile}':\n------found------\n{content}\n-----expected-----\n{expected_content}\n-----------------".format(
resultfile=resultfile,
content=content,
expected_content=expected_content,
)
if check_results:
for resultfile in get_expected_files(results_dir):
if resultfile in [".gitignore", ".gitkeep"] or not os.path.isfile(
os.path.join(results_dir, resultfile)
):
# this means tests cannot use directories as output files
continue
targetfile = join(tmpdir, resultfile)
expectedfile = join(results_dir, resultfile)

if ON_WINDOWS:
if os.path.exists(join(results_dir, resultfile + "_WIN")):
continue # Skip test if a Windows specific file exists
if resultfile.endswith("_WIN"):
targetfile = join(tmpdir, resultfile[:-4])
elif resultfile.endswith("_WIN"):
# Skip win specific result files on Posix platforms
continue

assert os.path.exists(
targetfile
), 'expected file "{}" not produced'.format(resultfile)
if check_md5:
md5expected = md5sum(expectedfile, ignore_newlines=ON_WINDOWS)
md5target = md5sum(targetfile, ignore_newlines=ON_WINDOWS)
if md5target != md5expected:
with open(expectedfile) as expected:
expected_content = expected.read()
with open(targetfile) as target:
content = target.read()
assert (
False
), "wrong result produced for file '{resultfile}':\n------found------\n{content}\n-----expected-----\n{expected_content}\n-----------------".format(
resultfile=resultfile,
content=content,
expected_content=expected_content,
)

if not cleanup:
return tmpdir
Expand Down
9 changes: 9 additions & 0 deletions tests/tests.py
Expand Up @@ -409,6 +409,10 @@ def test_conda():
run(dpath("test_conda"), use_conda=True)


def test_conda_list_envs():
run(dpath("test_conda"), list_conda_envs=True, check_results=False)


def test_upstream_conda():
run(dpath("test_conda"), use_conda=True, conda_frontend="conda")

Expand Down Expand Up @@ -1221,6 +1225,11 @@ def test_containerized():
run(dpath("test_containerized"), use_conda=True, use_singularity=True)


@skip_on_windows
def test_containerize():
run(dpath("test_conda"), containerize=True, check_results=False)


def test_long_shell():
run(dpath("test_long_shell"))

Expand Down