1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import qm
19 import qm.common
20 import re
21 import sys
22 import types
23
24
25
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
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
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
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
244 file = sys.stdin
245 else:
246
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
253 assignments = qm.common.read_assignments(file)
254
255 for (name, value) in assignments.items():
256 try:
257
258 self[name] = value
259 except ValueError, msg:
260
261
262 raise qm.cmdline.CommandError, msg
263
264
265
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
304
305 if self.__context is None:
306 return super(Context, self).items()
307 else:
308
309
310
311
312 unified_dict = dict(self.__context.items())
313 unified_dict.update(self)
314 return unified_dict.items()
315
316
317
318
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