@@ -765,9 +765,13 @@ def count_files(self):
765765 """
766766 Determine number of files (sources + patches) required for this easyconfig.
767767 """
768- cnt = len (self ['sources' ]) + len (self ['patches' ])
769768
770- for ext in self ['exts_list' ]:
769+ # No need to resolve templates as we only need a count not the names
770+ with self .disable_templating ():
771+ cnt = len (self ['sources' ]) + len (self ['patches' ])
772+ exts = self ['exts_list' ]
773+
774+ for ext in exts :
771775 if isinstance (ext , tuple ) and len (ext ) >= 3 :
772776 ext_opts = ext [2 ]
773777 # check for 'sources' first, since that's also considered first by EasyBlock.collect_exts_file_info
@@ -1653,8 +1657,9 @@ def _finalize_dependencies(self):
16531657 filter_deps_specs = self .parse_filter_deps ()
16541658
16551659 for key in DEPENDENCY_PARAMETERS :
1656- # loop over a *copy* of dependency dicts (with resolved templates);
1657- deps = self [key ]
1660+ # loop over a *copy* of dependency dicts with resolved templates,
1661+ # although some templates may not resolve yet (e.g. those relying on dependencies like %(pyver)s)
1662+ deps = resolve_template (self .get_ref (key ), self .template_values , expect_resolved = False )
16581663
16591664 # to update the original dep dict, we need to get a reference with templating disabled...
16601665 deps_ref = self .get_ref (key )
@@ -1826,11 +1831,14 @@ def get(self, key, default=None, resolve=True):
18261831 # see also https://docs.python.org/2/reference/datamodel.html#object.__eq__
18271832 def __eq__ (self , ec ):
18281833 """Is this EasyConfig instance equivalent to the provided one?"""
1829- return self .asdict () == ec .asdict ()
1834+ # Compare raw values to check that templates used are the same
1835+ with self .disable_templating ():
1836+ with ec .disable_templating ():
1837+ return self .asdict () == ec .asdict ()
18301838
18311839 def __ne__ (self , ec ):
18321840 """Is this EasyConfig instance equivalent to the provided one?"""
1833- return self . asdict () != ec . asdict ()
1841+ return not self == ec
18341842
18351843 def __hash__ (self ):
18361844 """Return hash value for a hashable representation of this EasyConfig instance."""
@@ -1843,8 +1851,9 @@ def make_hashable(val):
18431851 return val
18441852
18451853 lst = []
1846- for (key , val ) in sorted (self .asdict ().items ()):
1847- lst .append ((key , make_hashable (val )))
1854+ with self .disable_templating ():
1855+ for (key , val ) in sorted (self .asdict ().items ()):
1856+ lst .append ((key , make_hashable (val )))
18481857
18491858 # a list is not hashable, but a tuple is
18501859 return hash (tuple (lst ))
@@ -1859,7 +1868,8 @@ def asdict(self):
18591868 if self .enable_templating :
18601869 if not self .template_values :
18611870 self .generate_template_values ()
1862- value = resolve_template (value , self .template_values )
1871+ # Not all values can be resolved, e.g. %(installdir)s
1872+ value = resolve_template (value , self .template_values , expect_resolved = False )
18631873 res [key ] = value
18641874 return res
18651875
@@ -2007,10 +2017,11 @@ def get_module_path(name, generic=None, decode=True):
20072017 return '.' .join (modpath + [module_name ])
20082018
20092019
2010- def resolve_template (value , tmpl_dict ):
2020+ def resolve_template (value , tmpl_dict , expect_resolved = True ):
20112021 """Given a value, try to susbstitute the templated strings with actual values.
20122022 - value: some python object (supported are string, tuple/list, dict or some mix thereof)
20132023 - tmpl_dict: template dictionary
2024+ - expect_resolved: Expects that all templates get resolved
20142025 """
20152026 if isinstance (value , str ):
20162027 # simple escaping, making all '%foo', '%%foo', '%%%foo' post-templates values available,
@@ -2063,7 +2074,14 @@ def resolve_template(value, tmpl_dict):
20632074 _log .deprecated (f"Easyconfig template '{ old_tmpl } ' is deprecated, use '{ new_tmpl } ' instead" ,
20642075 ver )
20652076 except KeyError :
2066- _log .warning (f"Unable to resolve template value { value } with dict { tmpl_dict } " )
2077+ if expect_resolved :
2078+ msg = (f'Failed to resolve all templates in "{ value } " using template dictionary: { tmpl_dict } . '
2079+ 'This might cause failures or unexpected behavior, '
2080+ 'check for correct escaping if this is intended!' )
2081+ if build_option ('allow_unresolved_templates' ):
2082+ print_warning (msg )
2083+ else :
2084+ raise EasyBuildError (msg )
20672085 value = raw_value # Undo "%"-escaping
20682086
20692087 for key in tmpl_dict :
@@ -2084,11 +2102,12 @@ def resolve_template(value, tmpl_dict):
20842102 # self._config['x']['y'] = z
20852103 # it can not be intercepted with __setitem__ because the set is done at a deeper level
20862104 if isinstance (value , list ):
2087- value = [resolve_template (val , tmpl_dict ) for val in value ]
2105+ value = [resolve_template (val , tmpl_dict , expect_resolved ) for val in value ]
20882106 elif isinstance (value , tuple ):
2089- value = tuple (resolve_template (list (value ), tmpl_dict ))
2107+ value = tuple (resolve_template (list (value ), tmpl_dict , expect_resolved ))
20902108 elif isinstance (value , dict ):
2091- value = {resolve_template (k , tmpl_dict ): resolve_template (v , tmpl_dict ) for k , v in value .items ()}
2109+ value = {resolve_template (k , tmpl_dict , expect_resolved ): resolve_template (v , tmpl_dict , expect_resolved )
2110+ for k , v in value .items ()}
20922111
20932112 return value
20942113
0 commit comments