environment : added machinery to collect modifications to the environment and apply them later

This commit is contained in:
alalazo 2016-03-14 14:35:48 +01:00
parent 1d70b590fc
commit f9923452b3
3 changed files with 209 additions and 1 deletions

View 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()

View file

@ -66,7 +66,8 @@
'database', 'database',
'namespace_trie', 'namespace_trie',
'yaml', 'yaml',
'sbang'] 'sbang',
'environment']
def list_tests(): def list_tests():

View 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'])