Control.py
Go to the documentation of this file.
1 '''
2 Created on Jun 27, 2011
3 
4 @author: mplajner
5 '''
6 import xmlModule
7 import os
8 from time import gmtime, strftime
9 import Variable
10 import EnvConfig
11 import logging
12 
13 class Environment(object):
14  '''object to hold settings of environment'''
15 
16  def __init__(self, loadFromSystem=True, useAsWriter=False, searchPath=None):
17  '''Initial variables to be pushed and setup
18 
19  append switch between append and prepend for initial variables.
20  loadFromSystem causes variable`s system value to be loaded on first encounter.
21  If useAsWriter == True than every change to variables is recorded to XML file.
22  reportLevel sets the level of messaging.
23  '''
24  self.log = logging.getLogger('Environment')
25 
26  self.separator = ':'
27 
28  # Prepeare the internal search path for xml files (used by 'include' elements)
29  if searchPath is None:
30  self.searchPath = []
31  else:
32  self.searchPath = list(searchPath)
33 
34  def addToSearchPath(n, _1, _2):
35  '''
36  Add entries to the search path expanding variables inside.
37  '''
38  entries = Variable.List('_SEARCH_PATH')
39  entries.set(n, os.pathsep, environment=self.variables)
40  self.searchPath.extend(entries)
41 
42  self.actions = {}
43  self.actions['include'] = lambda n, c, h: self.loadXML(self._locate(n, c, h))
44  self.actions['append'] = lambda n, v, _: self.append(n, v)
45  self.actions['prepend'] = lambda n, v, _: self.prepend(n, v)
46  self.actions['set'] = lambda n, v, _: self.set(n, v)
47  self.actions['unset'] = lambda n, v, _: self.unset(n, v)
48  self.actions['default'] = lambda n, v, _: self.default(n, v)
49  self.actions['remove'] = lambda n, v, _: self.remove(n, v)
50  self.actions['remove-regexp'] = lambda n, v, _: self.remove_regexp(n, v)
51  self.actions['declare'] = self.declare
52  self.actions['search_path'] = addToSearchPath
53 
54  self.variables = {}
55 
56  self.loadFromSystem = loadFromSystem
57  self.asWriter = useAsWriter
58  if useAsWriter:
60  self.startXMLinput()
61 
62  self.loadedFiles = set()
63 
64  # Prepare the stack for the directory of the loaded file(s)
65  self._fileDirStack = []
66  # Note: cannot use self.declare() because we do not want to write out
67  # the changes to ${.}
68  dot = Variable.Scalar('.', local=True)
69  dot.expandVars = False
70  dot.set('')
71  self.variables['.'] = dot
72 
73 
74  def _locate(self, filename, caller=None, hints=None):
75  '''
76  Find 'filename' in the internal search path.
77  '''
78  from os.path import isabs, isfile, join, dirname, normpath, abspath
79  if isabs(filename):
80  return filename
81 
82  self.log.debug('looking for %s', filename)
83  if hints is None:
84  hints = []
85  elif type(hints) is str:
86  hints = hints.split(self.separator)
87 
88  if caller:
89  calldir = dirname(caller)
90  localfile = join(calldir, filename)
91  self.log.debug('trying %s', localfile)
92  if isfile(localfile):
93  self.log.debug('OK (local file)')
94  return localfile
95  # allow for relative hints
96  hints = [join(calldir, hint) for hint in hints]
97 
98  sp = EnvConfig.path + self.searchPath + hints
99  def candidates():
100  for d in sp:
101  f = normpath(join(d, filename))
102  self.log.debug('trying %s', f)
103  yield f
104  try:
105  f = (abspath(f) for f in candidates() if isfile(f)).next()
106  self.log.debug('OK')
107  return f
108  except StopIteration:
109  from errno import ENOENT
110  raise OSError(ENOENT, 'cannot find file in %r' % sp, filename)
111 
112  def vars(self, strings=True):
113  '''returns dictionary of all variables optionally converted to string'''
114  if strings:
115  return dict([(n, v.value(True))
116  for n, v in self.variables.items()
117  if n != '.'])
118  else:
119  # clone the dictionary to remove the internal special var '.'
120  vars_ = dict(self.variables)
121  if '.' in vars_:
122  del vars_['.']
123  return vars_
124 
125  def var(self, name):
126  '''Gets a single variable. If not available then tries to load from system.'''
127  if name in self.variables:
128  return self.variables[name]
129  else:
130  return os.environ[name]
131 
132  def search(self, varName, expr, regExp=False):
133  '''Searches in a variable for a value.'''
134  return self.variables[varName].search(expr, regExp)
135 
136  def _guessType(self, varname):
137  '''
138  Guess the type of the variable from its name: if the name contains
139  'PATH' or 'DIRS', then the variable is a list, otherwise it is a scalar.
140  '''
141  varname = varname.upper() # make the comparison case insensitive
142  if 'PATH' in varname or 'DIRS' in varname:
143  return 'list'
144  else:
145  return 'scalar'
146 
147  def declare(self, name, vartype, local):
148  '''Creates an instance of new variable. It loads values from the OS if the variable is not local.'''
149  if self.asWriter:
150  self._writeVarToXML(name, 'declare', '', vartype, local)
151 
152  if not isinstance(local, bool):
153  if str(local).lower() == 'true':
154  local = True
155  else:
156  local = False
157 
158  if name in self.variables.keys():
159  if self.variables[name].local != local:
160  raise Variable.EnvError(name, 'redeclaration')
161  else:
162  if vartype.lower() == "list":
163  if not isinstance(self.variables[name],Variable.List):
164  raise Variable.EnvError(name, 'redeclaration')
165  else:
166  if not isinstance(self.variables[name],Variable.Scalar):
167  raise Variable.EnvError(name, 'redeclaration')
168 
169  if vartype.lower() == "list":
170  a = Variable.List(name, local)
171  else:
172  a = Variable.Scalar(name, local)
173 
174  if self.loadFromSystem and not local and name in os.environ:
175  a.expandVars = False # disable var expansion when importing from the environment
176  a.set(os.environ[name], os.pathsep, environment=self.variables)
177  a.expandVars = True
178 
179  self.variables[name] = a
180 
181  def append(self, name, value):
182  '''Appends to an existing variable.'''
183  if self.asWriter:
184  self._writeVarToXML(name, 'append', value)
185  else:
186  if name not in self.variables:
187  self.declare(name, self._guessType(name), False)
188  self.variables[name].append(value, self.separator, self.variables)
189 
190  def prepend(self, name, value):
191  '''Prepends to an existing variable, or create a new one.'''
192  if self.asWriter:
193  self._writeVarToXML(name, 'prepend', value)
194  else:
195  if name not in self.variables:
196  self.declare(name, self._guessType(name), False)
197  self.variables[name].prepend(value, self.separator, self.variables)
198 
199  def set(self, name, value):
200  '''Sets a single variable - overrides any previous value!'''
201  name = str(name)
202  if self.asWriter:
203  self._writeVarToXML(name, 'set', value)
204  else:
205  if name not in self.variables:
206  self.declare(name, self._guessType(name), False)
207  self.variables[name].set(value, self.separator, self.variables)
208 
209  def default(self, name, value):
210  '''Sets a single variable only if it is not already set!'''
211  name = str(name)
212  if self.asWriter:
213  self._writeVarToXML(name, 'default', value)
214  else:
215  # Here it is different from the other actions because after a 'declare'
216  # we cannot tell if the variable was already set or not.
217  # FIXME: improve declare() to allow for a default.
218  if name not in self.variables:
219  if self._guessType(name) == 'list':
220  v = Variable.List(name, False)
221  else:
222  v = Variable.Scalar(name, False)
223  if self.loadFromSystem and name in os.environ:
224  v.set(os.environ[name], os.pathsep, environment=self.variables)
225  else:
226  v.set(value, self.separator, environment=self.variables)
227  self.variables[name] = v
228  else:
229  v = self.variables[name]
230  if not v.val:
231  v.set(value, self.separator, environment=self.variables)
232 
233  def unset(self, name, value=None):# pylint: disable=W0613
234  '''Unsets a single variable to an empty value - overrides any previous value!'''
235  if self.asWriter:
236  self._writeVarToXML(name, 'unset', '')
237  else:
238  if name in self.variables:
239  del self.variables[name]
240 
241  def remove(self, name, value, regexp=False):
242  '''Remove value from variable.'''
243  if self.asWriter:
244  self._writeVarToXML(name, 'remove', value)
245  else:
246  if name not in self.variables:
247  self.declare(name, self._guessType(name), False)
248  self.variables[name].remove(value, self.separator, regexp)
249 
250  def remove_regexp(self, name, value):
251  self.remove(name, value, True)
252 
253 
254  def searchFile(self, filename, varName):
255  '''Searches for appearance of variable in a file.'''
256  XMLFile = xmlModule.XMLFile()
257  variable = XMLFile.variable(filename, name=varName)
258  return variable
259 
260  def loadXML(self, fileName=None, namespace='EnvSchema'):
261  '''Loads XML file for input variables.'''
262  XMLfile = xmlModule.XMLFile()
263  fileName = self._locate(fileName)
264  if fileName in self.loadedFiles:
265  self.log.debug('ignore %s: already loaded', fileName)
266  return # ignore recursion
267  self.log.debug('loading %s', fileName)
268  self.loadedFiles.add(fileName)
269  dot = self.variables['.']
270  # push the previous value of ${.} onto the stack...
271  self._fileDirStack.append(dot.value())
272  # ... and update the variable
273  dot.set(os.path.dirname(fileName))
274  variables = XMLfile.variable(fileName, namespace=namespace)
275  for i, (action, args) in enumerate(variables):
276  if action not in self.actions:
277  self.log.error('Node {0}: No action taken with var "{1}". Probably wrong action argument: "{2}".'.format(i, args[0], action))
278  else:
279  self.actions[action](*args) # pylint: disable=W0142
280  # restore the old value of ${.}
281  dot.set(self._fileDirStack.pop())
282  # ensure that a change of ${.} in the file is reverted when exiting it
283  self.variables['.'] = dot
284 
285  def startXMLinput(self):
286  '''Renew writer for new input.'''
287  self.writer.resetWriter()
288 
289 
290  def finishXMLinput(self, outputFile = ''):
291  '''Finishes input of XML file and closes the file.'''
292  self.writer.writeToFile(outputFile)
293 
294 
295  def writeToFile(self, fileName, shell='sh'):
296  '''Creates an output file with a specified name to be used for setting variables by sourcing this file'''
297  f = open(fileName, 'w')
298  if shell == 'sh':
299  f.write('#!/bin/bash\n')
300  for variable in self.variables:
301  if not self[variable].local:
302  f.write('export ' +variable+'='+self[variable].value(True, os.pathsep)+'\n')
303  elif shell == 'csh':
304  f.write('#!/bin/csh\n')
305  for variable in self.variables:
306  if not self[variable].local:
307  f.write('setenv ' +variable+' '+self[variable].value(True, os.pathsep)+'\n')
308  else:
309  f.write('')
310  f.write('REM This is an enviroment settings file generated on '+strftime("%a, %d %b %Y %H:%M:%S\n", gmtime()))
311  for variable in self.variables:
312  if not self[variable].local:
313  f.write('set '+variable+'='+self[variable].value(True, os.pathsep)+'\n')
314 
315  f.close()
316 
317 
318  def writeToXMLFile(self, fileName):
319  '''Writes the current state of environment to a XML file.
320 
321  NOTE: There is no trace of actions taken, variables are written with a set action only.
322  '''
323  writer = xmlModule.XMLFile()
324  for varName in self.variables:
325  if varName == '.':
326  continue # this is an internal transient variable
327  writer.writeVar(varName, 'set', self.variables[varName].value(True, self.separator))
328  writer.writeToFile(fileName)
329 
330 
331  def presetFromSystem(self):
332  '''Loads all variables from the current system settings.'''
333  for k, v in os.environ.items():
334  if k not in self.variables:
335  self.set(k, v)
336 
337  def process(self):
338  '''
339  Call the variable processors on all the variables.
340  '''
341  for v in self.variables.values():
342  v.val = v.process(v.val, self.variables)
343 
344  def _concatenate(self, value):
345  '''Returns a variable string with separator separator from the values list'''
346  stri = ""
347  for it in value:
348  stri += it + self.separator
349  stri = stri[0:len(stri)-1]
350  return stri
351 
352 
353  def _writeVarToXML(self, name, action, value, vartype='list', local='false'):
354  '''Writes single variable to XML file.'''
355  if isinstance(value, list):
356  value = self._concatenate(value)
357  self.writer.writeVar(name, action, value, vartype, local)
358 
359 
360  def __getitem__(self, key):
361  return self.variables[key]
362 
363  def __setitem__(self, key, value):
364  if key in self.variables.keys():
365  self.log.warning('Addition canceled because of duplicate entry. Var: "%s" value: "%s".', key, value)
366  else:
367  self.append(key, value)
368 
369  def __delitem__(self, key):
370  del self.variables[key]
371 
372  def __iter__(self):
373  for i in self.variables:
374  yield i
375 
376  def __contains__(self, item):
377  return item in self.variables.keys()
378 
379  def __len__(self):
380  return len(self.variables.keys())
381 
def __delitem__(self, key)
Definition: Control.py:369
def _concatenate(self, value)
Definition: Control.py:344
def prepend(self, name, value)
Definition: Control.py:190
def _guessType(self, varname)
Definition: Control.py:136
def set(self, name, value)
Definition: Control.py:199
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
def searchFile(self, filename, varName)
Definition: Control.py:254
def append(self, name, value)
Definition: Control.py:181
def __setitem__(self, key, value)
Definition: Control.py:363
def remove_regexp(self, name, value)
Definition: Control.py:250
def declare(self, name, vartype, local)
Definition: Control.py:147
def __getitem__(self, key)
Definition: Control.py:360
def __contains__(self, item)
Definition: Control.py:376
def writeToXMLFile(self, fileName)
Definition: Control.py:318
def dirname(url)
string type
Definition: gaudirun.py:151
def default(self, name, value)
Definition: Control.py:209