4 Copyright 2015 Cray Inc. All Rights Reserved
8 ### TBD: The "Simple" in the addSimple* interfaces refers to a flag
9 ### that's a single bit. It's meant to distinguish from flags that
10 ### have multibit fields, such as the node/zone indices stuck in the
11 ### high end of struct page.flags; or a field that's mostly a pointer
12 ### but with some flags in the low bits.
14 ### To add cases like that will mean redoing most of the
15 ### implementation, but all the current interfaces should be ok, with
16 ### new interfaces added to let users define the non-simple flags.
22 """A collection of flags and values, with routines for translating
24 For decoding a flag int to a string, encoding a flag string to an
25 int, and providing python identifiers for testing by name, e.g.,
27 jafs = FlagSet() # job_attach flagset
28 jafs.addSimpleFlag("disable_affinity_apply")
29 if job_attach.flags & jafs.disable_affinity_apply: ...
31 The advantages over just using a dict include:
32 * Define the values once, and get value->string, string->value,
33 and python identifiers ns.<name> and ns.<name>_shift as above.
34 * The auto-incrementing _next_bit
36 def __init__(self, mapping=None):
37 """Create and initialize a FlagSet object
40 mapping: if specified, provides a mapping object, e.g. dict,
41 that supplies the initial key(name)/value pairs.
43 # Public dict of flag names to flag values (not the bit number)
44 self.str_to_value = {}
45 # Public dict of flag values to flag names
46 self.value_to_str = {}
50 # sorted_values is so that translating a value to a string
51 # will report the strings in the same order every time. That
52 # order is by numerically increasing value.
53 self._sorted_values = []
54 self._sorted_strs = []
56 if mapping is not None:
59 def addSimpleFlag(self, s, bit=None):
60 """Add a single-bit flag.
62 If bit is not specified, uses the bit one greater than the
63 previously defined bit. If multiple flags are defined to use
64 the same bit, value_to_str will remember only the first."""
66 if s in self.str_to_value.keys():
67 raise ValueError("Flag {0} already defined (value {1:x})".format(
68 s, self.str_to_value[s]))
69 if s + "_shift" in self.str_to_value.keys():
70 raise ValueError("Flag {0} conflicts with another "
71 "flag ({1})".format(s, s + "_shift"))
75 except AttributeError:
78 raise ValueError("Value {0} already used by FlagSet object!".
82 getattr(self, s + "_shift")
83 except AttributeError:
86 raise valueError("{0}_shift already used by FlagSet object!".
92 self._next_bit = bit + 1
95 if value not in self.value_to_str:
96 self.value_to_str[value] = s
97 self.str_to_value[s] = value
99 self._sorted_values = []
101 setattr(self, s, value)
102 setattr(self, s+"_shift", bit)
104 def addSimpleFlags(self, *l):
105 """Adds a list of single-bit flags."""
106 map(self.addSimpleFlag, l)
108 def addMap(self, mapping):
109 """Add the key/value pairs from a mapping type"""
110 for k, v in mapping.items():
111 self.addSimpleFlag(k, v)
113 def _EnsureSorted(self):
114 if self._sorted_values:
116 self._sorted_values = sorted(self.value_to_str.keys())
117 # self._sorted_strs = sorted(self.str_to_value.keys())
120 def flagsToStringList(self, flagint):
121 """Translate a given flag int to a list of flag strings."""
124 for v in self._sorted_values:
126 strs.append(self.value_to_str[v])
129 strs.append("{0:#x}".format(flagint))
132 def UFLookup(self, key, **kwargs):
133 return uflookup.UFLookup(self.str_to_value, key, **kwargs)
135 # TBD: interface to enable a script --dump-flag-translations argument?
139 def join_flaglist(fl, sep = "|", empty = "0"):
140 """Helper function to join a list of flag strings."""
149 # I'm trying to follow the convention of
151 # assertEquals(expectedvalue, function_under_test(args))
153 # I didn't discover that (on some unittest page) until I was halfway
154 # through, so I may not have gotten them all the right order.
156 if __name__ == '__main__':
159 class Test_join_flaglist(unittest.TestCase):
160 """Test the join_flaglist function"""
162 def assertJoinFlaglistEqual(self, expectedstring, flaglist):
163 self.assertEqual(expectedstring, join_flaglist(flaglist))
165 def test_single_value(self):
166 """Test join_flaglist() with a single value"""
167 self.assertJoinFlaglistEqual("aflag", ["aflag"])
169 def test_two_values(self):
170 """Test join_flaglist() with two values"""
171 self.assertJoinFlaglistEqual("aflag|bflag",["aflag", "bflag"])
173 def test_three_values(self):
174 """Test join_flaglist() with three values"""
175 self.assertJoinFlaglistEqual("af|bf|cf", ["af", "bf", "cf"])
177 def test_comma_sep(self):
178 """Test join_flaglist() with a non-default sep"""
179 self.assertEqual("af,bf,cf",
180 join_flaglist(["af", "bf", "cf"], sep=','))
182 def test_join_empty(self):
183 """Test join_flaglist() with an empty list"""
184 self.assertEqual("0", join_flaglist([]))
186 def test_join_empty_nondefault(self):
187 """Test join_flaglist() with a non-default value of empty"""
188 self.assertEqual(" ", join_flaglist([], empty=" "))
191 class Test_FlagSet(unittest.TestCase):
192 """Test the FlagSet class"""
197 def VerifyFlag(self, string, value):
198 """Test string->value and value->string"""
199 self.assertEqual(value, self.fs.str_to_value[string])
200 self.assertEqual(string, self.fs.value_to_str[value])
201 self.assertEqual(value, getattr(self.fs, string))
202 self.assertEqual(value, 1<<getattr(self.fs, string+"_shift"))
204 class Test_FlagSet_Constructor(Test_FlagSet):
205 def test_constructor(self):
207 self.assertEqual(self.fs._next_bit, 0)
208 self.assertFalse(self.fs.value_to_str)
211 class Test_Add_Simple_Flag(Test_FlagSet):
212 def test_add_simple_flag(self):
213 """Test that adding a simple flag to an empty FlagSet works"""
214 self.fs.addSimpleFlag("FOO")
215 self.VerifyFlag("FOO", 1)
217 def test_3_add_simple_flag(self):
218 """Test multiple addSimpleFlag calls"""
219 self.fs.addSimpleFlag("FOO")
220 self.fs.addSimpleFlag("BAR")
221 self.fs.addSimpleFlag("BAZ")
223 self.VerifyFlag("FOO", 1)
224 self.VerifyFlag("BAR", 2)
225 self.VerifyFlag("BAZ", 4)
227 self.assertEqual(1, self.fs.FOO)
228 self.assertEqual(2, self.fs.BAR)
229 self.assertEqual(4, self.fs.BAZ)
231 self.assertEqual(0, self.fs.FOO_shift)
232 self.assertEqual(1, self.fs.BAR_shift)
233 self.assertEqual(2, self.fs.BAZ_shift)
235 self.fs._EnsureSorted()
236 # self.assertEqual(self.fs._sorted_strs, ["BAR", "BAZ", "FOO"])
237 self.assertEqual(self.fs._sorted_values, [1, 2, 4])
239 def test_add_simple_flag_with_value(self):
240 """Test addSimpleFlag calls with explicit bit="""
241 self.fs.addSimpleFlag("FOO")
242 self.fs.addSimpleFlag("BAR", bit=1)
243 self.fs.addSimpleFlag("BAZ")
244 self.fs.addSimpleFlag("BLAT", bit=17)
245 self.fs.addSimpleFlag("FROB")
246 self.fs.addSimpleFlag("SNARF", bit=5)
248 self.VerifyFlag("FOO", 1)
249 self.VerifyFlag("BAR", 2)
250 self.VerifyFlag("BAZ", 4)
251 self.VerifyFlag("SNARF", 32)
252 self.VerifyFlag("BLAT", 1<<17)
253 self.VerifyFlag("FROB", 1<<18)
255 self.fs._EnsureSorted()
256 # self.assertEqual(self.fs._sorted_strs,
257 # ["BAR", "BAZ", "BLAT", "FOO", "FROB"])
258 self.assertEqual(self.fs._sorted_values,
259 [1, 2, 4, 32, 1<<17, 1<<18])
262 def test_add_simple_flag_dup_name(self):
263 """Test exception on duplicate flag name"""
264 self.fs.addSimpleFlag("FOO")
265 self.assertRaises(ValueError, self.fs.addSimpleFlag, "FOO")
267 def test_add_simple_flag_dup_value(self):
268 """Test exception on duplicate flag value"""
269 self.fs.addSimpleFlag("FOO")
270 self.fs.addSimpleFlag("BAR", bit=0)
272 self.VerifyFlag("FOO", 1)
273 self.assertEqual(1, self.fs.str_to_value["BAR"])
275 def test_add_shift_duplicated_name(self):
276 """Test that name and name_shift can't both be added"""
277 self.fs.addSimpleFlag("FOO_shift")
278 self.assertRaises(ValueError, self.fs.addSimpleFlag, "FOO")
279 self.assertRaises(ValueError,
280 self.fs.addSimpleFlag, "FOO_shift_shift")
282 def test_attr_name_conflict(self):
283 """Test that adding a flag won't clobber an object attribute"""
284 self.assertRaises(ValueError,
285 self.fs.addSimpleFlag, "addSimpleFlag")
287 class Test_Add_Simple_Flags(Test_FlagSet):
288 def test_add_simple_flags(self):
289 """Test that addSimpleFlags() can add several flags"""
291 self.fs.addSimpleFlags("FOO", "BAR", "BAZ")
292 self.VerifyFlag("FOO", 1)
293 self.VerifyFlag("BAR", 2)
294 self.VerifyFlag("BAZ", 4)
296 class Test_FlagSet_mapping(Test_FlagSet):
298 self.fs = FlagSet(mapping={"FOO": 9, "BAR": 1})
300 def test_constructor(self):
301 self.VerifyFlag("FOO", 1<<9)
302 self.VerifyFlag("BAR", 1<<1)
304 def test_addMap(self):
305 self.fs.addMap({"BAZ": 3, "ZING": 7})
307 self.VerifyFlag("FOO", 1<<9)
308 self.VerifyFlag("BAR", 1<<1)
309 self.VerifyFlag("BAZ", 1<<3)
310 self.VerifyFlag("ZING", 1<<7)
312 class Test_FlagSet_FBBZZ(Test_FlagSet):
313 """FlagSet with certain set of flags"""
316 self.fs.addSimpleFlags("FOO", "BAR", "BAZ")
317 self.fs.addSimpleFlag("ZING", bit=13)
318 self.fs.addSimpleFlag("ZOING", bit=42)
320 def Verify_F2SL(self, expectedstrlist, flags):
321 self.assertEqual(expectedstrlist, self.fs.flagsToStringList(flags))
323 class Test_FlagSet_FBBZZ_flagsToStringList(Test_FlagSet_FBBZZ):
325 self.Verify_F2SL(["FOO"], 1)
327 self.Verify_F2SL(["BAR"], 2)
329 self.Verify_F2SL(["BAZ"], 4)
331 self.Verify_F2SL(["ZING"], 1<<13)
333 self.Verify_F2SL(["ZOING"], 1<<42)
336 self.Verify_F2SL(["FOO", "BAR"], 3)
338 self.Verify_F2SL(["FOO", "BAR", "BAZ"], 7)
340 self.Verify_F2SL(["BAR", "BAZ"], 6)
342 def test_FBBZZ(self):
343 self.Verify_F2SL(["FOO", "BAR", "BAZ", "ZING", "ZOING"],
346 def test_unknownflag(self):
347 self.Verify_F2SL(["0x10"], 0x10)
348 def test_unknownflags(self):
349 self.Verify_F2SL(["0x30"], 0x30)
350 def test_knownandunknownflags(self):
351 self.Verify_F2SL(["FOO", "0x30"], 0x31)