Package qm :: Package test :: Module context
[hide private]
[frames] | no frames]

Source Code for Module qm.test.context

  1  ######################################################################## 
  2  # 
  3  # File:   context.py 
  4  # Author: Mark Mitchell 
  5  # Date:   11/06/2001 
  6  # 
  7  # Contents: 
  8  #   QMTest Context class 
  9  # 
 10  # Copyright (c) 2001, 2002, 2003 by CodeSourcery, LLC.  All rights reserved.  
 11  # 
 12  ######################################################################## 
 13   
 14  ######################################################################## 
 15  # Imports 
 16  ######################################################################## 
 17   
 18  import qm 
 19  import qm.common 
 20  import re 
 21  import sys 
 22  import types 
 23   
 24  ######################################################################## 
 25  # Classes 
 26  ######################################################################## 
 27   
28 -class ContextException(qm.common.QMException):
29 """A 'ContextException' indicates an invalid context variable.""" 30
31 - def __init__(self, key, msg = "missing context variable"):
32 """Construct a new 'ContextException'. 33 34 'key' -- A string giving the context key for which no valid 35 value was available. 36 37 'msg' -- A diagnostic identifier explaining the problem. The 38 message string may contain a fill-in for the key.""" 39 40 msg = qm.error(msg, key = key) 41 qm.common.QMException.__init__(self, msg) 42 self.key = key
43 44 45
46 -class ContextWrapper:
47 """Do-nothing class to preserve pickle compatability. 48 49 A class called 'ContextWrapper' used to be used in instead of a 50 'Context' class in some cases, and we used to put contexts into 51 'Result's. Because of how pickles work, this means that the only way 52 to unpickle these old 'Result's is to have a do-nothing placeholder 53 class that can be instantiated and then thrown away.""" 54 55 pass
56 57 58
59 -class Context(types.DictType):
60 """Test-time and local configuration for tests. 61 62 A 'Context' object contains all of the information a test needs to 63 execute, beyond what is stored as part of the test specification 64 itself. Information in the context can include, 65 66 * Local (per-user, etc.) configuration, such as where to find the 67 tested program. 68 69 * Environmental information, such as which machine the test is 70 running on. 71 72 * One-time configuration, including test arguments specified on 73 the command line. 74 75 A 'Context' object is effectively a mapping object whose keys must 76 be labels and values must be strings.""" 77 78 TARGET_CONTEXT_PROPERTY = "qmtest.target" 79 """The context variable giving the name of the current target.""" 80 81 DB_PATH_CONTEXT_PROPERTY = "qmtest.dbpath" 82 """The context variable giving the path to the database. 83 84 The value of this context variable will be a string giving the 85 path to the database directory. For example, if QMTest is invoked 86 as 'qmtest -D /path/to/db run', the value of this variable would 87 be '/path/to/db'. The value may be an absolute or a relative 88 path.""" 89 90 ID_CONTEXT_PROPERTY = "qmtest.id" 91 """The context variable giving the name of the running test or resource. 92 93 This value of this context variable will be the string giving the 94 name of the of the test or resource that is presently executing.""" 95 96 TMPDIR_CONTEXT_PROPERTY = "qmtest.tmpdir" 97 """A context property whose value is a string giving the path to a 98 temporary directory. This directory will be used only by the 99 'Runnable' in whose context this property occurs during the 100 execution of that 'Runnable'. No other object will use the same 101 temporary directory at the same time. There is no guarantee that 102 the temporary directory is empty, however; it may contain files 103 left behind by the execution of other 'Runnable' objects.""" 104 105 __safe_for_unpickling__ = 1 106 """Required to unpickle new-style classes under Python 2.2.""" 107
108 - def __init__(self, context = None):
109 """Construct a new context. 110 111 'context' -- If not 'None', the existing 'Context' being 112 wrapped by this new context.""" 113 114 super(Context, self).__init__() 115 116 self.__context = context 117 118 # Stuff everything in the RC configuration into the context. 119 options = qm.rc.GetOptions() 120 for option in options: 121 value = qm.rc.Get(option, None) 122 assert value is not None 123 self[option] = value
124 125
126 - def GetDerivedValue(self, klass, variable, default = None):
127 """Return the value for 'variable' in scope 'klass'. 128 Scopes are nested with '.', and inner variables hide 129 outer variables of the same name. Thus, looking up the 130 value of 'a.b.c.var' will return 1 if the context 131 contains 132 a.b.c.var=1 133 but 2 if it contains 134 a.b.d.var=1 135 a.b.var=2 136 a.var=3. 137 138 'klass' -- The variable's scope. 139 140 'variable' -- The variable name. 141 142 'default' -- Default value.""" 143 144 145 while True: 146 147 if klass: 148 k = klass + '.' + variable 149 else: 150 k = variable 151 if self.has_key(k): 152 return self[k] 153 if not klass: 154 return default 155 if '.' not in klass: 156 klass = '' 157 else: 158 klass = klass[0:klass.rfind('.')]
159 160
161 - def GetBoolean(self, key, default = None):
162 """Return the boolean value associated with 'key'. 163 164 'key' -- A string. 165 166 'default' -- A default boolean value. 167 168 returns -- The value associated with 'key' in the context, 169 interpreted as a boolean. 170 171 If there is no value associated with 'key' and default is not 172 'None', then the boolean value associated with default is 173 used. If there is no value associated with 'key' and default 174 is 'None', an exception is raised. 175 176 The value associated with 'key' must be a string. If not, an 177 exception is raised. If the value is a string, but does not 178 correspond to a boolean value, an exception is raised.""" 179 180 valstr = self.get(key) 181 if valstr is None: 182 if default is None: 183 raise ContextException(key) 184 else: 185 return default 186 187 try: 188 return qm.common.parse_boolean(valstr) 189 except ValueError: 190 raise ContextException(key, "invalid boolean context var")
191 192
193 - def GetStringList(self, key, default = None):
194 """Return the list of strings associated with 'key'. 195 196 'key' -- A string. 197 198 'default' -- A default list. 199 200 If there is no value associated with 'key' and default is not 201 'None', then the boolean value associated with default is 202 used. If there is no value associated with 'key' and default 203 is 'None', an exception is raised. 204 205 The value associated with 'key' must be a string. If not, an 206 exception is raised. If the value is a string, but does not 207 correspond to a string list, an exception is raised. 208 """ 209 210 valstr = self.get(key) 211 if valstr is None: 212 if default is None: 213 raise ContextException(key) 214 else: 215 return default 216 217 try: 218 return qm.common.parse_string_list(valstr) 219 except ValueError: 220 raise ContextException(key, "invalid string list context var")
221 222
223 - def GetTemporaryDirectory(self):
224 """Return the path to the a temporary directory. 225 226 returns -- The path to the a temporary directory. The 227 'Runnable' object may make free use of this temporary 228 directory; no other 'Runnable's will use the same directory at 229 the same time.""" 230 231 return self[self.TMPDIR_CONTEXT_PROPERTY]
232 233
234 - def Read(self, file_name):
235 """Read the context file 'file_name'. 236 237 'file_name' -- The name of the context file. 238 239 Reads the context file and adds the context properties in the 240 file to 'self'.""" 241 242 if file_name == "-": 243 # Read from standard input. 244 file = sys.stdin 245 else: 246 # Read from a named file. 247 try: 248 file = open(file_name, "r") 249 except: 250 raise qm.cmdline.CommandError, \ 251 qm.error("could not read file", path=file_name) 252 # Read the assignments. 253 assignments = qm.common.read_assignments(file) 254 # Add them to the context. 255 for (name, value) in assignments.items(): 256 try: 257 # Insert it into the context. 258 self[name] = value 259 except ValueError, msg: 260 # The format of the context key is invalid, but 261 # raise a 'CommandError' instead. 262 raise qm.cmdline.CommandError, msg
263 264 265 # Methods to simulate a map object. 266
267 - def __contains__(self, key):
268 269 if super(Context, self).__contains__(key): 270 return 1 271 272 if self.__context is not None: 273 return self.__context.__contains__(key) 274 275 return 0
276 277
278 - def get(self, key, default = None):
279 280 if key in self: 281 return self[key] 282 283 return default
284 285
286 - def has_key(self, key):
287 288 return key in self
289 290
291 - def __getitem__(self, key):
292 try: 293 return super(Context, self).__getitem__(key) 294 except KeyError: 295 if self.__context is None: 296 raise ContextException(key) 297 try: 298 return self.__context[key] 299 except KeyError: 300 raise ContextException(key)
301 302
303 - def items(self):
304 305 if self.__context is None: 306 return super(Context, self).items() 307 else: 308 # Have to be careful, because self.__context and self may 309 # contain different values for the same keys, and the values 310 # defined in self should override the values defined in 311 # self.__context. 312 unified_dict = dict(self.__context.items()) 313 unified_dict.update(self) 314 return unified_dict.items()
315 316 317 # Helper methods. 318
319 - def GetAddedProperties(self):
320 """Return the properties added to this context by resources. 321 322 returns -- A map from strings to values indicating properties 323 that were added to this context by resources.""" 324 325 if self.__context is None: 326 return {} 327 328 added = self.__context.GetAddedProperties() 329 added.update(self) 330 return added
331