environment : added machinery to collect modifications to the environment and apply them later
This commit is contained in:
parent
1d70b590fc
commit
f9923452b3
3 changed files with 209 additions and 1 deletions
157
lib/spack/spack/environment.py
Normal file
157
lib/spack/spack/environment.py
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import collections
|
||||||
|
|
||||||
|
|
||||||
|
class SetEnv(object):
|
||||||
|
def __init__(self, name, value, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
os.environ[self.name] = str(self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class UnsetEnv(object):
|
||||||
|
def __init__(self, name, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
os.environ.pop(self.name, None) # Avoid throwing if the variable was not set
|
||||||
|
|
||||||
|
|
||||||
|
class AppendPath(object):
|
||||||
|
def __init__(self, name, path, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
environment_value = os.environ.get(self.name, '')
|
||||||
|
directories = environment_value.split(':') if environment_value else []
|
||||||
|
# TODO : Check if this is a valid directory name
|
||||||
|
directories.append(os.path.normpath(self.path))
|
||||||
|
os.environ[self.name] = ':'.join(directories)
|
||||||
|
|
||||||
|
|
||||||
|
class PrependPath(object):
|
||||||
|
def __init__(self, name, path, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
environment_value = os.environ.get(self.name, '')
|
||||||
|
directories = environment_value.split(':') if environment_value else []
|
||||||
|
# TODO : Check if this is a valid directory name
|
||||||
|
directories = [os.path.normpath(self.path)] + directories
|
||||||
|
os.environ[self.name] = ':'.join(directories)
|
||||||
|
|
||||||
|
|
||||||
|
class RemovePath(object):
|
||||||
|
def __init__(self, name, path, **kwargs):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
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.path)]
|
||||||
|
os.environ[self.name] = ':'.join(directories)
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentModifications(object):
|
||||||
|
"""
|
||||||
|
Keeps track of requests to modify the current environment
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.env_modifications = []
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.env_modifications)
|
||||||
|
|
||||||
|
def set_env(self, name, value, **kwargs):
|
||||||
|
"""
|
||||||
|
Stores in the current object a request to set an environment variable
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: name of the environment variable to be set
|
||||||
|
value: value of the environment variable
|
||||||
|
"""
|
||||||
|
item = SetEnv(name, value, **kwargs)
|
||||||
|
self.env_modifications.append(item)
|
||||||
|
|
||||||
|
def unset_env(self, name, **kwargs):
|
||||||
|
"""
|
||||||
|
Stores in the current object a request to unset an environment variable
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: name of the environment variable to be set
|
||||||
|
"""
|
||||||
|
item = UnsetEnv(name, **kwargs)
|
||||||
|
self.env_modifications.append(item)
|
||||||
|
|
||||||
|
def append_path(self, name, path, **kwargs):
|
||||||
|
"""
|
||||||
|
Stores in the current object a request to append a path to a path list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: name of the path list in the environment
|
||||||
|
path: path to be appended
|
||||||
|
"""
|
||||||
|
item = AppendPath(name, path, **kwargs)
|
||||||
|
self.env_modifications.append(item)
|
||||||
|
|
||||||
|
def prepend_path(self, name, path, **kwargs):
|
||||||
|
"""
|
||||||
|
Same as `append_path`, but the path is pre-pended
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: name of the path list in the environment
|
||||||
|
path: path to be pre-pended
|
||||||
|
"""
|
||||||
|
item = PrependPath(name, path, **kwargs)
|
||||||
|
self.env_modifications.append(item)
|
||||||
|
|
||||||
|
def remove_path(self, name, path, **kwargs):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
path: path to be removed
|
||||||
|
"""
|
||||||
|
item = RemovePath(name, path, **kwargs)
|
||||||
|
self.env_modifications.append(item)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_environment_modifications(env):
|
||||||
|
modifications = collections.defaultdict(list)
|
||||||
|
for item in env:
|
||||||
|
modifications[item.name].append(item)
|
||||||
|
return modifications
|
||||||
|
|
||||||
|
|
||||||
|
def apply_environment_modifications(env):
|
||||||
|
"""
|
||||||
|
Modifies the current environment according to the request in env
|
||||||
|
|
||||||
|
Args:
|
||||||
|
env: object storing modifications to the environment
|
||||||
|
"""
|
||||||
|
modifications = validate_environment_modifications(env)
|
||||||
|
|
||||||
|
# Cycle over the environment variables that will be modified
|
||||||
|
for variable, actions in modifications.items():
|
||||||
|
# Execute all the actions in the order they were issued
|
||||||
|
for x in actions:
|
||||||
|
x.execute()
|
|
@ -66,7 +66,8 @@
|
||||||
'database',
|
'database',
|
||||||
'namespace_trie',
|
'namespace_trie',
|
||||||
'yaml',
|
'yaml',
|
||||||
'sbang']
|
'sbang',
|
||||||
|
'environment']
|
||||||
|
|
||||||
|
|
||||||
def list_tests():
|
def list_tests():
|
||||||
|
|
50
lib/spack/spack/test/environment.py
Normal file
50
lib/spack/spack/test/environment.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
from spack.environment import EnvironmentModifications, apply_environment_modifications
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentTest(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
os.environ.clear()
|
||||||
|
os.environ['UNSET_ME'] = 'foo'
|
||||||
|
os.environ['EMPTY_PATH_LIST'] = ''
|
||||||
|
os.environ['PATH_LIST'] = '/path/second:/path/third'
|
||||||
|
os.environ['REMOVE_PATH_LIST'] = '/a/b:/duplicate:/a/c:/remove/this:/a/d:/duplicate/:/f/g'
|
||||||
|
|
||||||
|
def test_set_env(self):
|
||||||
|
env = EnvironmentModifications()
|
||||||
|
env.set_env('A', 'dummy value')
|
||||||
|
env.set_env('B', 3)
|
||||||
|
apply_environment_modifications(env)
|
||||||
|
self.assertEqual('dummy value', os.environ['A'])
|
||||||
|
self.assertEqual(str(3), os.environ['B'])
|
||||||
|
|
||||||
|
def test_unset_env(self):
|
||||||
|
env = EnvironmentModifications()
|
||||||
|
self.assertEqual('foo', os.environ['UNSET_ME'])
|
||||||
|
env.unset_env('UNSET_ME')
|
||||||
|
apply_environment_modifications(env)
|
||||||
|
self.assertRaises(KeyError, os.environ.__getitem__, 'UNSET_ME')
|
||||||
|
|
||||||
|
def test_path_manipulation(self):
|
||||||
|
env = EnvironmentModifications()
|
||||||
|
|
||||||
|
env.append_path('PATH_LIST', '/path/last')
|
||||||
|
env.prepend_path('PATH_LIST', '/path/first')
|
||||||
|
|
||||||
|
env.append_path('EMPTY_PATH_LIST', '/path/middle')
|
||||||
|
env.append_path('EMPTY_PATH_LIST', '/path/last')
|
||||||
|
env.prepend_path('EMPTY_PATH_LIST', '/path/first')
|
||||||
|
|
||||||
|
env.append_path('NEWLY_CREATED_PATH_LIST', '/path/middle')
|
||||||
|
env.append_path('NEWLY_CREATED_PATH_LIST', '/path/last')
|
||||||
|
env.prepend_path('NEWLY_CREATED_PATH_LIST', '/path/first')
|
||||||
|
|
||||||
|
env.remove_path('REMOVE_PATH_LIST', '/remove/this')
|
||||||
|
env.remove_path('REMOVE_PATH_LIST', '/duplicate/')
|
||||||
|
|
||||||
|
apply_environment_modifications(env)
|
||||||
|
self.assertEqual('/path/first:/path/second:/path/third:/path/last', os.environ['PATH_LIST'])
|
||||||
|
self.assertEqual('/path/first:/path/middle:/path/last', os.environ['EMPTY_PATH_LIST'])
|
||||||
|
self.assertEqual('/path/first:/path/middle:/path/last', os.environ['NEWLY_CREATED_PATH_LIST'])
|
||||||
|
self.assertEqual('/a/b:/a/c:/a/d:/f/g', os.environ['REMOVE_PATH_LIST'])
|
Loading…
Reference in a new issue