Skip to content
Snippets Groups Projects
Commit 22bb0562 authored by Massimiliano Culpo's avatar Massimiliano Culpo
Browse files

Revert "flake8 : fixed all issues?"

This reverts commit 71e49e28.
parent b215b19c
Branches
Tags
No related merge requests found
......@@ -32,21 +32,18 @@
from spack.modules import module_types
from spack.util.string import *
description = "Manipulate modules and dotkits."
description ="Manipulate modules and dotkits."
def setup_parser(subparser):
sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='module_command')
sp.add_parser('refresh', help='Regenerate all module files.')
refresh_parser = sp.add_parser('refresh', help='Regenerate all module files.')
find_parser = sp.add_parser('find', help='Find module files for packages.')
find_parser.add_argument('module_type',
help="Type of module to find file for. [" +
'|'.join(module_types) + "]")
find_parser.add_argument('spec',
nargs='+',
help='spec to find a module file for.')
find_parser.add_argument(
'module_type', help="Type of module to find file for. [" + '|'.join(module_types) + "]")
find_parser.add_argument('spec', nargs='+', help='spec to find a module file for.')
def module_find(mtype, spec_array):
......@@ -56,8 +53,7 @@ def module_find(mtype, spec_array):
should type to use that package's module.
"""
if mtype not in module_types:
tty.die("Invalid module type: '%s'. Options are %s" %
(mtype, comma_or(module_types)))
tty.die("Invalid module type: '%s'. Options are %s" % (mtype, comma_or(module_types)))
specs = spack.cmd.parse_specs(spec_array)
if len(specs) > 1:
......
......@@ -134,6 +134,8 @@
# Hacked yaml for configuration files preserves line numbers.
import spack.util.spack_yaml as syaml
"""Dict from section names -> schema for that section."""
section_schemas = {
'compilers': {
......@@ -147,31 +149,25 @@
'default': {},
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*': { # architecture
r'\w[\w-]*': { # architecture
'type': 'object',
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*@\w[\w-]*': { # compiler spec
r'\w[\w-]*@\w[\w-]*': { # compiler spec
'type': 'object',
'additionalProperties': False,
'required': ['cc', 'cxx', 'f77', 'fc'],
'properties': {
'cc': {'anyOf': [{'type': 'string'},
{'type': 'null'}]},
'cxx': {'anyOf': [{'type': 'string'},
{'type': 'null'}]},
'f77': {'anyOf': [{'type': 'string'},
{'type': 'null'}]},
'fc': {'anyOf': [{'type': 'string'},
{'type': 'null'}]},
},
},
},
},
},
},
},
},
'cc': { 'anyOf': [ {'type' : 'string' },
{'type' : 'null' }]},
'cxx': { 'anyOf': [ {'type' : 'string' },
{'type' : 'null' }]},
'f77': { 'anyOf': [ {'type' : 'string' },
{'type' : 'null' }]},
'fc': { 'anyOf': [ {'type' : 'string' },
{'type' : 'null' }]},
},},},},},},},},
'mirrors': {
'$schema': 'http://json-schema.org/schema#',
'title': 'Spack mirror configuration file schema',
......@@ -184,12 +180,8 @@
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*': {
'type': 'string'
},
},
},
},
},
'type': 'string'},},},},},
'repos': {
'$schema': 'http://json-schema.org/schema#',
'title': 'Spack repository configuration file schema',
......@@ -200,11 +192,8 @@
'type': 'array',
'default': [],
'items': {
'type': 'string'
},
},
},
},
'type': 'string'},},},},
'packages': {
'$schema': 'http://json-schema.org/schema#',
'title': 'Spack package configuration file schema',
......@@ -216,48 +205,39 @@
'default': {},
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*': { # package name
r'\w[\w-]*': { # package name
'type': 'object',
'default': {},
'additionalProperties': False,
'properties': {
'version': {
'type': 'array',
'default': [],
'items': {'anyOf': [{'type': 'string'},
{'type': 'number'}]}
}, # version strings
'type' : 'array',
'default' : [],
'items' : { 'anyOf' : [ { 'type' : 'string' },
{ 'type' : 'number'}]}}, #version strings
'compiler': {
'type': 'array',
'default': [],
'items': {'type': 'string'}
}, # compiler specs
'type' : 'array',
'default' : [],
'items' : { 'type' : 'string' } }, #compiler specs
'buildable': {
'type': 'boolean',
'type': 'boolean',
'default': True,
},
},
'providers': {
'type': 'object',
'type': 'object',
'default': {},
'additionalProperties': False,
'patternProperties': {
r'\w[\w-]*': {
'type': 'array',
'default': [],
'items': {'type': 'string'},
},
},
},
'type' : 'array',
'default' : [],
'items' : { 'type' : 'string' },},},},
'paths': {
'type': 'object',
'default': {},
'type' : 'object',
'default' : {},
}
},
},
},
},
},
},
},},},},},},
'modules': {
'$schema': 'http://json-schema.org/schema#',
'title': 'Spack module file configuration file schema',
......@@ -303,22 +283,17 @@
}
},
'autoload': {'$ref': '#/definitions/dependency_selection'},
'prerequisites':
{'$ref': '#/definitions/dependency_selection'},
'prerequisites': {'$ref': '#/definitions/dependency_selection'},
'conflict': {'$ref': '#/definitions/array_of_strings'},
'environment': {
'type': 'object',
'default': {},
'additionalProperties': False,
'properties': {
'set':
{'$ref': '#/definitions/dictionary_of_strings'},
'unset':
{'$ref': '#/definitions/array_of_strings'},
'prepend_path':
{'$ref': '#/definitions/dictionary_of_strings'},
'append_path':
{'$ref': '#/definitions/dictionary_of_strings'}
'set': {'$ref': '#/definitions/dictionary_of_strings'},
'unset': {'$ref': '#/definitions/array_of_strings'},
'prepend_path': {'$ref': '#/definitions/dictionary_of_strings'},
'append_path': {'$ref': '#/definitions/dictionary_of_strings'}
}
}
}
......@@ -329,20 +304,15 @@
'anyOf': [
{
'properties': {
'whitelist':
{'$ref': '#/definitions/array_of_strings'},
'blacklist':
{'$ref': '#/definitions/array_of_strings'},
'whitelist': {'$ref': '#/definitions/array_of_strings'},
'blacklist': {'$ref': '#/definitions/array_of_strings'},
'naming_scheme': {
'type':
'string' # Can we be more specific here?
'type': 'string' # Can we be more specific here?
}
}
},
{
'patternProperties':
{r'\w[\w-]*':
{'$ref': '#/definitions/module_file_configuration'}}
'patternProperties': {r'\w[\w-]*': {'$ref': '#/definitions/module_file_configuration'}}
}
]
}
......@@ -356,8 +326,7 @@
'prefix_inspections': {
'type': 'object',
'patternProperties': {
r'\w[\w-]*':
{ # path to be inspected (relative to prefix)
r'\w[\w-]*': { # path to be inspected for existence (relative to prefix)
'$ref': '#/definitions/array_of_strings'
}
}
......@@ -372,15 +341,13 @@
},
'tcl': {
'allOf': [
{'$ref': '#/definitions/module_type_configuration'
}, # Base configuration
{'$ref': '#/definitions/module_type_configuration'}, # Base configuration
{} # Specific tcl extensions
]
},
'dotkit': {
'allOf': [
{'$ref': '#/definitions/module_type_configuration'
}, # Base configuration
{'$ref': '#/definitions/module_type_configuration'}, # Base configuration
{} # Specific dotkit extensions
]
},
......@@ -389,6 +356,7 @@
},
},
}
"""OrderedDict of config scopes keyed by name.
Later scopes will override earlier scopes.
"""
......@@ -398,13 +366,12 @@
def validate_section_name(section):
"""Raise a ValueError if the section is not a valid section."""
if section not in section_schemas:
raise ValueError("Invalid config section: '%s'. Options are %s" %
(section, section_schemas))
raise ValueError("Invalid config section: '%s'. Options are %s"
% (section, section_schemas))
def extend_with_default(validator_class):
"""Add support for the 'default' attribute for
properties and patternProperties
"""Add support for the 'default' attribute for properties and patternProperties.
jsonschema does not handle this out of the box -- it only
validates. This allows us to set default values for configs
......@@ -413,15 +380,13 @@ def extend_with_default(validator_class):
"""
validate_properties = validator_class.VALIDATORS["properties"]
validate_pattern_properties = validator_class.VALIDATORS[
"patternProperties"]
validate_pattern_properties = validator_class.VALIDATORS["patternProperties"]
def set_defaults(validator, properties, instance, schema):
for property, subschema in properties.iteritems():
if "default" in subschema:
instance.setdefault(property, subschema["default"])
for err in validate_properties(validator, properties, instance,
schema):
for err in validate_properties(validator, properties, instance, schema):
yield err
def set_pp_defaults(validator, properties, instance, schema):
......@@ -432,19 +397,17 @@ def set_pp_defaults(validator, properties, instance, schema):
if re.match(property, key) and val is None:
instance[key] = subschema["default"]
for err in validate_pattern_properties(validator, properties, instance,
schema):
for err in validate_pattern_properties(validator, properties, instance, schema):
yield err
return validators.extend(validator_class, {
"properties": set_defaults,
"patternProperties": set_pp_defaults
"properties" : set_defaults,
"patternProperties" : set_pp_defaults
})
DefaultSettingValidator = extend_with_default(Draft4Validator)
def validate_section(data, schema):
"""Validate data read in from a Spack YAML file.
......@@ -466,9 +429,9 @@ class ConfigScope(object):
"""
def __init__(self, name, path):
self.name = name # scope name.
self.path = path # path to directory containing configs.
self.sections = {} # sections read from config files.
self.name = name # scope name.
self.path = path # path to directory containing configs.
self.sections = {} # sections read from config files.
# Register in a dict of all ConfigScopes
# TODO: make this cleaner. Mocking up for testing is brittle.
......@@ -479,14 +442,16 @@ def get_section_filename(self, section):
validate_section_name(section)
return os.path.join(self.path, "%s.yaml" % section)
def get_section(self, section):
if section not in self.sections:
path = self.get_section_filename(section)
if not section in self.sections:
path = self.get_section_filename(section)
schema = section_schemas[section]
data = _read_config_file(path, schema)
data = _read_config_file(path, schema)
self.sections[section] = data
return self.sections[section]
def write_section(self, section):
filename = self.get_section_filename(section)
data = self.get_section(section)
......@@ -498,8 +463,8 @@ def write_section(self, section):
except jsonschema.ValidationError as e:
raise ConfigSanityError(e, data)
except (yaml.YAMLError, IOError) as e:
raise ConfigFileError("Error writing to config file: '%s'" %
str(e))
raise ConfigFileError("Error writing to config file: '%s'" % str(e))
def clear(self):
"""Empty cached config information."""
......@@ -531,8 +496,8 @@ def validate_scope(scope):
return config_scopes[scope]
else:
raise ValueError("Invalid config scope: '%s'. Must be one of %s" %
(scope, config_scopes.keys()))
raise ValueError("Invalid config scope: '%s'. Must be one of %s"
% (scope, config_scopes.keys()))
def _read_config_file(filename, schema):
......@@ -558,12 +523,12 @@ def _read_config_file(filename, schema):
return data
except MarkedYAMLError as e:
raise ConfigFileError("Error parsing yaml%s: %s" %
(str(e.context_mark), e.problem))
raise ConfigFileError(
"Error parsing yaml%s: %s" % (str(e.context_mark), e.problem))
except IOError as e:
raise ConfigFileError("Error reading configuration file %s: %s" %
(filename, str(e)))
raise ConfigFileError(
"Error reading configuration file %s: %s" % (filename, str(e)))
def clear_config_caches():
......@@ -586,7 +551,6 @@ def _merge_yaml(dest, source):
parent instead of merging.
"""
def they_are(t):
return isinstance(dest, t) and isinstance(source, t)
......@@ -607,7 +571,7 @@ def they_are(t):
# Source dict is merged into dest.
elif they_are(dict):
for sk, sv in source.iteritems():
if sk not in dest:
if not sk in dest:
dest[sk] = copy.copy(sv)
else:
dest[sk] = _merge_yaml(dest[sk], source[sk])
......@@ -689,7 +653,7 @@ def print_section(section):
data = syaml.syaml_dict()
data[section] = get_config(section)
syaml.dump(data, stream=sys.stdout, default_flow_style=False)
except (yaml.YAMLError, IOError):
except (yaml.YAMLError, IOError) as e:
raise ConfigError("Error reading configuration: %s" % section)
......@@ -719,22 +683,15 @@ def is_spec_buildable(spec):
"""Return true if the spec pkgspec is configured as buildable"""
allpkgs = get_config('packages')
name = spec.name
if name not in allpkgs:
if not spec.name in allpkgs:
return True
if 'buildable' not in allpkgs[name]:
if not 'buildable' in allpkgs[spec.name]:
return True
return allpkgs[spec.name]['buildable']
class ConfigError(SpackError):
pass
class ConfigFileError(ConfigError):
pass
class ConfigError(SpackError): pass
class ConfigFileError(ConfigError): pass
def get_path(path, data):
if path:
......@@ -742,10 +699,8 @@ def get_path(path, data):
else:
return data
class ConfigFormatError(ConfigError):
"""Raised when a configuration format does not match its schema."""
def __init__(self, validation_error, data):
# Try to get line number from erroneous instance and its parent
instance_mark = getattr(validation_error.instance, '_start_mark', None)
......@@ -778,6 +733,5 @@ def __init__(self, validation_error, data):
message = '%s: %s' % (location, validation_error.message)
super(ConfigError, self).__init__(message)
class ConfigSanityError(ConfigFormatError):
"""Same as ConfigFormatError, raised when config is written by Spack."""
......@@ -26,8 +26,7 @@ def execute(self):
class UnsetEnv(NameModifier):
def execute(self):
# Avoid throwing if the variable was not set
os.environ.pop(self.name, None)
os.environ.pop(self.name, None) # Avoid throwing if the variable was not set
class SetPath(NameValueModifier):
......@@ -56,9 +55,7 @@ class RemovePath(NameValueModifier):
def execute(self):
environment_value = os.environ.get(self.name, '')
directories = environment_value.split(':') if environment_value else []
directories = [os.path.normpath(x)
for x in directories
if x != os.path.normpath(self.value)]
directories = [os.path.normpath(x) for x in directories if x != os.path.normpath(self.value)]
os.environ[self.name] = ':'.join(directories)
......@@ -66,8 +63,7 @@ class EnvironmentModifications(object):
"""
Keeps track of requests to modify the current environment.
Each call to a method to modify the environment stores the extra
information on the caller in the request:
Each call to a method to modify the environment stores the extra information on the caller in the request:
- 'filename' : filename of the module where the caller is defined
- 'lineno': line number where the request occurred
- 'context' : line of code that issued the request that failed
......@@ -75,10 +71,10 @@ class EnvironmentModifications(object):
def __init__(self, other=None):
"""
Initializes a new instance, copying commands from other if not None
Initializes a new instance, copying commands from other if it is not None
Args:
other: another instance of EnvironmentModifications
other: another instance of EnvironmentModifications from which (optional)
"""
self.env_modifications = []
if other is not None:
......@@ -97,7 +93,7 @@ def extend(self, other):
@staticmethod
def _check_other(other):
if not isinstance(other, EnvironmentModifications):
raise TypeError('not an instance of EnvironmentModifications')
raise TypeError('other must be an instance of EnvironmentModifications')
def _get_outside_caller_attributes(self):
stack = inspect.stack()
......@@ -105,10 +101,12 @@ def _get_outside_caller_attributes(self):
_, filename, lineno, _, context, index = stack[2]
context = context[index].strip()
except Exception:
filename = 'unknown file'
lineno = 'unknown line'
context = 'unknown context'
args = {'filename': filename, 'lineno': lineno, 'context': context}
filename, lineno, context = 'unknown file', 'unknown line', 'unknown context'
args = {
'filename': filename,
'lineno': lineno,
'context': context
}
return args
def set(self, name, value, **kwargs):
......@@ -172,7 +170,7 @@ def prepend_path(self, name, path, **kwargs):
def remove_path(self, name, path, **kwargs):
"""
Stores in the current object a request to remove a path from a list
Stores in the current object a request to remove a path from a path list
Args:
name: name of the path list in the environment
......@@ -187,8 +185,7 @@ def group_by_name(self):
Returns a dict of the modifications grouped by variable name
Returns:
dict mapping the environment variable name to the modifications
to be done on it
dict mapping the environment variable name to the modifications to be done on it
"""
modifications = collections.defaultdict(list)
for item in self:
......@@ -206,8 +203,7 @@ def apply_modifications(self):
Applies the modifications and clears the list
"""
modifications = self.group_by_name()
# Apply the modifications to the environment variables one variable
# at a time
# Apply the modifications to the environment variables one variable at a time
for name, actions in sorted(modifications.items()):
for x in actions:
x.execute()
......@@ -228,17 +224,13 @@ def concatenate_paths(paths):
def set_or_unset_not_first(variable, changes, errstream):
"""
Check if we are going to set or unset something after other modifications
have already been requested
Check if we are going to set or unset something after other modifications have already been requested
"""
indexes = [ii
for ii, item in enumerate(changes)
if ii != 0 and type(item) in [SetEnv, UnsetEnv]]
indexes = [ii for ii, item in enumerate(changes) if ii != 0 and type(item) in [SetEnv, UnsetEnv]]
if indexes:
good = '\t \t{context} at {filename}:{lineno}'
nogood = '\t--->\t{context} at {filename}:{lineno}'
message = 'Suspicious requests to set or unset the variable \'{var}\' found' # NOQA: ignore=E501
errstream(message.format(var=variable))
errstream('Suspicious requests to set or unset the variable \'{var}\' found'.format(var=variable))
for ii, item in enumerate(changes):
print_format = nogood if ii in indexes else good
errstream(print_format.format(**item.args))
......@@ -246,8 +238,8 @@ def set_or_unset_not_first(variable, changes, errstream):
def validate(env, errstream):
"""
Validates the environment modifications to check for the presence of
suspicious patterns. Prompts a warning for everything that was found
Validates the environment modifications to check for the presence of suspicious patterns. Prompts a warning for
everything that was found
Current checks:
- set or unset variables after other changes on the same variable
......@@ -262,8 +254,7 @@ def validate(env, errstream):
def filter_environment_blacklist(env, variables):
"""
Generator that filters out any change to environment variables present in
the input list
Generator that filters out any change to environment variables present in the input list
Args:
env: list of environment modifications
......
......@@ -23,34 +23,36 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""
This module contains code for creating environment modules, which can include
dotkits, tcl modules, lmod, and others.
This module contains code for creating environment modules, which can include dotkits, tcl modules, lmod, and others.
The various types of modules are installed by post-install hooks and removed
after an uninstall by post-uninstall hooks. This class consolidates the logic
for creating an abstract description of the information that module systems
need.
The various types of modules are installed by post-install hooks and removed after an uninstall by post-uninstall hooks.
This class consolidates the logic for creating an abstract description of the information that module systems need.
Currently that includes a number of directories to be appended to paths in the user's environment:
This module also includes logic for coming up with unique names for the module
files so that they can be found by the various shell-support files in
$SPACK/share/spack/setup-env.*.
* /bin directories to be appended to PATH
* /lib* directories for LD_LIBRARY_PATH
* /include directories for CPATH
* /man* and /share/man* directories for MANPATH
* the package prefix for CMAKE_PREFIX_PATH
Each hook implements the logic for writing its specific type of module.
This module also includes logic for coming up with unique names for the module files so that they can be found by the
various shell-support files in $SPACK/share/spack/setup-env.*.
Each hook in hooks/ implements the logic for writing its specific type of module file.
"""
import copy
import datetime
import os
import os.path
import re
import string
import textwrap
import string
import llnl.util.tty as tty
import spack
import spack.config
from llnl.util.filesystem import join_path, mkdirp
from spack.build_environment import parent_class_modules
from spack.build_environment import set_module_variables_for_package
from spack.build_environment import parent_class_modules, set_module_variables_for_package
from spack.environment import *
__all__ = ['EnvModule', 'Dotkit', 'TclModule']
......@@ -65,26 +67,30 @@ def print_help():
"""
For use by commands to tell user how to activate shell support.
"""
tty.msg("This command requires spack's shell integration.", "",
tty.msg("This command requires spack's shell integration.",
"",
"To initialize spack's shell commands, you must run one of",
"the commands below. Choose the right command for your shell.",
"", "For bash and zsh:",
" . %s/setup-env.sh" % spack.share_path, "",
"For csh and tcsh:", " setenv SPACK_ROOT %s" % spack.prefix,
" source %s/setup-env.csh" % spack.share_path, "")
"",
"For bash and zsh:",
" . %s/setup-env.sh" % spack.share_path,
"",
"For csh and tcsh:",
" setenv SPACK_ROOT %s" % spack.prefix,
" source %s/setup-env.csh" % spack.share_path,
"")
def inspect_path(prefix):
"""
Inspects the prefix of an installation to search for common layouts.
Issues a request to modify the environment when an item is found.
Inspects the prefix of an installation to search for common layouts. Issues a request to modify the environment
accordingly when an item is found.
Args:
prefix: prefix of the installation
Returns:
instance of EnvironmentModifications containing the requested
modifications
instance of EnvironmentModifications containing the requested modifications
"""
env = EnvironmentModifications()
# Inspect the prefix to check for the existence of common directories
......@@ -99,22 +105,18 @@ def inspect_path(prefix):
def dependencies(spec, request='all'):
"""
Returns the list of dependent specs for a given spec, according to the
given request
Returns the list of dependent specs for a given spec, according to the given request
Args:
spec: target spec
request: either 'none', 'direct' or 'all'
Returns:
empty list if 'none', direct dependency list if 'direct', all
dependencies if 'all'
empty list if 'none', direct dependency list if 'direct', all dependencies if 'all'
"""
if request not in ('none', 'direct', 'all'):
message = "Wrong value for argument 'request' : "
message += "should be one of ('none', 'direct', 'all')"
message += " [current value is '{0}']"
raise tty.error(message.format(request))
raise tty.error("Wrong value for argument 'request' : should be one of ('none', 'direct', 'all') "
" [current value is '%s']" % request)
if request == 'none':
return []
......@@ -122,19 +124,12 @@ def dependencies(spec, request='all'):
if request == 'direct':
return [xx for _, xx in spec.dependencies.items()]
# FIXME : during module file creation nodes seem to be visited
# FIXME : multiple times even if cover='nodes' is given. This work around
# FIXME : permits to get a unique list of spec anyhow. Maybe we miss a
# FIXME : merge step among nodes that refer to the same package?
# FIXME : during module file creation nodes seem to be visited multiple times even if cover='nodes'
# FIXME : is given. This work around permits to get a unique list of spec anyhow.
# FIXME : Possibly we miss a merge step among nodes that refer to the same package.
seen = set()
seen_add = seen.add
l = [xx
for xx in sorted(
spec.traverse(order='post',
depth=True,
cover='nodes',
root=False),
reverse=True)]
l = [xx for xx in sorted(spec.traverse(order='post', depth=True, cover='nodes', root=False), reverse=True)]
return [xx for ii, xx in l if not (xx in seen or seen_add(xx))]
......@@ -151,8 +146,7 @@ def update_dictionary_extending_lists(target, update):
def parse_config_options(module_generator):
"""
Parse the configuration file and returns a bunch of items that will be
needed during module file generation
Parse the configuration file and returns a bunch of items that will be needed during module file generation
Args:
module_generator: module generator for a given spec
......@@ -160,14 +154,11 @@ def parse_config_options(module_generator):
Returns:
autoloads: list of specs to be autoloaded
prerequisites: list of specs to be marked as prerequisite
filters: list of environment variables whose modification is
blacklisted in module files
env: list of custom environment modifications to be applied in the
module file
filters: list of environment variables whose modification is blacklisted in module files
env: list of custom environment modifications to be applied in the module file
"""
# Get the configuration for this kind of generator
module_configuration = copy.deepcopy(CONFIGURATION.get(
module_generator.name, {}))
module_configuration = copy.deepcopy(CONFIGURATION.get(module_generator.name, {}))
#####
# Merge all the rules
......@@ -188,12 +179,9 @@ def parse_config_options(module_generator):
#####
# Automatic loading loads
module_file_actions['autoload'] = dependencies(
module_generator.spec, module_file_actions.get('autoload', 'none'))
module_file_actions['autoload'] = dependencies(module_generator.spec, module_file_actions.get('autoload', 'none'))
# Prerequisites
module_file_actions['prerequisites'] = dependencies(
module_generator.spec, module_file_actions.get('prerequisites',
'none'))
module_file_actions['prerequisites'] = dependencies(module_generator.spec, module_file_actions.get('prerequisites', 'none'))
# Environment modifications
environment_actions = module_file_actions.pop('environment', {})
env = EnvironmentModifications()
......@@ -201,7 +189,7 @@ def parse_config_options(module_generator):
def process_arglist(arglist):
if method == 'unset':
for x in arglist:
yield (x, )
yield (x,)
else:
for x in arglist.iteritems():
yield x
......@@ -210,20 +198,19 @@ def process_arglist(arglist):
for args in process_arglist(arglist):
getattr(env, method)(*args)
# for item in arglist:
# if method == 'unset':
# args = [item]
# else:
# args = item.split(',')
# getattr(env, method)(*args)
# for item in arglist:
# if method == 'unset':
# args = [item]
# else:
# args = item.split(',')
# getattr(env, method)(*args)
return module_file_actions, env
def filter_blacklisted(specs, module_name):
"""
Given a sequence of specs, filters the ones that are blacklisted in the
module configuration file.
Given a sequence of specs, filters the ones that are blacklisted in the module configuration file.
Args:
specs: sequence of spec instances
......@@ -246,8 +233,7 @@ class EnvModule(object):
class __metaclass__(type):
def __init__(cls, name, bases, dict):
type.__init__(cls, name, bases, dict)
if cls.name != 'env_module' and cls.name in CONFIGURATION[
'enable']:
if cls.name != 'env_module' and cls.name in CONFIGURATION['enable']:
module_types[cls.name] = cls
def __init__(self, spec=None):
......@@ -263,8 +249,7 @@ def __init__(self, spec=None):
# long description is the docstring with reduced whitespace.
self.long_description = None
if self.spec.package.__doc__:
self.long_description = re.sub(r'\s+', ' ',
self.spec.package.__doc__)
self.long_description = re.sub(r'\s+', ' ', self.spec.package.__doc__)
@property
def naming_scheme(self):
......@@ -286,14 +271,12 @@ def tokens(self):
@property
def use_name(self):
"""
Subclasses should implement this to return the name the module command
uses to refer to the package.
Subclasses should implement this to return the name the module command uses to refer to the package.
"""
naming_tokens = self.tokens
naming_scheme = self.naming_scheme
name = naming_scheme.format(**naming_tokens)
name += '-' + self.spec.dag_hash(
) # Always append the hash to make the module file unique
name += '-' + self.spec.dag_hash() # Always append the hash to make the module file unique
# Not everybody is working on linux...
parts = name.split('/')
name = join_path(*parts)
......@@ -313,12 +296,8 @@ def category(self):
@property
def blacklisted(self):
configuration = CONFIGURATION.get(self.name, {})
whitelist_matches = [x
for x in configuration.get('whitelist', [])
if self.spec.satisfies(x)]
blacklist_matches = [x
for x in configuration.get('blacklist', [])
if self.spec.satisfies(x)]
whitelist_matches = [x for x in configuration.get('whitelist', []) if self.spec.satisfies(x)]
blacklist_matches = [x for x in configuration.get('blacklist', []) if self.spec.satisfies(x)]
if whitelist_matches:
message = '\tWHITELIST : %s [matches : ' % self.spec.cshort_spec
for rule in whitelist_matches:
......@@ -348,8 +327,7 @@ def write(self):
"""
if self.blacklisted:
return
tty.debug("\tWRITE : %s [%s]" %
(self.spec.cshort_spec, self.file_name))
tty.debug("\tWRITE : %s [%s]" % (self.spec.cshort_spec, self.file_name))
module_dir = os.path.dirname(self.file_name)
if not os.path.exists(module_dir):
......@@ -359,12 +337,11 @@ def write(self):
# installation prefix
env = inspect_path(self.spec.prefix)
# Let the extendee/dependency modify their extensions/dependencies
# before asking for package-specific modifications
# Let the extendee/dependency modify their extensions/dependencies before asking for
# package-specific modifications
spack_env = EnvironmentModifications()
# TODO : the code down below is quite similar to
# TODO : build_environment.setup_package and needs to be factored out
# TODO : to a single place
# TODO : the code down below is quite similar to build_environment.setup_package and needs to be
# TODO : factored out to a single place
for item in dependencies(self.spec, 'all'):
package = self.spec[item.name].package
modules = parent_class_modules(package.__class__)
......@@ -381,18 +358,14 @@ def write(self):
# Parse configuration file
module_configuration, conf_env = parse_config_options(self)
env.extend(conf_env)
filters = module_configuration.get('filter', {}).get(
'environment_blacklist', {})
filters = module_configuration.get('filter', {}).get('environment_blacklist',{})
# Build up the module file content
module_file_content = self.header
for x in filter_blacklisted(
module_configuration.pop('autoload', []), self.name):
for x in filter_blacklisted(module_configuration.pop('autoload', []), self.name):
module_file_content += self.autoload(x)
for x in filter_blacklisted(
module_configuration.pop('prerequisites', []), self.name):
for x in filter_blacklisted(module_configuration.pop('prerequisites', []), self.name):
module_file_content += self.prerequisite(x)
for line in self.process_environment_command(
filter_environment_blacklist(env, filters)):
for line in self.process_environment_command(filter_environment_blacklist(env, filters)):
module_file_content += line
for line in self.module_specific_content(module_configuration):
module_file_content += line
......@@ -419,13 +392,10 @@ def prerequisite(self, spec):
def process_environment_command(self, env):
for command in env:
try:
yield self.environment_modifications_formats[type(
command)].format(**command.args)
yield self.environment_modifications_formats[type(command)].format(**command.args)
except KeyError:
message = 'Cannot handle command of type {command} : skipping request' # NOQA: ignore=E501
tty.warn(message.format(command=type(command)))
context = '{context} at {filename}:{lineno}'
tty.warn(context.format(**command.args))
tty.warn('Cannot handle command of type {command} : skipping request'.format(command=type(command)))
tty.warn('{context} at {filename}:{lineno}'.format(**command.args))
@property
def file_name(self):
......@@ -438,12 +408,9 @@ def remove(self):
if os.path.exists(mod_file):
try:
os.remove(mod_file) # Remove the module file
os.removedirs(
os.path.dirname(mod_file)
) # Remove all the empty directories from the leaf up
os.removedirs(os.path.dirname(mod_file)) # Remove all the empty directories from the leaf up
except OSError:
# removedirs throws OSError on first non-empty directory found
pass
pass # removedirs throws OSError on first non-empty directory found
class Dotkit(EnvModule):
......@@ -457,12 +424,13 @@ class Dotkit(EnvModule):
autoload_format = 'dk_op {module_file}\n'
default_naming_format = '{name}-{version}-{compiler.name}-{compiler.version}' # NOQA: ignore=E501
prerequisite_format = None # TODO : does something like prerequisite exist for dotkit?
default_naming_format = '{name}-{version}-{compiler.name}-{compiler.version}'
@property
def file_name(self):
return join_path(Dotkit.path, self.spec.architecture,
'%s.dk' % self.use_name)
return join_path(Dotkit.path, self.spec.architecture, '%s.dk' % self.use_name)
@property
def header(self):
......@@ -506,7 +474,7 @@ class TclModule(EnvModule):
prerequisite_format = 'prereq {module_file}\n'
default_naming_format = '{name}-{version}-{compiler.name}-{compiler.version}' # NOQA: ignore=E501
default_naming_format = '{name}-{version}-{compiler.name}-{compiler.version}'
@property
def file_name(self):
......@@ -514,10 +482,9 @@ def file_name(self):
@property
def header(self):
timestamp = datetime.datetime.now()
# TCL Modulefile header
header = '#%Module1.0\n'
header += '## Module file created by spack (https://github.com/LLNL/spack) on %s\n' % timestamp # NOQA: ignore=E501
header += '## Module file created by spack (https://github.com/LLNL/spack) on %s\n' % datetime.datetime.now()
header += '##\n'
header += '## %s\n' % self.spec.short_spec
header += '##\n'
......@@ -542,19 +509,16 @@ def module_specific_content(self, configuration):
f = string.Formatter()
for item in conflict_format:
line = 'conflict ' + item + '\n'
if len([x for x in f.parse(line)
]) > 1: # We do have placeholder to substitute
for naming_dir, conflict_dir in zip(
self.naming_scheme.split('/'), item.split('/')):
if len([x for x in f.parse(line)]) > 1: # We do have placeholder to substitute
for naming_dir, conflict_dir in zip(self.naming_scheme.split('/'), item.split('/')):
if naming_dir != conflict_dir:
message = 'conflict scheme does not match naming'
message += ' [{spec}]\n\n'
message = 'conflict scheme does not match naming scheme [{spec}]\n\n'
message += 'naming scheme : "{nformat}"\n'
message += 'conflict scheme : "{cformat}"\n\n'
message += '** You may want to check your `modules.yaml` configuration file **\n' # NOQA: ignore=E501
tty.error(message.format(spec=self.spec,
nformat=self.naming_scheme,
cformat=item))
message += '** You may want to check your `modules.yaml` configuration file **\n'
tty.error(
message.format(spec=self.spec, nformat=self.naming_scheme, cformat=item)
)
raise SystemExit('Module generation aborted.')
line = line.format(**naming_tokens)
yield line
......@@ -23,23 +23,53 @@
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import sys
import llnl.util.tty as tty
import unittest
import nose
import spack
from spack.test.tally_plugin import Tally
from llnl.util.filesystem import join_path
import llnl.util.tty as tty
from llnl.util.tty.colify import colify
from spack.test.tally_plugin import Tally
import spack
"""Names of tests to be included in Spack's test suite"""
test_names = ['versions', 'url_parse', 'url_substitution', 'packages', 'stage',
'spec_syntax', 'spec_semantics', 'spec_dag', 'concretize',
'multimethod', 'install', 'package_sanity', 'config',
'directory_layout', 'pattern', 'python_version', 'git_fetch',
'svn_fetch', 'hg_fetch', 'mirror', 'modules', 'url_extrapolate',
'cc', 'link_tree', 'spec_yaml', 'optional_deps',
'make_executable', 'configure_guess', 'lock', 'database',
'namespace_trie', 'yaml', 'sbang', 'environment',
'cmd.uninstall', 'cmd.test_install']
test_names = ['versions',
'url_parse',
'url_substitution',
'packages',
'stage',
'spec_syntax',
'spec_semantics',
'spec_dag',
'concretize',
'multimethod',
'install',
'package_sanity',
'config',
'directory_layout',
'pattern',
'python_version',
'git_fetch',
'svn_fetch',
'hg_fetch',
'mirror',
'modules',
'url_extrapolate',
'cc',
'link_tree',
'spec_yaml',
'optional_deps',
'make_executable',
'configure_guess',
'lock',
'database',
'namespace_trie',
'yaml',
'sbang',
'environment',
'cmd.uninstall',
'cmd.test_install']
def list_tests():
......@@ -50,6 +80,7 @@ def list_tests():
def run(names, outputDir, verbose=False):
"""Run tests with the supplied names. Names should be a list. If
it's empty, run ALL of Spack's tests."""
verbosity = 1 if not verbose else 2
if not names:
names = test_names
......@@ -64,7 +95,7 @@ def run(names, outputDir, verbose=False):
tally = Tally()
for test in names:
module = 'spack.test.' + test
print(module)
print module
tty.msg("Running test: %s" % test)
......@@ -74,13 +105,15 @@ def run(names, outputDir, verbose=False):
xmlOutputFname = "unittests-{0}.xml".format(test)
xmlOutputPath = join_path(outputDir, xmlOutputFname)
runOpts += ["--with-xunit",
"--xunit-file={0}".format(xmlOutputPath)]
"--xunit-file={0}".format(xmlOutputPath)]
argv = [""] + runOpts + [module]
nose.run(argv=argv, addplugins=[tally])
result = nose.run(argv=argv, addplugins=[tally])
succeeded = not tally.failCount and not tally.errorCount
tty.msg("Tests Complete.", "%5d tests run" % tally.numberOfTestsRun,
"%5d failures" % tally.failCount, "%5d errors" % tally.errorCount)
tty.msg("Tests Complete.",
"%5d tests run" % tally.numberOfTestsRun,
"%5d failures" % tally.failCount,
"%5d errors" % tally.errorCount)
if succeeded:
tty.info("OK", format='g')
......
......@@ -2,18 +2,14 @@
from contextlib import contextmanager
import StringIO
import spack.modules
from spack.test.mock_packages_test import MockPackagesTest
FILE_REGISTRY = collections.defaultdict(StringIO.StringIO)
# Monkey-patch open to write module files to a StringIO instance
@contextmanager
def mock_open(filename, mode):
if not mode == 'w':
message = 'test.modules : unexpected opening mode [mock_open]'
raise RuntimeError(message)
raise RuntimeError('test.modules : unexpected opening mode for monkey-patched open')
FILE_REGISTRY[filename] = StringIO.StringIO()
......@@ -24,6 +20,7 @@ def mock_open(filename, mode):
FILE_REGISTRY[filename] = handle.getvalue()
handle.close()
import spack.modules
configuration_autoload_direct = {
'enable': ['tcl'],
......@@ -50,8 +47,7 @@ def mock_open(filename, mode):
'filter': {'environment_blacklist': ['CMAKE_PREFIX_PATH']}
},
'=x86-linux': {
'environment': {'set': {'FOO': 'foo'},
'unset': ['BAR']}
'environment': {'set': {'FOO': 'foo'}, 'unset': ['BAR']}
}
}
}
......@@ -76,14 +72,15 @@ def mock_open(filename, mode):
}
}
from spack.test.mock_packages_test import MockPackagesTest
class TclTests(MockPackagesTest):
def setUp(self):
super(TclTests, self).setUp()
self.configuration_obj = spack.modules.CONFIGURATION
spack.modules.open = mock_open
# Make sure that a non-mocked configuration will trigger an error
spack.modules.CONFIGURATION = None
spack.modules.CONFIGURATION = None # Make sure that a non-mocked configuration will trigger an error
def tearDown(self):
del spack.modules.open
......@@ -101,7 +98,7 @@ def test_simple_case(self):
spack.modules.CONFIGURATION = configuration_autoload_direct
spec = spack.spec.Spec('mpich@3.0.4=x86-linux')
content = self.get_modulefile_content(spec)
self.assertTrue('module-whatis "mpich @3.0.4"' in content)
self.assertTrue('module-whatis "mpich @3.0.4"' in content )
def test_autoload(self):
spack.modules.CONFIGURATION = configuration_autoload_direct
......@@ -120,22 +117,14 @@ def test_alter_environment(self):
spack.modules.CONFIGURATION = configuration_alter_environment
spec = spack.spec.Spec('mpileaks=x86-linux')
content = self.get_modulefile_content(spec)
self.assertEqual(
len([x
for x in content
if x.startswith('prepend-path CMAKE_PREFIX_PATH')]), 0)
self.assertEqual(
len([x for x in content if 'setenv FOO "foo"' in x]), 1)
self.assertEqual(len([x for x in content if x.startswith('prepend-path CMAKE_PREFIX_PATH')]), 0)
self.assertEqual(len([x for x in content if 'setenv FOO "foo"' in x]), 1)
self.assertEqual(len([x for x in content if 'unsetenv BAR' in x]), 1)
spec = spack.spec.Spec('libdwarf=x64-linux')
content = self.get_modulefile_content(spec)
self.assertEqual(
len([x
for x in content
if x.startswith('prepend-path CMAKE_PREFIX_PATH')]), 0)
self.assertEqual(
len([x for x in content if 'setenv FOO "foo"' in x]), 0)
self.assertEqual(len([x for x in content if x.startswith('prepend-path CMAKE_PREFIX_PATH')]), 0)
self.assertEqual(len([x for x in content if 'setenv FOO "foo"' in x]), 0)
self.assertEqual(len([x for x in content if 'unsetenv BAR' in x]), 0)
def test_blacklist(self):
......@@ -149,9 +138,6 @@ def test_conflicts(self):
spack.modules.CONFIGURATION = configuration_conflicts
spec = spack.spec.Spec('mpileaks=x86-linux')
content = self.get_modulefile_content(spec)
self.assertEqual(
len([x for x in content if x.startswith('conflict')]), 2)
self.assertEqual(
len([x for x in content if x == 'conflict mpileaks']), 1)
self.assertEqual(
len([x for x in content if x == 'conflict intel/14.0.1']), 1)
self.assertEqual(len([x for x in content if x.startswith('conflict')]), 2)
self.assertEqual(len([x for x in content if x == 'conflict mpileaks']), 1)
self.assertEqual(len([x for x in content if x == 'conflict intel/14.0.1']), 1)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment