in lib/ramble/spack/spec.py [0:0]
def format(self, format_string=default_format, **kwargs):
r"""Prints out particular pieces of a spec, depending on what is
in the format string.
Using the ``{attribute}`` syntax, any field of the spec can be
selected. Those attributes can be recursive. For example,
``s.format({compiler.version})`` will print the version of the
compiler.
Commonly used attributes of the Spec for format strings include::
name
version
compiler
compiler.name
compiler.version
compiler_flags
variants
architecture
architecture.platform
architecture.os
architecture.target
prefix
Some additional special-case properties can be added::
hash[:len] The DAG hash with optional length argument
spack_root The spack root directory
spack_install The spack install directory
The ``^`` sigil can be used to access dependencies by name.
``s.format({^mpi.name})`` will print the name of the MPI
implementation in the spec.
The ``@``, ``%``, ``arch=``, and ``/`` sigils
can be used to include the sigil with the printed
string. These sigils may only be used with the appropriate
attributes, listed below::
@ ``{@version}``, ``{@compiler.version}``
% ``{%compiler}``, ``{%compiler.name}``
arch= ``{arch=architecture}``
/ ``{/hash}``, ``{/hash:7}``, etc
The ``@`` sigil may also be used for any other property named
``version``. Sigils printed with the attribute string are only
printed if the attribute string is non-empty, and are colored
according to the color of the attribute.
Sigils are not used for printing variants. Variants listed by
name naturally print with their sigil. For example,
``spec.format('{variants.debug}')`` would print either
``+debug`` or ``~debug`` depending on the name of the
variant. Non-boolean variants print as ``name=value``. To
print variant names or values independently, use
``spec.format('{variants.<name>.name}')`` or
``spec.format('{variants.<name>.value}')``.
Spec format strings use ``\`` as the escape character. Use
``\{`` and ``\}`` for literal braces, and ``\\`` for the
literal ``\`` character. Also use ``\$`` for the literal ``$``
to differentiate from previous, deprecated format string
syntax.
The previous format strings are deprecated. They can still be
accessed by the ``old_format`` method. The ``format`` method
will call ``old_format`` if the character ``$`` appears
unescaped in the format string.
Args:
format_string (str): string containing the format to be expanded
Keyword Args:
color (bool): True if returned string is colored
transform (dict): maps full-string formats to a callable \
that accepts a string and returns another one
"""
# If we have an unescaped $ sigil, use the deprecated format strings
if re.search(r'[^\\]*\$', format_string):
return self.old_format(format_string, **kwargs)
color = kwargs.get('color', False)
transform = kwargs.get('transform', {})
out = io.StringIO()
def write(s, c=None):
f = clr.cescape(s)
if c is not None:
f = color_formats[c] + f + '@.'
clr.cwrite(f, stream=out, color=color)
def write_attribute(spec, attribute, color):
current = spec
if attribute.startswith('^'):
attribute = attribute[1:]
dep, attribute = attribute.split('.', 1)
current = self[dep]
if attribute == '':
raise SpecFormatStringError(
'Format string attributes must be non-empty')
attribute = attribute.lower()
sig = ''
if attribute[0] in '@%/':
# color sigils that are inside braces
sig = attribute[0]
attribute = attribute[1:]
elif attribute.startswith('arch='):
sig = ' arch=' # include space as separator
attribute = attribute[5:]
parts = attribute.split('.')
assert parts
# check that the sigil is valid for the attribute.
if sig == '@' and parts[-1] not in ('versions', 'version'):
raise SpecFormatSigilError(sig, 'versions', attribute)
elif sig == '%' and attribute not in ('compiler', 'compiler.name'):
raise SpecFormatSigilError(sig, 'compilers', attribute)
elif sig == '/' and not re.match(r'hash(:\d+)?$', attribute):
raise SpecFormatSigilError(sig, 'DAG hashes', attribute)
elif sig == ' arch=' and attribute not in ('architecture', 'arch'):
raise SpecFormatSigilError(sig, 'the architecture', attribute)
# find the morph function for our attribute
morph = transform.get(attribute, lambda s, x: x)
# Special cases for non-spec attributes and hashes.
# These must be the only non-dep component of the format attribute
if attribute == 'spack_root':
write(morph(spec, spack.paths.spack_root))
return
elif attribute == 'spack_install':
write(morph(spec, spack.store.layout.root))
return
elif re.match(r'hash(:\d)?', attribute):
col = '#'
if ':' in attribute:
_, length = attribute.split(':')
write(sig + morph(spec, spec.dag_hash(int(length))), col)
else:
write(sig + morph(spec, spec.dag_hash()), col)
return
# Iterate over components using getattr to get next element
for idx, part in enumerate(parts):
if not part:
raise SpecFormatStringError(
'Format string attributes must be non-empty'
)
if part.startswith('_'):
raise SpecFormatStringError(
'Attempted to format private attribute'
)
else:
if isinstance(current, vt.VariantMap):
# subscript instead of getattr for variant names
current = current[part]
else:
# aliases
if part == 'arch':
part = 'architecture'
elif part == 'version':
# Version requires concrete spec, versions does not
# when concrete, they print the same thing
part = 'versions'
try:
current = getattr(current, part)
except AttributeError:
parent = '.'.join(parts[:idx])
m = 'Attempted to format attribute %s.' % attribute
m += 'Spec.%s has no attribute %s' % (parent, part)
raise SpecFormatStringError(m)
if isinstance(current, vn.VersionList):
if current == _any_version:
# We don't print empty version lists
return
if callable(current):
raise SpecFormatStringError(
'Attempted to format callable object'
)
if not current:
# We're not printing anything
return
# Set color codes for various attributes
col = None
if 'variants' in parts:
col = '+'
elif 'architecture' in parts:
col = '='
elif 'compiler' in parts or 'compiler_flags' in parts:
col = '%'
elif 'version' in parts:
col = '@'
# Finally, write the output
write(sig + morph(spec, str(current)), col)
attribute = ''
in_attribute = False
escape = False
for c in format_string:
if escape:
out.write(c)
escape = False
elif c == '\\':
escape = True
elif in_attribute:
if c == '}':
write_attribute(self, attribute, color)
attribute = ''
in_attribute = False
else:
attribute += c
else:
if c == '}':
raise SpecFormatStringError(
'Encountered closing } before opening {'
)
elif c == '{':
in_attribute = True
else:
out.write(c)
if in_attribute:
raise SpecFormatStringError(
'Format string terminated while reading attribute.'
'Missing terminating }.'
)
formatted_spec = out.getvalue()
return formatted_spec.strip()