Skip to content

Commit 9784237

Browse files
authored
Merge pull request #3547 from Flamefire/use-resolve-context-manager
Use context managers for templating changes in `Bundle` easyblock
2 parents 0b7b6b5 + 6a26673 commit 9784237

File tree

1 file changed

+87
-93
lines changed

1 file changed

+87
-93
lines changed

easybuild/easyblocks/generic/bundle.py

Lines changed: 87 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -91,89 +91,88 @@ def __init__(self, *args, **kwargs):
9191
self.cfg = self.cfg.copy()
9292

9393
# disable templating to avoid premature resolving of template values
94-
self.cfg.enable_templating = False
95-
96-
# list of checksums for patches (must be included after checksums for sources)
97-
checksums_patches = []
98-
99-
if self.cfg['sanity_check_components'] and self.cfg['sanity_check_all_components']:
100-
raise EasyBuildError("sanity_check_components and sanity_check_all_components cannot be enabled together")
101-
102-
# backup and reset general sanity checks from main body of ec, if component-specific sanity checks are enabled
103-
# necessary to avoid:
104-
# - duplicating the general sanity check across all components running sanity checks
105-
# - general sanity checks taking precedence over those defined in a component's easyblock
106-
self.backup_sanity_paths = self.cfg['sanity_check_paths']
107-
self.backup_sanity_cmds = self.cfg['sanity_check_commands']
108-
if self.cfg['sanity_check_components'] or self.cfg['sanity_check_all_components']:
109-
# reset general sanity checks, to be restored later
110-
self.cfg['sanity_check_paths'] = {}
111-
self.cfg['sanity_check_commands'] = {}
112-
113-
for comp in self.cfg['components']:
114-
comp_name, comp_version, comp_specs = comp[0], comp[1], {}
115-
if len(comp) == 3:
116-
comp_specs = comp[2]
117-
118-
comp_cfg = self.cfg.copy()
119-
120-
comp_cfg['name'] = comp_name
121-
comp_cfg['version'] = comp_version
122-
123-
# determine easyblock to use for this component
124-
# - if an easyblock is specified explicitely, that will be used
125-
# - if not, a software-specific easyblock will be considered by get_easyblock_class
126-
# - if no easyblock was found, default_easyblock is considered
127-
comp_easyblock = comp_specs.get('easyblock')
128-
easyblock_class = get_easyblock_class(comp_easyblock, name=comp_name, error_on_missing_easyblock=False)
129-
if easyblock_class is None:
130-
if self.cfg['default_easyblock']:
131-
easyblock = self.cfg['default_easyblock']
132-
easyblock_class = get_easyblock_class(easyblock)
133-
94+
# Note that self.cfg.update also resolves templates!
95+
with self.cfg.disable_templating():
96+
# list of checksums for patches (must be included after checksums for sources)
97+
checksums_patches = []
98+
99+
if self.cfg['sanity_check_components'] and self.cfg['sanity_check_all_components']:
100+
raise EasyBuildError("sanity_check_components and sanity_check_all_components"
101+
"cannot be enabled together")
102+
103+
# backup and reset general sanity checks from main body of ec,
104+
# if component-specific sanity checks are enabled necessary to avoid:
105+
# - duplicating the general sanity check across all components running sanity checks
106+
# - general sanity checks taking precedence over those defined in a component's easyblock
107+
self.backup_sanity_paths = self.cfg['sanity_check_paths']
108+
self.backup_sanity_cmds = self.cfg['sanity_check_commands']
109+
if self.cfg['sanity_check_components'] or self.cfg['sanity_check_all_components']:
110+
# reset general sanity checks, to be restored later
111+
self.cfg['sanity_check_paths'] = {}
112+
self.cfg['sanity_check_commands'] = {}
113+
components = self.cfg['components']
114+
115+
for comp in components:
116+
comp_name, comp_version, comp_specs = comp[0], comp[1], {}
117+
if len(comp) == 3:
118+
comp_specs = comp[2]
119+
120+
comp_cfg = self.cfg.copy()
121+
122+
comp_cfg['name'] = comp_name
123+
comp_cfg['version'] = comp_version
124+
125+
# determine easyblock to use for this component
126+
# - if an easyblock is specified explicitely, that will be used
127+
# - if not, a software-specific easyblock will be considered by get_easyblock_class
128+
# - if no easyblock was found, default_easyblock is considered
129+
comp_easyblock = comp_specs.get('easyblock')
130+
easyblock_class = get_easyblock_class(comp_easyblock, name=comp_name, error_on_missing_easyblock=False)
134131
if easyblock_class is None:
135-
raise EasyBuildError("No easyblock found for component %s v%s", comp_name, comp_version)
136-
else:
137-
self.log.info("Using default easyblock %s for component %s", easyblock, comp_name)
138-
else:
139-
easyblock = easyblock_class.__name__
140-
self.log.info("Using easyblock %s for component %s", easyblock, comp_name)
132+
if self.cfg['default_easyblock']:
133+
easyblock = self.cfg['default_easyblock']
134+
easyblock_class = get_easyblock_class(easyblock)
141135

142-
if easyblock == 'Bundle':
143-
raise EasyBuildError("The Bundle easyblock can not be used to install components in a bundle")
136+
if easyblock_class is None:
137+
raise EasyBuildError("No easyblock found for component %s v%s", comp_name, comp_version)
138+
self.log.info("Using default easyblock %s for component %s", easyblock, comp_name)
139+
else:
140+
easyblock = easyblock_class.__name__
141+
self.log.info("Using easyblock %s for component %s", easyblock, comp_name)
144142

145-
comp_cfg.easyblock = easyblock_class
143+
if easyblock == 'Bundle':
144+
raise EasyBuildError("The Bundle easyblock can not be used to install components in a bundle")
146145

147-
# make sure that extra easyconfig parameters are known, so they can be set
148-
extra_opts = comp_cfg.easyblock.extra_options()
149-
comp_cfg.extend_params(copy.deepcopy(extra_opts))
146+
comp_cfg.easyblock = easyblock_class
150147

151-
comp_cfg.generate_template_values()
148+
# make sure that extra easyconfig parameters are known, so they can be set
149+
extra_opts = comp_cfg.easyblock.extra_options()
150+
comp_cfg.extend_params(copy.deepcopy(extra_opts))
152151

153-
# do not inherit easyblock to use from parent (since that would result in an infinite loop in install_step)
154-
comp_cfg['easyblock'] = None
152+
comp_cfg.generate_template_values()
155153

156-
# reset list of sources/source_urls/checksums
157-
comp_cfg['sources'] = comp_cfg['source_urls'] = comp_cfg['checksums'] = comp_cfg['patches'] = []
154+
# do not inherit easyblock to use from parent
155+
# (since that would result in an infinite loop in install_step)
156+
comp_cfg['easyblock'] = None
158157

159-
for key in self.cfg['default_component_specs']:
160-
comp_cfg[key] = self.cfg['default_component_specs'][key]
158+
# reset list of sources/source_urls/checksums
159+
comp_cfg['sources'] = comp_cfg['source_urls'] = comp_cfg['checksums'] = comp_cfg['patches'] = []
161160

162-
for key in comp_specs:
163-
comp_cfg[key] = comp_specs[key]
161+
for key in self.cfg['default_component_specs']:
162+
comp_cfg[key] = self.cfg['default_component_specs'][key]
164163

165-
# enable resolving of templates for component-specific EasyConfig instance,
166-
# but don't require that all template values can be resolved at this point;
167-
# this is important to ensure that template values like %(name)s and %(version)s
168-
# are correctly resolved with the component name/version before values are copied over to self.cfg
169-
comp_cfg.enable_templating = True
170-
comp_cfg.expect_resolved_template_values = False
164+
for key in comp_specs:
165+
comp_cfg[key] = comp_specs[key]
171166

172-
# 'sources' is strictly required
173-
comp_sources = comp_cfg['sources']
174-
if comp_sources:
167+
# Don't require that all template values can be resolved at this point but still resolve them.
168+
# This is important to ensure that template values like %(name)s and %(version)s
169+
# are correctly resolved with the component name/version before values are copied over to self.cfg
170+
with comp_cfg.allow_unresolved_templates():
171+
comp_sources = comp_cfg['sources']
172+
comp_source_urls = comp_cfg['source_urls']
173+
if not comp_sources:
174+
raise EasyBuildError("No sources specification for component %s v%s", comp_name, comp_version)
175175
# If per-component source URLs are provided, attach them directly to the relevant sources
176-
comp_source_urls = comp_cfg['source_urls']
177176
if comp_source_urls:
178177
for source in comp_sources:
179178
if isinstance(source, str):
@@ -190,36 +189,31 @@ def __init__(self, *args, **kwargs):
190189
else:
191190
# add component sources to list of sources
192191
self.cfg.update('sources', comp_sources)
193-
else:
194-
raise EasyBuildError("No sources specification for component %s v%s", comp_name, comp_version)
195-
196-
comp_checksums = comp_cfg['checksums']
197-
if comp_checksums:
198-
src_cnt = len(comp_sources)
199192

200-
# add per-component checksums for sources to list of checksums
201-
self.cfg.update('checksums', comp_checksums[:src_cnt])
193+
comp_checksums = comp_cfg['checksums']
194+
if comp_checksums:
195+
src_cnt = len(comp_sources)
202196

203-
# add per-component checksums for patches to list of checksums for patches
204-
checksums_patches.extend(comp_checksums[src_cnt:])
197+
# add per-component checksums for sources to list of checksums
198+
self.cfg.update('checksums', comp_checksums[:src_cnt])
205199

206-
comp_patches = comp_cfg['patches']
207-
if comp_patches:
208-
self.cfg.update('patches', comp_patches)
200+
# add per-component checksums for patches to list of checksums for patches
201+
checksums_patches.extend(comp_checksums[src_cnt:])
209202

210-
comp_cfg.expect_resolved_template_values = True
203+
with comp_cfg.allow_unresolved_templates():
204+
comp_patches = comp_cfg['patches']
205+
if comp_patches:
206+
self.cfg.update('patches', comp_patches)
211207

212-
self.comp_instances.append((comp_cfg, comp_cfg.easyblock(comp_cfg, logfile=self.logfile)))
208+
self.comp_instances.append((comp_cfg, comp_cfg.easyblock(comp_cfg, logfile=self.logfile)))
213209

214-
self.cfg.update('checksums', checksums_patches)
210+
self.cfg.update('checksums', checksums_patches)
215211

216212
# restore general sanity checks if using component-specific sanity checks
217213
if self.cfg['sanity_check_components'] or self.cfg['sanity_check_all_components']:
218214
self.cfg['sanity_check_paths'] = self.backup_sanity_paths
219215
self.cfg['sanity_check_commands'] = self.backup_sanity_cmds
220216

221-
self.cfg.enable_templating = True
222-
223217
def check_checksums(self):
224218
"""
225219
Check whether a SHA256 checksum is available for all sources & patches (incl. extensions).
@@ -279,9 +273,10 @@ def install_step(self):
279273

280274
comp.src = []
281275

282-
# find match entries in self.src for this component
283-
comp.cfg.expect_resolved_template_values = False
284-
for source in comp.cfg['sources']:
276+
# find matching entries in self.src for this component
277+
with comp.cfg.allow_unresolved_templates():
278+
comp_sources = comp.cfg['sources']
279+
for source in comp_sources:
285280
if isinstance(source, str):
286281
comp_src_fn = source
287282
elif isinstance(source, dict):
@@ -304,7 +299,6 @@ def install_step(self):
304299

305300
# location of first unpacked source is used to determine where to apply patch(es)
306301
comp.src[-1]['finalpath'] = comp.cfg['start_dir']
307-
comp.cfg.expect_resolved_template_values = True
308302

309303
# check if sanity checks are enabled for the component
310304
if self.cfg['sanity_check_all_components'] or comp.cfg['name'] in self.cfg['sanity_check_components']:

0 commit comments

Comments
 (0)