Skip to content

Commit 66600c4

Browse files
fix: cache conda envs to fix performance regression introduced in #1300 (#3093)
<!--Add a description of your PR here--> ### QC <!-- Make sure that you can tick the boxes below. --> * [x] The PR contains a test case for the changes or the changes are already covered by an existing test case. * [x] The documentation (`docs/`) is updated to reflect the changes or this is not necessary (e.g. if the change does neither modify the language nor the behavior or functionalities of Snakemake). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a caching mechanism for the conda environment expansion, improving performance by reducing redundant computations. - **Bug Fixes** - Enhanced logic for determining cacheability of the conda environment, ensuring accurate evaluations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent e9ce4a3 commit 66600c4

File tree

1 file changed

+23
-6
lines changed

1 file changed

+23
-6
lines changed

snakemake/rules.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
_IOFile,
2626
Namedlist,
2727
AnnotatedString,
28+
contains_wildcard,
2829
contains_wildcard_constraints,
2930
update_wildcard_constraints,
3031
flag,
@@ -66,6 +67,9 @@
6667
from snakemake_interface_common.rules import RuleInterface
6768

6869

70+
_NOT_CACHED = object()
71+
72+
6973
class Rule(RuleInterface):
7074
def __init__(self, name, workflow, lineno=None, snakefile=None):
7175
"""
@@ -92,6 +96,7 @@ def __init__(self, name, workflow, lineno=None, snakefile=None):
9296
self._log = Log()
9397
self._benchmark = None
9498
self._conda_env = None
99+
self._expanded_conda_env = _NOT_CACHED
95100
self._container_img = None
96101
self.is_containerized = False
97102
self.env_modules = None
@@ -1061,6 +1066,9 @@ def expand_group(self, wildcards):
10611066
return self.group
10621067

10631068
def expand_conda_env(self, wildcards, params=None, input=None):
1069+
if self._expanded_conda_env is not _NOT_CACHED:
1070+
return self._expanded_conda_env
1071+
10641072
from snakemake.common import is_local_file
10651073
from snakemake.sourcecache import SourceFile, infer_source_file
10661074
from snakemake.deployment.conda import (
@@ -1070,12 +1078,18 @@ def expand_conda_env(self, wildcards, params=None, input=None):
10701078
)
10711079

10721080
conda_env = self._conda_env
1073-
if callable(conda_env):
1074-
conda_env, _ = self.apply_input_function(
1075-
conda_env, wildcards=wildcards, params=params, input=input
1076-
)
1077-
1078-
if conda_env is None:
1081+
if conda_env is not None:
1082+
if not callable(conda_env):
1083+
cacheable = not contains_wildcard(conda_env)
1084+
else:
1085+
conda_env, _ = self.apply_input_function(
1086+
conda_env, wildcards=wildcards, params=params, input=input
1087+
)
1088+
cacheable = False
1089+
if conda_env is None:
1090+
return None
1091+
else:
1092+
self._expanded_conda_env = None
10791093
return None
10801094

10811095
if is_conda_env_file(conda_env):
@@ -1096,6 +1110,9 @@ def expand_conda_env(self, wildcards, params=None, input=None):
10961110
conda_env = conda_env.apply_wildcards(wildcards, self)
10971111
conda_env.check()
10981112

1113+
if cacheable:
1114+
self._expanded_conda_env = conda_env
1115+
10991116
return conda_env
11001117

11011118
def is_producer(self, requested_output):

0 commit comments

Comments
 (0)