1# Copyright 2017 The Abseil Authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tests for absl.flags used as a package.""" 15 16import contextlib 17import enum 18import io 19import os 20import shutil 21import sys 22import tempfile 23import unittest 24 25from absl import flags 26from absl.flags import _exceptions 27from absl.flags import _helpers 28from absl.flags.tests import module_bar 29from absl.flags.tests import module_baz 30from absl.flags.tests import module_foo 31from absl.testing import absltest 32 33FLAGS = flags.FLAGS 34 35 36@contextlib.contextmanager 37def _use_gnu_getopt(flag_values, use_gnu_get_opt): 38 old_use_gnu_get_opt = flag_values.is_gnu_getopt() 39 flag_values.set_gnu_getopt(use_gnu_get_opt) 40 yield 41 flag_values.set_gnu_getopt(old_use_gnu_get_opt) 42 43 44class FlagDictToArgsTest(absltest.TestCase): 45 46 def test_flatten_google_flag_map(self): 47 arg_dict = { 48 'week-end': None, 49 'estudia': False, 50 'trabaja': False, 51 'party': True, 52 'monday': 'party', 53 'score': 42, 54 'loadthatstuff': [42, 'hello', 'goodbye'], 55 } 56 self.assertSameElements( 57 ('--week-end', '--noestudia', '--notrabaja', '--party', 58 '--monday=party', '--score=42', '--loadthatstuff=42,hello,goodbye'), 59 flags.flag_dict_to_args(arg_dict)) 60 61 def test_flatten_google_flag_map_with_multi_flag(self): 62 arg_dict = { 63 'some_list': ['value1', 'value2'], 64 'some_multi_string': ['value3', 'value4'], 65 } 66 self.assertSameElements( 67 ('--some_list=value1,value2', '--some_multi_string=value3', 68 '--some_multi_string=value4'), 69 flags.flag_dict_to_args(arg_dict, multi_flags={'some_multi_string'})) 70 71 72class Fruit(enum.Enum): 73 APPLE = object() 74 ORANGE = object() 75 76 77class CaseSensitiveFruit(enum.Enum): 78 apple = 1 79 orange = 2 80 APPLE = 3 81 82 83class EmptyEnum(enum.Enum): 84 pass 85 86 87class AliasFlagsTest(absltest.TestCase): 88 89 def setUp(self): 90 super(AliasFlagsTest, self).setUp() 91 self.flags = flags.FlagValues() 92 93 @property 94 def alias(self): 95 return self.flags['alias'] 96 97 @property 98 def aliased(self): 99 return self.flags['aliased'] 100 101 def define_alias(self, *args, **kwargs): 102 flags.DEFINE_alias(*args, flag_values=self.flags, **kwargs) 103 104 def define_integer(self, *args, **kwargs): 105 flags.DEFINE_integer(*args, flag_values=self.flags, **kwargs) 106 107 def define_multi_integer(self, *args, **kwargs): 108 flags.DEFINE_multi_integer(*args, flag_values=self.flags, **kwargs) 109 110 def define_string(self, *args, **kwargs): 111 flags.DEFINE_string(*args, flag_values=self.flags, **kwargs) 112 113 def assert_alias_mirrors_aliased(self, alias, aliased, ignore_due_to_bug=()): 114 # A few sanity checks to avoid false success 115 self.assertIn('FlagAlias', alias.__class__.__qualname__) 116 self.assertIsNot(alias, aliased) 117 self.assertNotEqual(aliased.name, alias.name) 118 119 alias_state = {} 120 aliased_state = {} 121 attrs = { 122 'allow_hide_cpp', 123 'allow_override', 124 'allow_override_cpp', 125 'allow_overwrite', 126 'allow_using_method_names', 127 'boolean', 128 'default', 129 'default_as_str', 130 'default_unparsed', 131 # TODO(rlevasseur): This should match, but a bug prevents it from being 132 # in sync. 133 # 'using_default_value', 134 'value', 135 } 136 attrs.difference_update(ignore_due_to_bug) 137 138 for attr in attrs: 139 alias_state[attr] = getattr(alias, attr) 140 aliased_state[attr] = getattr(aliased, attr) 141 142 self.assertEqual(aliased_state, alias_state, 'LHS is aliased; RHS is alias') 143 144 def test_serialize_multi(self): 145 self.define_multi_integer('aliased', [0, 1], '') 146 self.define_alias('alias', 'aliased') 147 148 actual = self.alias.serialize() 149 # TODO(rlevasseur): This should check for --alias=0\n--alias=1, but 150 # a bug causes it to serialize incorrectly. 151 self.assertEqual('--alias=[0, 1]', actual) 152 153 def test_allow_overwrite_false(self): 154 self.define_integer('aliased', None, 'help', allow_overwrite=False) 155 self.define_alias('alias', 'aliased') 156 157 with self.assertRaisesRegex(flags.IllegalFlagValueError, 'already defined'): 158 self.flags(['./program', '--alias=1', '--aliased=2']) 159 160 self.assertEqual(1, self.alias.value) 161 self.assertEqual(1, self.aliased.value) 162 163 def test_aliasing_multi_no_default(self): 164 165 def define_flags(): 166 self.flags = flags.FlagValues() 167 self.define_multi_integer('aliased', None, 'help') 168 self.define_alias('alias', 'aliased') 169 170 with self.subTest('after defining'): 171 define_flags() 172 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 173 self.assertIsNone(self.alias.value) 174 175 with self.subTest('set alias'): 176 define_flags() 177 self.flags(['./program', '--alias=1', '--alias=2']) 178 self.assertEqual([1, 2], self.alias.value) 179 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 180 181 with self.subTest('set aliased'): 182 define_flags() 183 self.flags(['./program', '--aliased=1', '--aliased=2']) 184 self.assertEqual([1, 2], self.alias.value) 185 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 186 187 with self.subTest('not setting anything'): 188 define_flags() 189 self.flags(['./program']) 190 self.assertEqual(None, self.alias.value) 191 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 192 193 def test_aliasing_multi_with_default(self): 194 195 def define_flags(): 196 self.flags = flags.FlagValues() 197 self.define_multi_integer('aliased', [0], 'help') 198 self.define_alias('alias', 'aliased') 199 200 with self.subTest('after defining'): 201 define_flags() 202 self.assertEqual([0], self.alias.default) 203 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 204 205 with self.subTest('set alias'): 206 define_flags() 207 self.flags(['./program', '--alias=1', '--alias=2']) 208 self.assertEqual([1, 2], self.alias.value) 209 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 210 211 self.assertEqual(2, self.alias.present) 212 # TODO(rlevasseur): This should assert 0, but a bug with aliases and 213 # MultiFlag causes the alias to increment aliased's present counter. 214 self.assertEqual(2, self.aliased.present) 215 216 with self.subTest('set aliased'): 217 define_flags() 218 self.flags(['./program', '--aliased=1', '--aliased=2']) 219 self.assertEqual([1, 2], self.alias.value) 220 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 221 self.assertEqual(0, self.alias.present) 222 223 # TODO(rlevasseur): This should assert 0, but a bug with aliases and 224 # MultiFlag causes the alias to increment aliased present counter. 225 self.assertEqual(2, self.aliased.present) 226 227 with self.subTest('not setting anything'): 228 define_flags() 229 self.flags(['./program']) 230 self.assertEqual([0], self.alias.value) 231 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 232 self.assertEqual(0, self.alias.present) 233 self.assertEqual(0, self.aliased.present) 234 235 def test_aliasing_regular(self): 236 237 def define_flags(): 238 self.flags = flags.FlagValues() 239 self.define_string('aliased', '', 'help') 240 self.define_alias('alias', 'aliased') 241 242 define_flags() 243 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 244 245 self.flags(['./program', '--alias=1']) 246 self.assertEqual('1', self.alias.value) 247 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 248 self.assertEqual(1, self.alias.present) 249 self.assertEqual('--alias=1', self.alias.serialize()) 250 self.assertEqual(1, self.aliased.present) 251 252 define_flags() 253 self.flags(['./program', '--aliased=2']) 254 self.assertEqual('2', self.alias.value) 255 self.assert_alias_mirrors_aliased(self.alias, self.aliased) 256 self.assertEqual(0, self.alias.present) 257 self.assertEqual('--alias=2', self.alias.serialize()) 258 self.assertEqual(1, self.aliased.present) 259 260 def test_defining_alias_doesnt_affect_aliased_state_regular(self): 261 self.define_string('aliased', 'default', 'help') 262 self.define_alias('alias', 'aliased') 263 264 self.assertEqual(0, self.aliased.present) 265 self.assertEqual(0, self.alias.present) 266 267 def test_defining_alias_doesnt_affect_aliased_state_multi(self): 268 self.define_multi_integer('aliased', [0], 'help') 269 self.define_alias('alias', 'aliased') 270 271 self.assertEqual([0], self.aliased.value) 272 self.assertEqual([0], self.aliased.default) 273 self.assertEqual(0, self.aliased.present) 274 275 self.assertEqual([0], self.aliased.value) 276 self.assertEqual([0], self.aliased.default) 277 self.assertEqual(0, self.alias.present) 278 279 280class FlagsUnitTest(absltest.TestCase): 281 """Flags Unit Test.""" 282 283 maxDiff = None 284 285 def test_flags(self): 286 """Test normal usage with no (expected) errors.""" 287 # Define flags 288 number_test_framework_flags = len(FLAGS) 289 repeat_help = 'how many times to repeat (0-5)' 290 flags.DEFINE_integer( 291 'repeat', 4, repeat_help, lower_bound=0, short_name='r') 292 flags.DEFINE_string('name', 'Bob', 'namehelp') 293 flags.DEFINE_boolean('debug', 0, 'debughelp') 294 flags.DEFINE_boolean('q', 1, 'quiet mode') 295 flags.DEFINE_boolean('quack', 0, "superstring of 'q'") 296 flags.DEFINE_boolean('noexec', 1, 'boolean flag with no as prefix') 297 flags.DEFINE_float('float', 3.14, 'using floats') 298 flags.DEFINE_integer('octal', '0o666', 'using octals') 299 flags.DEFINE_integer('decimal', '666', 'using decimals') 300 flags.DEFINE_integer('hexadecimal', '0x666', 'using hexadecimals') 301 flags.DEFINE_integer('x', 3, 'how eXtreme to be') 302 flags.DEFINE_integer('l', 0x7fffffff00000000, 'how long to be') 303 flags.DEFINE_list('args', 'v=1,"vmodule=a=0,b=2"', 'a list of arguments') 304 flags.DEFINE_list('letters', 'a,b,c', 'a list of letters') 305 flags.DEFINE_list( 306 'list_default_list', 307 ['a', 'b', 'c'], 308 'with default being a list of strings', 309 ) 310 flags.DEFINE_enum('kwery', None, ['who', 'what', 'Why', 'where', 'when'], 311 '?') 312 flags.DEFINE_enum( 313 'sense', None, ['Case', 'case', 'CASE'], '?', case_sensitive=True) 314 flags.DEFINE_enum( 315 'cases', 316 None, ['UPPER', 'lower', 'Initial', 'Ot_HeR'], 317 '?', 318 case_sensitive=False) 319 flags.DEFINE_enum( 320 'funny', 321 None, ['Joke', 'ha', 'ha', 'ha', 'ha'], 322 '?', 323 case_sensitive=True) 324 flags.DEFINE_enum( 325 'blah', 326 None, ['bla', 'Blah', 'BLAH', 'blah'], 327 '?', 328 case_sensitive=False) 329 flags.DEFINE_string( 330 'only_once', None, 'test only sets this once', allow_overwrite=False) 331 flags.DEFINE_string( 332 'universe', 333 None, 334 'test tries to set this three times', 335 allow_overwrite=False) 336 337 # Specify number of flags defined above. The short_name defined 338 # for 'repeat' counts as an extra flag. 339 number_defined_flags = 22 + 1 340 self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags) 341 342 self.assertEqual(FLAGS.repeat, 4) 343 self.assertEqual(FLAGS.name, 'Bob') 344 self.assertEqual(FLAGS.debug, 0) 345 self.assertEqual(FLAGS.q, 1) 346 self.assertEqual(FLAGS.octal, 0o666) 347 self.assertEqual(FLAGS.decimal, 666) 348 self.assertEqual(FLAGS.hexadecimal, 0x666) 349 self.assertEqual(FLAGS.x, 3) 350 self.assertEqual(FLAGS.l, 0x7fffffff00000000) 351 self.assertEqual(FLAGS.args, ['v=1', 'vmodule=a=0,b=2']) 352 self.assertEqual(FLAGS.letters, ['a', 'b', 'c']) 353 self.assertEqual(FLAGS.list_default_list, ['a', 'b', 'c']) 354 self.assertIsNone(FLAGS.kwery) 355 self.assertIsNone(FLAGS.sense) 356 self.assertIsNone(FLAGS.cases) 357 self.assertIsNone(FLAGS.funny) 358 self.assertIsNone(FLAGS.blah) 359 360 flag_values = FLAGS.flag_values_dict() 361 self.assertEqual(flag_values['repeat'], 4) 362 self.assertEqual(flag_values['name'], 'Bob') 363 self.assertEqual(flag_values['debug'], 0) 364 self.assertEqual(flag_values['r'], 4) # Short for repeat. 365 self.assertEqual(flag_values['q'], 1) 366 self.assertEqual(flag_values['quack'], 0) 367 self.assertEqual(flag_values['x'], 3) 368 self.assertEqual(flag_values['l'], 0x7fffffff00000000) 369 self.assertEqual(flag_values['args'], ['v=1', 'vmodule=a=0,b=2']) 370 self.assertEqual(flag_values['letters'], ['a', 'b', 'c']) 371 self.assertEqual(flag_values['list_default_list'], ['a', 'b', 'c']) 372 self.assertIsNone(flag_values['kwery']) 373 self.assertIsNone(flag_values['sense']) 374 self.assertIsNone(flag_values['cases']) 375 self.assertIsNone(flag_values['funny']) 376 self.assertIsNone(flag_values['blah']) 377 378 # Verify string form of defaults 379 self.assertEqual(FLAGS['repeat'].default_as_str, "'4'") 380 self.assertEqual(FLAGS['name'].default_as_str, "'Bob'") 381 self.assertEqual(FLAGS['debug'].default_as_str, "'false'") 382 self.assertEqual(FLAGS['q'].default_as_str, "'true'") 383 self.assertEqual(FLAGS['quack'].default_as_str, "'false'") 384 self.assertEqual(FLAGS['noexec'].default_as_str, "'true'") 385 self.assertEqual(FLAGS['x'].default_as_str, "'3'") 386 self.assertEqual(FLAGS['l'].default_as_str, "'9223372032559808512'") 387 self.assertEqual(FLAGS['args'].default_as_str, '\'v=1,"vmodule=a=0,b=2"\'') 388 self.assertEqual(FLAGS['letters'].default_as_str, "'a,b,c'") 389 self.assertEqual(FLAGS['list_default_list'].default_as_str, "'a,b,c'") 390 391 # Verify that the iterator for flags yields all the keys 392 keys = list(FLAGS) 393 keys.sort() 394 reg_flags = list(FLAGS._flags()) 395 reg_flags.sort() 396 self.assertEqual(keys, reg_flags) 397 398 # Parse flags 399 # .. empty command line 400 argv = ('./program',) 401 argv = FLAGS(argv) 402 self.assertLen(argv, 1, 'wrong number of arguments pulled') 403 self.assertEqual(argv[0], './program', 'program name not preserved') 404 405 # .. non-empty command line 406 argv = ('./program', '--debug', '--name=Bob', '-q', '--x=8') 407 argv = FLAGS(argv) 408 self.assertLen(argv, 1, 'wrong number of arguments pulled') 409 self.assertEqual(argv[0], './program', 'program name not preserved') 410 self.assertEqual(FLAGS['debug'].present, 1) 411 FLAGS['debug'].present = 0 # Reset 412 self.assertEqual(FLAGS['name'].present, 1) 413 FLAGS['name'].present = 0 # Reset 414 self.assertEqual(FLAGS['q'].present, 1) 415 FLAGS['q'].present = 0 # Reset 416 self.assertEqual(FLAGS['x'].present, 1) 417 FLAGS['x'].present = 0 # Reset 418 419 # Flags list. 420 self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags) 421 self.assertIn('name', FLAGS) 422 self.assertIn('debug', FLAGS) 423 self.assertIn('repeat', FLAGS) 424 self.assertIn('r', FLAGS) 425 self.assertIn('q', FLAGS) 426 self.assertIn('quack', FLAGS) 427 self.assertIn('x', FLAGS) 428 self.assertIn('l', FLAGS) 429 self.assertIn('args', FLAGS) 430 self.assertIn('letters', FLAGS) 431 self.assertIn('list_default_list', FLAGS) 432 433 # __contains__ 434 self.assertIn('name', FLAGS) 435 self.assertNotIn('name2', FLAGS) 436 437 # try deleting a flag 438 del FLAGS.r 439 self.assertLen(FLAGS, 440 number_defined_flags - 1 + number_test_framework_flags) 441 self.assertNotIn('r', FLAGS) 442 443 # .. command line with extra stuff 444 argv = ('./program', '--debug', '--name=Bob', 'extra') 445 argv = FLAGS(argv) 446 self.assertLen(argv, 2, 'wrong number of arguments pulled') 447 self.assertEqual(argv[0], './program', 'program name not preserved') 448 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 449 self.assertEqual(FLAGS['debug'].present, 1) 450 FLAGS['debug'].present = 0 # Reset 451 self.assertEqual(FLAGS['name'].present, 1) 452 FLAGS['name'].present = 0 # Reset 453 454 # Test reset 455 argv = ('./program', '--debug') 456 argv = FLAGS(argv) 457 self.assertLen(argv, 1, 'wrong number of arguments pulled') 458 self.assertEqual(argv[0], './program', 'program name not preserved') 459 self.assertEqual(FLAGS['debug'].present, 1) 460 self.assertTrue(FLAGS['debug'].value) 461 FLAGS.unparse_flags() 462 self.assertEqual(FLAGS['debug'].present, 0) 463 self.assertFalse(FLAGS['debug'].value) 464 465 # Test that reset restores default value when default value is None. 466 argv = ('./program', '--kwery=who') 467 argv = FLAGS(argv) 468 self.assertLen(argv, 1, 'wrong number of arguments pulled') 469 self.assertEqual(argv[0], './program', 'program name not preserved') 470 self.assertEqual(FLAGS['kwery'].present, 1) 471 self.assertEqual(FLAGS['kwery'].value, 'who') 472 FLAGS.unparse_flags() 473 argv = ('./program', '--kwery=Why') 474 argv = FLAGS(argv) 475 self.assertLen(argv, 1, 'wrong number of arguments pulled') 476 self.assertEqual(argv[0], './program', 'program name not preserved') 477 self.assertEqual(FLAGS['kwery'].present, 1) 478 self.assertEqual(FLAGS['kwery'].value, 'Why') 479 FLAGS.unparse_flags() 480 self.assertEqual(FLAGS['kwery'].present, 0) 481 self.assertIsNone(FLAGS['kwery'].value) 482 483 # Test case sensitive enum. 484 argv = ('./program', '--sense=CASE') 485 argv = FLAGS(argv) 486 self.assertLen(argv, 1, 'wrong number of arguments pulled') 487 self.assertEqual(argv[0], './program', 'program name not preserved') 488 self.assertEqual(FLAGS['sense'].present, 1) 489 self.assertEqual(FLAGS['sense'].value, 'CASE') 490 FLAGS.unparse_flags() 491 argv = ('./program', '--sense=Case') 492 argv = FLAGS(argv) 493 self.assertLen(argv, 1, 'wrong number of arguments pulled') 494 self.assertEqual(argv[0], './program', 'program name not preserved') 495 self.assertEqual(FLAGS['sense'].present, 1) 496 self.assertEqual(FLAGS['sense'].value, 'Case') 497 FLAGS.unparse_flags() 498 499 # Test case insensitive enum. 500 argv = ('./program', '--cases=upper') 501 argv = FLAGS(argv) 502 self.assertLen(argv, 1, 'wrong number of arguments pulled') 503 self.assertEqual(argv[0], './program', 'program name not preserved') 504 self.assertEqual(FLAGS['cases'].present, 1) 505 self.assertEqual(FLAGS['cases'].value, 'UPPER') 506 FLAGS.unparse_flags() 507 508 # Test case sensitive enum with duplicates. 509 argv = ('./program', '--funny=ha') 510 argv = FLAGS(argv) 511 self.assertLen(argv, 1, 'wrong number of arguments pulled') 512 self.assertEqual(argv[0], './program', 'program name not preserved') 513 self.assertEqual(FLAGS['funny'].present, 1) 514 self.assertEqual(FLAGS['funny'].value, 'ha') 515 FLAGS.unparse_flags() 516 517 # Test case insensitive enum with duplicates. 518 argv = ('./program', '--blah=bLah') 519 argv = FLAGS(argv) 520 self.assertLen(argv, 1, 'wrong number of arguments pulled') 521 self.assertEqual(argv[0], './program', 'program name not preserved') 522 self.assertEqual(FLAGS['blah'].present, 1) 523 self.assertEqual(FLAGS['blah'].value, 'Blah') 524 FLAGS.unparse_flags() 525 argv = ('./program', '--blah=BLAH') 526 argv = FLAGS(argv) 527 self.assertLen(argv, 1, 'wrong number of arguments pulled') 528 self.assertEqual(argv[0], './program', 'program name not preserved') 529 self.assertEqual(FLAGS['blah'].present, 1) 530 self.assertEqual(FLAGS['blah'].value, 'Blah') 531 FLAGS.unparse_flags() 532 533 # Test integer argument passing 534 argv = ('./program', '--x', '0x12345') 535 argv = FLAGS(argv) 536 self.assertEqual(FLAGS.x, 0x12345) 537 self.assertEqual(type(FLAGS.x), int) 538 539 argv = ('./program', '--x', '0x1234567890ABCDEF1234567890ABCDEF') 540 argv = FLAGS(argv) 541 self.assertEqual(FLAGS.x, 0x1234567890ABCDEF1234567890ABCDEF) 542 self.assertIsInstance(FLAGS.x, int) 543 544 argv = ('./program', '--x', '0o12345') 545 argv = FLAGS(argv) 546 self.assertEqual(FLAGS.x, 0o12345) 547 self.assertEqual(type(FLAGS.x), int) 548 549 # Treat 0-prefixed parameters as base-10, not base-8 550 argv = ('./program', '--x', '012345') 551 argv = FLAGS(argv) 552 self.assertEqual(FLAGS.x, 12345) 553 self.assertEqual(type(FLAGS.x), int) 554 555 argv = ('./program', '--x', '0123459') 556 argv = FLAGS(argv) 557 self.assertEqual(FLAGS.x, 123459) 558 self.assertEqual(type(FLAGS.x), int) 559 560 argv = ('./program', '--x', '0x123efg') 561 with self.assertRaises(flags.IllegalFlagValueError): 562 argv = FLAGS(argv) 563 564 # Test boolean argument parsing 565 flags.DEFINE_boolean('test0', None, 'test boolean parsing') 566 argv = ('./program', '--notest0') 567 argv = FLAGS(argv) 568 self.assertEqual(FLAGS.test0, 0) 569 570 flags.DEFINE_boolean('test1', None, 'test boolean parsing') 571 argv = ('./program', '--test1') 572 argv = FLAGS(argv) 573 self.assertEqual(FLAGS.test1, 1) 574 575 FLAGS.test0 = None 576 argv = ('./program', '--test0=false') 577 argv = FLAGS(argv) 578 self.assertEqual(FLAGS.test0, 0) 579 580 FLAGS.test1 = None 581 argv = ('./program', '--test1=true') 582 argv = FLAGS(argv) 583 self.assertEqual(FLAGS.test1, 1) 584 585 FLAGS.test0 = None 586 argv = ('./program', '--test0=0') 587 argv = FLAGS(argv) 588 self.assertEqual(FLAGS.test0, 0) 589 590 FLAGS.test1 = None 591 argv = ('./program', '--test1=1') 592 argv = FLAGS(argv) 593 self.assertEqual(FLAGS.test1, 1) 594 595 # Test booleans that already have 'no' as a prefix 596 FLAGS.noexec = None 597 argv = ('./program', '--nonoexec', '--name', 'Bob') 598 argv = FLAGS(argv) 599 self.assertEqual(FLAGS.noexec, 0) 600 601 FLAGS.noexec = None 602 argv = ('./program', '--name', 'Bob', '--noexec') 603 argv = FLAGS(argv) 604 self.assertEqual(FLAGS.noexec, 1) 605 606 # Test unassigned booleans 607 flags.DEFINE_boolean('testnone', None, 'test boolean parsing') 608 argv = ('./program',) 609 argv = FLAGS(argv) 610 self.assertIsNone(FLAGS.testnone) 611 612 # Test get with default 613 flags.DEFINE_boolean('testget1', None, 'test parsing with defaults') 614 flags.DEFINE_boolean('testget2', None, 'test parsing with defaults') 615 flags.DEFINE_boolean('testget3', None, 'test parsing with defaults') 616 flags.DEFINE_integer('testget4', None, 'test parsing with defaults') 617 argv = ('./program', '--testget1', '--notestget2') 618 argv = FLAGS(argv) 619 self.assertEqual(FLAGS.get_flag_value('testget1', 'foo'), 1) 620 self.assertEqual(FLAGS.get_flag_value('testget2', 'foo'), 0) 621 self.assertEqual(FLAGS.get_flag_value('testget3', 'foo'), 'foo') 622 self.assertEqual(FLAGS.get_flag_value('testget4', 'foo'), 'foo') 623 624 # test list code 625 lists = [['hello', 'moo', 'boo', '1'], []] 626 627 flags.DEFINE_list('testcomma_list', '', 'test comma list parsing') 628 flags.DEFINE_spaceseplist('testspace_list', '', 'tests space list parsing') 629 flags.DEFINE_spaceseplist( 630 'testspace_or_comma_list', 631 '', 632 'tests space list parsing with comma compatibility', 633 comma_compat=True) 634 635 for name, sep in (('testcomma_list', ','), ('testspace_list', 636 ' '), ('testspace_list', '\n'), 637 ('testspace_or_comma_list', 638 ' '), ('testspace_or_comma_list', 639 '\n'), ('testspace_or_comma_list', ',')): 640 for lst in lists: 641 argv = ('./program', '--%s=%s' % (name, sep.join(lst))) 642 argv = FLAGS(argv) 643 self.assertEqual(getattr(FLAGS, name), lst) 644 645 # Test help text 646 flags_help = str(FLAGS) 647 self.assertNotEqual( 648 flags_help.find('repeat'), -1, 'cannot find flag in help') 649 self.assertNotEqual( 650 flags_help.find(repeat_help), -1, 'cannot find help string in help') 651 652 # Test flag specified twice 653 argv = ('./program', '--repeat=4', '--repeat=2', '--debug', '--nodebug') 654 argv = FLAGS(argv) 655 self.assertEqual(FLAGS.get_flag_value('repeat', None), 2) 656 self.assertEqual(FLAGS.get_flag_value('debug', None), 0) 657 658 # Test MultiFlag with single default value 659 flags.DEFINE_multi_string( 660 's_str', 661 'sing1', 662 'string option that can occur multiple times', 663 short_name='s') 664 self.assertEqual(FLAGS.get_flag_value('s_str', None), ['sing1']) 665 666 # Test MultiFlag with list of default values 667 multi_string_defs = ['def1', 'def2'] 668 flags.DEFINE_multi_string( 669 'm_str', 670 multi_string_defs, 671 'string option that can occur multiple times', 672 short_name='m') 673 self.assertEqual(FLAGS.get_flag_value('m_str', None), multi_string_defs) 674 675 # Test flag specified multiple times with a MultiFlag 676 argv = ('./program', '--m_str=str1', '-m', 'str2') 677 argv = FLAGS(argv) 678 self.assertEqual(FLAGS.get_flag_value('m_str', None), ['str1', 'str2']) 679 680 # A flag with allow_overwrite set to False should behave normally when it 681 # is only specified once 682 argv = ('./program', '--only_once=singlevalue') 683 argv = FLAGS(argv) 684 self.assertEqual(FLAGS.get_flag_value('only_once', None), 'singlevalue') 685 686 # A flag with allow_overwrite set to False should complain when it is 687 # specified more than once 688 argv = ('./program', '--universe=ptolemaic', '--universe=copernicean', 689 '--universe=euclidean') 690 self.assertRaisesWithLiteralMatch( 691 flags.IllegalFlagValueError, 692 'flag --universe=copernicean: already defined as ptolemaic', FLAGS, 693 argv) 694 695 # A flag value error shouldn't modify the value: 696 flags.DEFINE_integer('smol', 1, 'smol flag', upper_bound=5) 697 with self.assertRaises(flags.IllegalFlagValueError): 698 FLAGS.smol = 6 699 self.assertEqual(FLAGS.smol, 1) 700 self.assertTrue(FLAGS['smol'].using_default_value) 701 702 # Test single-letter flags; should support both single and double dash 703 argv = ('./program', '-q') 704 argv = FLAGS(argv) 705 self.assertEqual(FLAGS.get_flag_value('q', None), 1) 706 707 argv = ('./program', '--q', '--x', '9', '--noquack') 708 argv = FLAGS(argv) 709 self.assertEqual(FLAGS.get_flag_value('q', None), 1) 710 self.assertEqual(FLAGS.get_flag_value('x', None), 9) 711 self.assertEqual(FLAGS.get_flag_value('quack', None), 0) 712 713 argv = ('./program', '--noq', '--x=10', '--quack') 714 argv = FLAGS(argv) 715 self.assertEqual(FLAGS.get_flag_value('q', None), 0) 716 self.assertEqual(FLAGS.get_flag_value('x', None), 10) 717 self.assertEqual(FLAGS.get_flag_value('quack', None), 1) 718 719 #################################### 720 # Test flag serialization code: 721 722 old_testcomma_list = FLAGS.testcomma_list 723 old_testspace_list = FLAGS.testspace_list 724 old_testspace_or_comma_list = FLAGS.testspace_or_comma_list 725 726 argv = ('./program', FLAGS['test0'].serialize(), FLAGS['test1'].serialize(), 727 FLAGS['s_str'].serialize()) 728 729 argv = FLAGS(argv) 730 self.assertEqual(FLAGS['test0'].serialize(), '--notest0') 731 self.assertEqual(FLAGS['test1'].serialize(), '--test1') 732 self.assertEqual(FLAGS['s_str'].serialize(), '--s_str=sing1') 733 734 self.assertEqual(FLAGS['testnone'].serialize(), '') 735 736 testcomma_list1 = ['aa', 'bb'] 737 testspace_list1 = ['aa', 'bb', 'cc'] 738 testspace_or_comma_list1 = ['aa', 'bb', 'cc', 'dd'] 739 FLAGS.testcomma_list = list(testcomma_list1) 740 FLAGS.testspace_list = list(testspace_list1) 741 FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1) 742 argv = ('./program', FLAGS['testcomma_list'].serialize(), 743 FLAGS['testspace_list'].serialize(), 744 FLAGS['testspace_or_comma_list'].serialize()) 745 argv = FLAGS(argv) 746 self.assertEqual(FLAGS.testcomma_list, testcomma_list1) 747 self.assertEqual(FLAGS.testspace_list, testspace_list1) 748 self.assertEqual(FLAGS.testspace_or_comma_list, testspace_or_comma_list1) 749 750 testcomma_list1 = ['aa some spaces', 'bb'] 751 testspace_list1 = ['aa', 'bb,some,commas,', 'cc'] 752 testspace_or_comma_list1 = ['aa', 'bb,some,commas,', 'cc'] 753 FLAGS.testcomma_list = list(testcomma_list1) 754 FLAGS.testspace_list = list(testspace_list1) 755 FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1) 756 argv = ('./program', FLAGS['testcomma_list'].serialize(), 757 FLAGS['testspace_list'].serialize(), 758 FLAGS['testspace_or_comma_list'].serialize()) 759 argv = FLAGS(argv) 760 self.assertEqual(FLAGS.testcomma_list, testcomma_list1) 761 self.assertEqual(FLAGS.testspace_list, testspace_list1) 762 # We don't expect idempotency when commas are placed in an item value and 763 # comma_compat is enabled. 764 self.assertEqual(FLAGS.testspace_or_comma_list, 765 ['aa', 'bb', 'some', 'commas', 'cc']) 766 767 FLAGS.testcomma_list = old_testcomma_list 768 FLAGS.testspace_list = old_testspace_list 769 FLAGS.testspace_or_comma_list = old_testspace_or_comma_list 770 771 #################################### 772 # Test flag-update: 773 774 def args_list(): 775 # Exclude flags that have different default values based on the 776 # environment. 777 flags_to_exclude = {'log_dir', 'test_srcdir', 'test_tmpdir'} 778 flagnames = set(FLAGS) - flags_to_exclude 779 780 nonbool_flags = [] 781 truebool_flags = [] 782 falsebool_flags = [] 783 for name in flagnames: 784 flag_value = FLAGS.get_flag_value(name, None) 785 if not isinstance(FLAGS[name], flags.BooleanFlag): 786 nonbool_flags.append('--%s %s' % (name, flag_value)) 787 elif flag_value: 788 truebool_flags.append('--%s' % name) 789 else: 790 falsebool_flags.append('--no%s' % name) 791 all_flags = nonbool_flags + truebool_flags + falsebool_flags 792 all_flags.sort() 793 return all_flags 794 795 argv = ('./program', '--repeat=3', '--name=giants', '--nodebug') 796 797 FLAGS(argv) 798 self.assertEqual(FLAGS.get_flag_value('repeat', None), 3) 799 self.assertEqual(FLAGS.get_flag_value('name', None), 'giants') 800 self.assertEqual(FLAGS.get_flag_value('debug', None), 0) 801 self.assertListEqual( 802 [ 803 '--alsologtostderr', 804 "--args ['v=1', 'vmodule=a=0,b=2']", 805 '--blah None', 806 '--cases None', 807 '--decimal 666', 808 '--float 3.14', 809 '--funny None', 810 '--hexadecimal 1638', 811 '--kwery None', 812 '--l 9223372032559808512', 813 "--letters ['a', 'b', 'c']", 814 "--list_default_list ['a', 'b', 'c']", 815 '--logger_levels {}', 816 "--m ['str1', 'str2']", 817 "--m_str ['str1', 'str2']", 818 '--name giants', 819 '--no?', 820 '--nodebug', 821 '--noexec', 822 '--nohelp', 823 '--nohelpfull', 824 '--nohelpshort', 825 '--nohelpxml', 826 '--nologtostderr', 827 '--noonly_check_args', 828 '--nopdb_post_mortem', 829 '--noq', 830 '--norun_with_pdb', 831 '--norun_with_profiling', 832 '--notest0', 833 '--notestget2', 834 '--notestget3', 835 '--notestnone', 836 '--octal 438', 837 '--only_once singlevalue', 838 '--pdb False', 839 '--profile_file None', 840 '--quack', 841 '--repeat 3', 842 "--s ['sing1']", 843 "--s_str ['sing1']", 844 '--sense None', 845 '--showprefixforinfo', 846 '--smol 1', 847 '--stderrthreshold fatal', 848 '--test1', 849 '--test_random_seed 301', 850 '--test_randomize_ordering_seed ', 851 '--testcomma_list []', 852 '--testget1', 853 '--testget4 None', 854 '--testspace_list []', 855 '--testspace_or_comma_list []', 856 '--tmod_baz_x', 857 '--universe ptolemaic', 858 '--use_cprofile_for_profiling', 859 '--v -1', 860 '--verbosity -1', 861 '--x 10', 862 '--xml_output_file ', 863 ], 864 args_list(), 865 ) 866 867 argv = ('./program', '--debug', '--m_str=upd1', '-s', 'upd2') 868 FLAGS(argv) 869 self.assertEqual(FLAGS.get_flag_value('repeat', None), 3) 870 self.assertEqual(FLAGS.get_flag_value('name', None), 'giants') 871 self.assertEqual(FLAGS.get_flag_value('debug', None), 1) 872 873 # items appended to existing non-default value lists for --m/--m_str 874 # new value overwrites default value (not appended to it) for --s/--s_str 875 self.assertListEqual( 876 [ 877 '--alsologtostderr', 878 "--args ['v=1', 'vmodule=a=0,b=2']", 879 '--blah None', 880 '--cases None', 881 '--debug', 882 '--decimal 666', 883 '--float 3.14', 884 '--funny None', 885 '--hexadecimal 1638', 886 '--kwery None', 887 '--l 9223372032559808512', 888 "--letters ['a', 'b', 'c']", 889 "--list_default_list ['a', 'b', 'c']", 890 '--logger_levels {}', 891 "--m ['str1', 'str2', 'upd1']", 892 "--m_str ['str1', 'str2', 'upd1']", 893 '--name giants', 894 '--no?', 895 '--noexec', 896 '--nohelp', 897 '--nohelpfull', 898 '--nohelpshort', 899 '--nohelpxml', 900 '--nologtostderr', 901 '--noonly_check_args', 902 '--nopdb_post_mortem', 903 '--noq', 904 '--norun_with_pdb', 905 '--norun_with_profiling', 906 '--notest0', 907 '--notestget2', 908 '--notestget3', 909 '--notestnone', 910 '--octal 438', 911 '--only_once singlevalue', 912 '--pdb False', 913 '--profile_file None', 914 '--quack', 915 '--repeat 3', 916 "--s ['sing1', 'upd2']", 917 "--s_str ['sing1', 'upd2']", 918 '--sense None', 919 '--showprefixforinfo', 920 '--smol 1', 921 '--stderrthreshold fatal', 922 '--test1', 923 '--test_random_seed 301', 924 '--test_randomize_ordering_seed ', 925 '--testcomma_list []', 926 '--testget1', 927 '--testget4 None', 928 '--testspace_list []', 929 '--testspace_or_comma_list []', 930 '--tmod_baz_x', 931 '--universe ptolemaic', 932 '--use_cprofile_for_profiling', 933 '--v -1', 934 '--verbosity -1', 935 '--x 10', 936 '--xml_output_file ', 937 ], 938 args_list(), 939 ) 940 941 #################################### 942 # Test all kind of error conditions. 943 944 # Argument not in enum exception 945 argv = ('./program', '--kwery=WHEN') 946 self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv) 947 argv = ('./program', '--kwery=why') 948 self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv) 949 950 # Duplicate flag detection 951 with self.assertRaises(flags.DuplicateFlagError): 952 flags.DEFINE_boolean('run', 0, 'runhelp', short_name='q') 953 954 # Duplicate short flag detection 955 with self.assertRaisesRegex( 956 flags.DuplicateFlagError, 957 r"The flag 'z' is defined twice\. .*First from.*, Second from"): 958 flags.DEFINE_boolean('zoom1', 0, 'runhelp z1', short_name='z') 959 flags.DEFINE_boolean('zoom2', 0, 'runhelp z2', short_name='z') 960 raise AssertionError('duplicate short flag detection failed') 961 962 # Duplicate mixed flag detection 963 with self.assertRaisesRegex( 964 flags.DuplicateFlagError, 965 r"The flag 's' is defined twice\. .*First from.*, Second from"): 966 flags.DEFINE_boolean('short1', 0, 'runhelp s1', short_name='s') 967 flags.DEFINE_boolean('s', 0, 'runhelp s2') 968 969 # Check that duplicate flag detection detects definition sites 970 # correctly. 971 flagnames = ['repeated'] 972 original_flags = flags.FlagValues() 973 flags.DEFINE_boolean( 974 flagnames[0], 975 False, 976 'Flag about to be repeated.', 977 flag_values=original_flags) 978 duplicate_flags = module_foo.duplicate_flags(flagnames) 979 with self.assertRaisesRegex(flags.DuplicateFlagError, 980 'flags_test.*module_foo'): 981 original_flags.append_flag_values(duplicate_flags) 982 983 # Make sure allow_override works 984 try: 985 flags.DEFINE_boolean( 986 'dup1', 0, 'runhelp d11', short_name='u', allow_override=0) 987 flag = FLAGS._flags()['dup1'] 988 self.assertEqual(flag.default, 0) 989 990 flags.DEFINE_boolean( 991 'dup1', 1, 'runhelp d12', short_name='u', allow_override=1) 992 flag = FLAGS._flags()['dup1'] 993 self.assertEqual(flag.default, 1) 994 except flags.DuplicateFlagError: 995 raise AssertionError('allow_override did not permit a flag duplication') 996 997 # Make sure allow_override works 998 try: 999 flags.DEFINE_boolean( 1000 'dup2', 0, 'runhelp d21', short_name='u', allow_override=1) 1001 flag = FLAGS._flags()['dup2'] 1002 self.assertEqual(flag.default, 0) 1003 1004 flags.DEFINE_boolean( 1005 'dup2', 1, 'runhelp d22', short_name='u', allow_override=0) 1006 flag = FLAGS._flags()['dup2'] 1007 self.assertEqual(flag.default, 1) 1008 except flags.DuplicateFlagError: 1009 raise AssertionError('allow_override did not permit a flag duplication') 1010 1011 # Make sure that re-importing a module does not cause a DuplicateFlagError 1012 # to be raised. 1013 try: 1014 sys.modules.pop('absl.flags.tests.module_baz') 1015 import absl.flags.tests.module_baz # pylint: disable=g-import-not-at-top 1016 del absl 1017 except flags.DuplicateFlagError: 1018 raise AssertionError('Module reimport caused flag duplication error') 1019 1020 # Make sure that when we override, the help string gets updated correctly 1021 flags.DEFINE_boolean( 1022 'dup3', 0, 'runhelp d31', short_name='u', allow_override=1) 1023 flags.DEFINE_boolean( 1024 'dup3', 1, 'runhelp d32', short_name='u', allow_override=1) 1025 self.assertEqual(str(FLAGS).find('runhelp d31'), -1) 1026 self.assertNotEqual(str(FLAGS).find('runhelp d32'), -1) 1027 1028 # Make sure append_flag_values works 1029 new_flags = flags.FlagValues() 1030 flags.DEFINE_boolean('new1', 0, 'runhelp n1', flag_values=new_flags) 1031 flags.DEFINE_boolean('new2', 0, 'runhelp n2', flag_values=new_flags) 1032 self.assertEqual(len(new_flags._flags()), 2) 1033 old_len = len(FLAGS._flags()) 1034 FLAGS.append_flag_values(new_flags) 1035 self.assertEqual(len(FLAGS._flags()) - old_len, 2) 1036 self.assertEqual('new1' in FLAGS._flags(), True) 1037 self.assertEqual('new2' in FLAGS._flags(), True) 1038 1039 # Then test that removing those flags works 1040 FLAGS.remove_flag_values(new_flags) 1041 self.assertEqual(len(FLAGS._flags()), old_len) 1042 self.assertFalse('new1' in FLAGS._flags()) 1043 self.assertFalse('new2' in FLAGS._flags()) 1044 1045 # Make sure append_flag_values works with flags with shortnames. 1046 new_flags = flags.FlagValues() 1047 flags.DEFINE_boolean('new3', 0, 'runhelp n3', flag_values=new_flags) 1048 flags.DEFINE_boolean( 1049 'new4', 0, 'runhelp n4', flag_values=new_flags, short_name='n4') 1050 self.assertEqual(len(new_flags._flags()), 3) 1051 old_len = len(FLAGS._flags()) 1052 FLAGS.append_flag_values(new_flags) 1053 self.assertEqual(len(FLAGS._flags()) - old_len, 3) 1054 self.assertIn('new3', FLAGS._flags()) 1055 self.assertIn('new4', FLAGS._flags()) 1056 self.assertIn('n4', FLAGS._flags()) 1057 self.assertEqual(FLAGS._flags()['n4'], FLAGS._flags()['new4']) 1058 1059 # Then test removing them 1060 FLAGS.remove_flag_values(new_flags) 1061 self.assertEqual(len(FLAGS._flags()), old_len) 1062 self.assertFalse('new3' in FLAGS._flags()) 1063 self.assertFalse('new4' in FLAGS._flags()) 1064 self.assertFalse('n4' in FLAGS._flags()) 1065 1066 # Make sure append_flag_values fails on duplicates 1067 flags.DEFINE_boolean('dup4', 0, 'runhelp d41') 1068 new_flags = flags.FlagValues() 1069 flags.DEFINE_boolean('dup4', 0, 'runhelp d42', flag_values=new_flags) 1070 with self.assertRaises(flags.DuplicateFlagError): 1071 FLAGS.append_flag_values(new_flags) 1072 1073 # Integer out of bounds 1074 with self.assertRaises(flags.IllegalFlagValueError): 1075 argv = ('./program', '--repeat=-4') 1076 FLAGS(argv) 1077 1078 # Non-integer 1079 with self.assertRaises(flags.IllegalFlagValueError): 1080 argv = ('./program', '--repeat=2.5') 1081 FLAGS(argv) 1082 1083 # Missing required argument 1084 with self.assertRaises(flags.Error): 1085 argv = ('./program', '--name') 1086 FLAGS(argv) 1087 1088 # Non-boolean arguments for boolean 1089 with self.assertRaises(flags.IllegalFlagValueError): 1090 argv = ('./program', '--debug=goofup') 1091 FLAGS(argv) 1092 1093 with self.assertRaises(flags.IllegalFlagValueError): 1094 argv = ('./program', '--debug=42') 1095 FLAGS(argv) 1096 1097 # Non-numeric argument for integer flag --repeat 1098 with self.assertRaises(flags.IllegalFlagValueError): 1099 argv = ('./program', '--repeat', 'Bob', 'extra') 1100 FLAGS(argv) 1101 1102 # Aliases of existing flags 1103 with self.assertRaises(flags.UnrecognizedFlagError): 1104 flags.DEFINE_alias('alias_not_a_flag', 'not_a_flag') 1105 1106 # Programmtically modify alias and aliased flag 1107 flags.DEFINE_alias('alias_octal', 'octal') 1108 FLAGS.octal = 0o2222 1109 self.assertEqual(0o2222, FLAGS.octal) 1110 self.assertEqual(0o2222, FLAGS.alias_octal) 1111 FLAGS.alias_octal = 0o4444 1112 self.assertEqual(0o4444, FLAGS.octal) 1113 self.assertEqual(0o4444, FLAGS.alias_octal) 1114 1115 # Setting alias preserves the default of the original 1116 flags.DEFINE_alias('alias_name', 'name') 1117 flags.DEFINE_alias('alias_debug', 'debug') 1118 flags.DEFINE_alias('alias_decimal', 'decimal') 1119 flags.DEFINE_alias('alias_float', 'float') 1120 flags.DEFINE_alias('alias_letters', 'letters') 1121 self.assertEqual(FLAGS['name'].default, FLAGS.alias_name) 1122 self.assertEqual(FLAGS['debug'].default, FLAGS.alias_debug) 1123 self.assertEqual(int(FLAGS['decimal'].default), FLAGS.alias_decimal) 1124 self.assertEqual(float(FLAGS['float'].default), FLAGS.alias_float) 1125 self.assertSameElements(FLAGS['letters'].default, FLAGS.alias_letters) 1126 1127 # Original flags set on command line 1128 argv = ('./program', '--name=Martin', '--debug=True', '--decimal=777', 1129 '--letters=x,y,z') 1130 FLAGS(argv) 1131 self.assertEqual('Martin', FLAGS.name) 1132 self.assertEqual('Martin', FLAGS.alias_name) 1133 self.assertTrue(FLAGS.debug) 1134 self.assertTrue(FLAGS.alias_debug) 1135 self.assertEqual(777, FLAGS.decimal) 1136 self.assertEqual(777, FLAGS.alias_decimal) 1137 self.assertSameElements(['x', 'y', 'z'], FLAGS.letters) 1138 self.assertSameElements(['x', 'y', 'z'], FLAGS.alias_letters) 1139 1140 # Alias flags set on command line 1141 argv = ('./program', '--alias_name=Auston', '--alias_debug=False', 1142 '--alias_decimal=888', '--alias_letters=l,m,n') 1143 FLAGS(argv) 1144 self.assertEqual('Auston', FLAGS.name) 1145 self.assertEqual('Auston', FLAGS.alias_name) 1146 self.assertFalse(FLAGS.debug) 1147 self.assertFalse(FLAGS.alias_debug) 1148 self.assertEqual(888, FLAGS.decimal) 1149 self.assertEqual(888, FLAGS.alias_decimal) 1150 self.assertSameElements(['l', 'm', 'n'], FLAGS.letters) 1151 self.assertSameElements(['l', 'm', 'n'], FLAGS.alias_letters) 1152 1153 # Make sure importing a module does not change flag value parsed 1154 # from commandline. 1155 flags.DEFINE_integer( 1156 'dup5', 1, 'runhelp d51', short_name='u5', allow_override=0) 1157 self.assertEqual(FLAGS.dup5, 1) 1158 self.assertEqual(FLAGS.dup5, 1) 1159 argv = ('./program', '--dup5=3') 1160 FLAGS(argv) 1161 self.assertEqual(FLAGS.dup5, 3) 1162 flags.DEFINE_integer( 1163 'dup5', 2, 'runhelp d52', short_name='u5', allow_override=1) 1164 self.assertEqual(FLAGS.dup5, 3) 1165 1166 # Make sure importing a module does not change user defined flag value. 1167 flags.DEFINE_integer( 1168 'dup6', 1, 'runhelp d61', short_name='u6', allow_override=0) 1169 self.assertEqual(FLAGS.dup6, 1) 1170 FLAGS.dup6 = 3 1171 self.assertEqual(FLAGS.dup6, 3) 1172 flags.DEFINE_integer( 1173 'dup6', 2, 'runhelp d62', short_name='u6', allow_override=1) 1174 self.assertEqual(FLAGS.dup6, 3) 1175 1176 # Make sure importing a module does not change user defined flag value 1177 # even if it is the 'default' value. 1178 flags.DEFINE_integer( 1179 'dup7', 1, 'runhelp d71', short_name='u7', allow_override=0) 1180 self.assertEqual(FLAGS.dup7, 1) 1181 FLAGS.dup7 = 1 1182 self.assertEqual(FLAGS.dup7, 1) 1183 flags.DEFINE_integer( 1184 'dup7', 2, 'runhelp d72', short_name='u7', allow_override=1) 1185 self.assertEqual(FLAGS.dup7, 1) 1186 1187 # Test module_help(). 1188 helpstr = FLAGS.module_help(module_baz) 1189 1190 expected_help = '\n' + module_baz.__name__ + ':' + """ 1191 --[no]tmod_baz_x: Boolean flag. 1192 (default: 'true')""" 1193 1194 self.assertMultiLineEqual(expected_help, helpstr) 1195 1196 # Test main_module_help(). This must be part of test_flags because 1197 # it depends on dup1/2/3/etc being introduced first. 1198 helpstr = FLAGS.main_module_help() 1199 1200 expected_help = '\n' + sys.argv[0] + ':' + """ 1201 --[no]alias_debug: Alias for --debug. 1202 (default: 'false') 1203 --alias_decimal: Alias for --decimal. 1204 (default: '666') 1205 (an integer) 1206 --alias_float: Alias for --float. 1207 (default: '3.14') 1208 (a number) 1209 --alias_letters: Alias for --letters. 1210 (default: 'a,b,c') 1211 (a comma separated list) 1212 --alias_name: Alias for --name. 1213 (default: 'Bob') 1214 --alias_octal: Alias for --octal. 1215 (default: '438') 1216 (an integer) 1217 --args: a list of arguments 1218 (default: 'v=1,"vmodule=a=0,b=2"') 1219 (a comma separated list) 1220 --blah: <bla|Blah|BLAH|blah>: ? 1221 --cases: <UPPER|lower|Initial|Ot_HeR>: ? 1222 --[no]debug: debughelp 1223 (default: 'false') 1224 --decimal: using decimals 1225 (default: '666') 1226 (an integer) 1227 -u,--[no]dup1: runhelp d12 1228 (default: 'true') 1229 -u,--[no]dup2: runhelp d22 1230 (default: 'true') 1231 -u,--[no]dup3: runhelp d32 1232 (default: 'true') 1233 --[no]dup4: runhelp d41 1234 (default: 'false') 1235 -u5,--dup5: runhelp d51 1236 (default: '1') 1237 (an integer) 1238 -u6,--dup6: runhelp d61 1239 (default: '1') 1240 (an integer) 1241 -u7,--dup7: runhelp d71 1242 (default: '1') 1243 (an integer) 1244 --float: using floats 1245 (default: '3.14') 1246 (a number) 1247 --funny: <Joke|ha|ha|ha|ha>: ? 1248 --hexadecimal: using hexadecimals 1249 (default: '1638') 1250 (an integer) 1251 --kwery: <who|what|Why|where|when>: ? 1252 --l: how long to be 1253 (default: '9223372032559808512') 1254 (an integer) 1255 --letters: a list of letters 1256 (default: 'a,b,c') 1257 (a comma separated list) 1258 --list_default_list: with default being a list of strings 1259 (default: 'a,b,c') 1260 (a comma separated list) 1261 -m,--m_str: string option that can occur multiple times; 1262 repeat this option to specify a list of values 1263 (default: "['def1', 'def2']") 1264 --name: namehelp 1265 (default: 'Bob') 1266 --[no]noexec: boolean flag with no as prefix 1267 (default: 'true') 1268 --octal: using octals 1269 (default: '438') 1270 (an integer) 1271 --only_once: test only sets this once 1272 --[no]q: quiet mode 1273 (default: 'true') 1274 --[no]quack: superstring of 'q' 1275 (default: 'false') 1276 -r,--repeat: how many times to repeat (0-5) 1277 (default: '4') 1278 (a non-negative integer) 1279 -s,--s_str: string option that can occur multiple times; 1280 repeat this option to specify a list of values 1281 (default: "['sing1']") 1282 --sense: <Case|case|CASE>: ? 1283 --smol: smol flag 1284 (default: '1') 1285 (integer <= 5) 1286 --[no]test0: test boolean parsing 1287 --[no]test1: test boolean parsing 1288 --testcomma_list: test comma list parsing 1289 (default: '') 1290 (a comma separated list) 1291 --[no]testget1: test parsing with defaults 1292 --[no]testget2: test parsing with defaults 1293 --[no]testget3: test parsing with defaults 1294 --testget4: test parsing with defaults 1295 (an integer) 1296 --[no]testnone: test boolean parsing 1297 --testspace_list: tests space list parsing 1298 (default: '') 1299 (a whitespace separated list) 1300 --testspace_or_comma_list: tests space list parsing with comma compatibility 1301 (default: '') 1302 (a whitespace or comma separated list) 1303 --universe: test tries to set this three times 1304 --x: how eXtreme to be 1305 (default: '3') 1306 (an integer) 1307 -z,--[no]zoom1: runhelp z1 1308 (default: 'false')""" 1309 1310 self.assertMultiLineEqual(expected_help, helpstr) 1311 1312 def test_string_flag_with_wrong_type(self): 1313 fv = flags.FlagValues() 1314 with self.assertRaises(flags.IllegalFlagValueError): 1315 flags.DEFINE_string('name', False, 'help', flag_values=fv) # type: ignore 1316 with self.assertRaises(flags.IllegalFlagValueError): 1317 flags.DEFINE_string('name2', 0, 'help', flag_values=fv) # type: ignore 1318 1319 def test_integer_flag_with_wrong_type(self): 1320 fv = flags.FlagValues() 1321 with self.assertRaises(flags.IllegalFlagValueError): 1322 flags.DEFINE_integer('name', 1e2, 'help', flag_values=fv) # type: ignore 1323 with self.assertRaises(flags.IllegalFlagValueError): 1324 flags.DEFINE_integer('name', [], 'help', flag_values=fv) # type: ignore 1325 with self.assertRaises(flags.IllegalFlagValueError): 1326 flags.DEFINE_integer('name', False, 'help', flag_values=fv) 1327 1328 def test_float_flag_with_wrong_type(self): 1329 fv = flags.FlagValues() 1330 with self.assertRaises(flags.IllegalFlagValueError): 1331 flags.DEFINE_float('name', False, 'help', flag_values=fv) 1332 1333 def test_enum_flag_with_empty_values(self): 1334 fv = flags.FlagValues() 1335 with self.assertRaises(ValueError): 1336 flags.DEFINE_enum('fruit', None, [], 'help', flag_values=fv) 1337 1338 def test_enum_flag_with_str_values(self): 1339 fv = flags.FlagValues() 1340 with self.assertRaises(ValueError): 1341 flags.DEFINE_enum('fruit', None, 'option', 'help', flag_values=fv) # type: ignore 1342 1343 def test_multi_enum_flag_with_str_values(self): 1344 fv = flags.FlagValues() 1345 with self.assertRaises(ValueError): 1346 flags.DEFINE_multi_enum('fruit', None, 'option', 'help', flag_values=fv) # type: ignore 1347 1348 def test_define_enum_class_flag(self): 1349 fv = flags.FlagValues() 1350 flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv) 1351 fv.mark_as_parsed() 1352 1353 self.assertIsNone(fv.fruit) 1354 1355 def test_parse_enum_class_flag(self): 1356 fv = flags.FlagValues() 1357 flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv) 1358 1359 argv = ('./program', '--fruit=orange') 1360 argv = fv(argv) 1361 self.assertEqual(len(argv), 1, 'wrong number of arguments pulled') 1362 self.assertEqual(argv[0], './program', 'program name not preserved') 1363 self.assertEqual(fv['fruit'].present, 1) 1364 self.assertEqual(fv['fruit'].value, Fruit.ORANGE) 1365 fv.unparse_flags() 1366 argv = ('./program', '--fruit=APPLE') 1367 argv = fv(argv) 1368 self.assertEqual(len(argv), 1, 'wrong number of arguments pulled') 1369 self.assertEqual(argv[0], './program', 'program name not preserved') 1370 self.assertEqual(fv['fruit'].present, 1) 1371 self.assertEqual(fv['fruit'].value, Fruit.APPLE) 1372 fv.unparse_flags() 1373 1374 def test_enum_class_flag_help_message(self): 1375 fv = flags.FlagValues() 1376 flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv) 1377 1378 helpstr = fv.main_module_help() 1379 expected_help = '\n%s:\n --fruit: <apple|orange>: ?' % sys.argv[0] 1380 1381 self.assertEqual(helpstr, expected_help) 1382 1383 def test_enum_class_flag_with_wrong_default_value_type(self): 1384 fv = flags.FlagValues() 1385 with self.assertRaises(_exceptions.IllegalFlagValueError): 1386 flags.DEFINE_enum_class('fruit', 1, Fruit, 'help', flag_values=fv) # type: ignore 1387 1388 def test_enum_class_flag_requires_enum_class(self): 1389 fv = flags.FlagValues() 1390 with self.assertRaises(TypeError): 1391 flags.DEFINE_enum_class( # type: ignore 1392 'fruit', None, ['apple', 'orange'], 'help', flag_values=fv 1393 ) 1394 1395 def test_enum_class_flag_requires_non_empty_enum_class(self): 1396 fv = flags.FlagValues() 1397 with self.assertRaises(ValueError): 1398 flags.DEFINE_enum_class('empty', None, EmptyEnum, 'help', flag_values=fv) 1399 1400 def test_required_flag(self): 1401 fv = flags.FlagValues() 1402 fl = flags.DEFINE_integer( 1403 name='int_flag', 1404 default=None, 1405 help='help', 1406 required=True, 1407 flag_values=fv) 1408 # Since the flag is required, the FlagHolder should ensure value returned 1409 # is not None. 1410 self.assertTrue(fl._ensure_non_none_value) 1411 1412 def test_illegal_required_flag(self): 1413 fv = flags.FlagValues() 1414 with self.assertRaises(ValueError): 1415 flags.DEFINE_integer( 1416 name='int_flag', 1417 default=3, 1418 help='help', 1419 required=True, 1420 flag_values=fv) 1421 1422 1423class MultiNumericalFlagsTest(absltest.TestCase): 1424 1425 def test_multi_numerical_flags(self): 1426 """Test multi_int and multi_float flags.""" 1427 fv = flags.FlagValues() 1428 int_defaults = [77, 88] 1429 flags.DEFINE_multi_integer( 1430 'm_int', 1431 int_defaults, 1432 'integer option that can occur multiple times', 1433 short_name='mi', 1434 flag_values=fv) 1435 self.assertListEqual(fv['m_int'].default, int_defaults) 1436 argv = ('./program', '--m_int=-99', '--mi=101') 1437 fv(argv) 1438 self.assertListEqual(fv.get_flag_value('m_int', None), [-99, 101]) 1439 1440 float_defaults = [2.2, 3] 1441 flags.DEFINE_multi_float( 1442 'm_float', 1443 float_defaults, 1444 'float option that can occur multiple times', 1445 short_name='mf', 1446 flag_values=fv) 1447 for (expected, actual) in zip(float_defaults, 1448 fv.get_flag_value('m_float', None)): 1449 self.assertAlmostEqual(expected, actual) 1450 argv = ('./program', '--m_float=-17', '--mf=2.78e9') 1451 fv(argv) 1452 expected_floats = [-17.0, 2.78e9] 1453 for (expected, actual) in zip(expected_floats, 1454 fv.get_flag_value('m_float', None)): 1455 self.assertAlmostEqual(expected, actual) 1456 1457 def test_multi_numerical_with_tuples(self): 1458 """Verify multi_int/float accept tuples as default values.""" 1459 flags.DEFINE_multi_integer( 1460 'm_int_tuple', (77, 88), 1461 'integer option that can occur multiple times', 1462 short_name='mi_tuple') 1463 self.assertListEqual(FLAGS.get_flag_value('m_int_tuple', None), [77, 88]) 1464 1465 dict_with_float_keys = {2.2: 'hello', 3: 'happy'} 1466 float_defaults = dict_with_float_keys.keys() 1467 flags.DEFINE_multi_float( 1468 'm_float_tuple', 1469 float_defaults, 1470 'float option that can occur multiple times', 1471 short_name='mf_tuple') 1472 for (expected, actual) in zip(float_defaults, 1473 FLAGS.get_flag_value('m_float_tuple', None)): 1474 self.assertAlmostEqual(expected, actual) 1475 1476 def test_single_value_default(self): 1477 """Test multi_int and multi_float flags with a single default value.""" 1478 int_default = 77 1479 flags.DEFINE_multi_integer('m_int1', int_default, 1480 'integer option that can occur multiple times') 1481 self.assertListEqual(FLAGS.get_flag_value('m_int1', None), [int_default]) 1482 1483 float_default = 2.2 1484 flags.DEFINE_multi_float('m_float1', float_default, 1485 'float option that can occur multiple times') 1486 actual = FLAGS.get_flag_value('m_float1', None) 1487 self.assertEqual(1, len(actual)) 1488 self.assertAlmostEqual(actual[0], float_default) 1489 1490 def test_bad_multi_numerical_flags(self): 1491 """Test multi_int and multi_float flags with non-parseable values.""" 1492 1493 # Test non-parseable defaults. 1494 self.assertRaisesRegex( 1495 flags.IllegalFlagValueError, 1496 r"flag --m_int2=abc: invalid literal for int\(\) with base 10: 'abc'", 1497 flags.DEFINE_multi_integer, 'm_int2', ['abc'], 'desc') 1498 1499 self.assertRaisesRegex( 1500 flags.IllegalFlagValueError, r'flag --m_float2=abc: ' 1501 r'(invalid literal for float\(\)|could not convert string to float): ' 1502 r"'?abc'?", flags.DEFINE_multi_float, 'm_float2', ['abc'], 'desc') 1503 1504 # Test non-parseable command line values. 1505 fv = flags.FlagValues() 1506 flags.DEFINE_multi_integer( 1507 'm_int2', 1508 '77', 1509 'integer option that can occur multiple times', 1510 flag_values=fv) 1511 argv = ('./program', '--m_int2=def') 1512 self.assertRaisesRegex( 1513 flags.IllegalFlagValueError, 1514 r"flag --m_int2=def: invalid literal for int\(\) with base 10: 'def'", 1515 fv, argv) 1516 1517 flags.DEFINE_multi_float( 1518 'm_float2', 1519 2.2, 1520 'float option that can occur multiple times', 1521 flag_values=fv) 1522 argv = ('./program', '--m_float2=def') 1523 self.assertRaisesRegex( 1524 flags.IllegalFlagValueError, r'flag --m_float2=def: ' 1525 r'(invalid literal for float\(\)|could not convert string to float): ' 1526 r"'?def'?", fv, argv) 1527 1528 1529class MultiEnumFlagsTest(absltest.TestCase): 1530 1531 def test_multi_enum_flags(self): 1532 """Test multi_enum flags.""" 1533 fv = flags.FlagValues() 1534 1535 enum_defaults = ['FOO', 'BAZ'] 1536 flags.DEFINE_multi_enum( 1537 'm_enum', 1538 enum_defaults, ['FOO', 'BAR', 'BAZ', 'WHOOSH'], 1539 'Enum option that can occur multiple times', 1540 short_name='me', 1541 flag_values=fv) 1542 self.assertListEqual(fv['m_enum'].default, enum_defaults) 1543 argv = ('./program', '--m_enum=WHOOSH', '--me=FOO') 1544 fv(argv) 1545 self.assertListEqual(fv.get_flag_value('m_enum', None), ['WHOOSH', 'FOO']) 1546 1547 def test_help_text(self): 1548 """Test multi_enum flag's help text.""" 1549 fv = flags.FlagValues() 1550 1551 flags.DEFINE_multi_enum( 1552 'm_enum', 1553 None, ['FOO', 'BAR'], 1554 'Enum option that can occur multiple times', 1555 flag_values=fv) 1556 self.assertRegex( 1557 fv['m_enum'].help, 1558 r'<FOO\|BAR>: Enum option that can occur multiple times;\s+' 1559 'repeat this option to specify a list of values') 1560 1561 def test_single_value_default(self): 1562 """Test multi_enum flags with a single default value.""" 1563 fv = flags.FlagValues() 1564 enum_default = 'FOO' 1565 flags.DEFINE_multi_enum( 1566 'm_enum1', 1567 enum_default, ['FOO', 'BAR', 'BAZ', 'WHOOSH'], 1568 'enum option that can occur multiple times', 1569 flag_values=fv) 1570 self.assertListEqual(fv['m_enum1'].default, [enum_default]) 1571 1572 def test_case_sensitivity(self): 1573 """Test case sensitivity of multi_enum flag.""" 1574 fv = flags.FlagValues() 1575 # Test case insensitive enum. 1576 flags.DEFINE_multi_enum( 1577 'm_enum2', ['whoosh'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'], 1578 'Enum option that can occur multiple times', 1579 short_name='me2', 1580 case_sensitive=False, 1581 flag_values=fv) 1582 argv = ('./program', '--m_enum2=bar', '--me2=fOo') 1583 fv(argv) 1584 self.assertListEqual(fv.get_flag_value('m_enum2', None), ['BAR', 'FOO']) 1585 1586 # Test case sensitive enum. 1587 flags.DEFINE_multi_enum( 1588 'm_enum3', ['BAR'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'], 1589 'Enum option that can occur multiple times', 1590 short_name='me3', 1591 case_sensitive=True, 1592 flag_values=fv) 1593 argv = ('./program', '--m_enum3=bar', '--me3=fOo') 1594 self.assertRaisesRegex( 1595 flags.IllegalFlagValueError, 1596 r'flag --m_enum3=invalid: value should be one of <FOO|BAR|BAZ|WHOOSH>', 1597 fv, argv) 1598 1599 def test_bad_multi_enum_flags(self): 1600 """Test multi_enum with invalid values.""" 1601 1602 # Test defaults that are not in the permitted list of enums. 1603 self.assertRaisesRegex( 1604 flags.IllegalFlagValueError, 1605 r'flag --m_enum=INVALID: value should be one of <FOO|BAR|BAZ>', 1606 flags.DEFINE_multi_enum, 'm_enum', ['INVALID'], ['FOO', 'BAR', 'BAZ'], 1607 'desc') 1608 1609 self.assertRaisesRegex( 1610 flags.IllegalFlagValueError, 1611 r'flag --m_enum=1234: value should be one of <FOO|BAR|BAZ>', 1612 flags.DEFINE_multi_enum, 'm_enum2', [1234], ['FOO', 'BAR', 'BAZ'], 1613 'desc') 1614 1615 # Test command-line values that are not in the permitted list of enums. 1616 flags.DEFINE_multi_enum('m_enum4', 'FOO', ['FOO', 'BAR', 'BAZ'], 1617 'enum option that can occur multiple times') 1618 argv = ('./program', '--m_enum4=INVALID') 1619 self.assertRaisesRegex( 1620 flags.IllegalFlagValueError, 1621 r'flag --m_enum4=invalid: value should be one of <FOO|BAR|BAZ>', FLAGS, 1622 argv) 1623 1624 1625class MultiEnumClassFlagsTest(absltest.TestCase): 1626 1627 def test_short_name(self): 1628 fv = flags.FlagValues() 1629 flags.DEFINE_multi_enum_class( 1630 'fruit', 1631 None, 1632 Fruit, 1633 'Enum option that can occur multiple times', 1634 flag_values=fv, 1635 short_name='me') 1636 self.assertEqual(fv['fruit'].short_name, 'me') 1637 1638 def test_define_results_in_registered_flag_with_none(self): 1639 fv = flags.FlagValues() 1640 enum_defaults = None 1641 flags.DEFINE_multi_enum_class( 1642 'fruit', 1643 enum_defaults, 1644 Fruit, 1645 'Enum option that can occur multiple times', 1646 flag_values=fv) 1647 fv.mark_as_parsed() 1648 1649 self.assertIsNone(fv.fruit) 1650 1651 def test_help_text(self): 1652 fv = flags.FlagValues() 1653 enum_defaults = None 1654 flags.DEFINE_multi_enum_class( 1655 'fruit', 1656 enum_defaults, 1657 Fruit, 1658 'Enum option that can occur multiple times', 1659 flag_values=fv) 1660 1661 self.assertRegex( 1662 fv['fruit'].help, 1663 r'<apple\|orange>: Enum option that can occur multiple times;\s+' 1664 'repeat this option to specify a list of values') 1665 1666 def test_define_results_in_registered_flag_with_string(self): 1667 fv = flags.FlagValues() 1668 enum_defaults = 'apple' 1669 flags.DEFINE_multi_enum_class( 1670 'fruit', 1671 enum_defaults, 1672 Fruit, 1673 'Enum option that can occur multiple times', 1674 flag_values=fv) 1675 fv.mark_as_parsed() 1676 1677 self.assertListEqual(fv.fruit, [Fruit.APPLE]) 1678 1679 def test_define_results_in_registered_flag_with_enum(self): 1680 fv = flags.FlagValues() 1681 enum_defaults = Fruit.APPLE 1682 flags.DEFINE_multi_enum_class( 1683 'fruit', 1684 enum_defaults, 1685 Fruit, 1686 'Enum option that can occur multiple times', 1687 flag_values=fv) 1688 fv.mark_as_parsed() 1689 1690 self.assertListEqual(fv.fruit, [Fruit.APPLE]) 1691 1692 def test_define_results_in_registered_flag_with_string_list(self): 1693 fv = flags.FlagValues() 1694 enum_defaults = ['apple', 'APPLE'] 1695 flags.DEFINE_multi_enum_class( 1696 'fruit', 1697 enum_defaults, 1698 CaseSensitiveFruit, 1699 'Enum option that can occur multiple times', 1700 flag_values=fv, 1701 case_sensitive=True) 1702 fv.mark_as_parsed() 1703 1704 self.assertListEqual(fv.fruit, 1705 [CaseSensitiveFruit.apple, CaseSensitiveFruit.APPLE]) 1706 1707 def test_define_results_in_registered_flag_with_enum_list(self): 1708 fv = flags.FlagValues() 1709 enum_defaults = [Fruit.APPLE, Fruit.ORANGE] 1710 flags.DEFINE_multi_enum_class( 1711 'fruit', 1712 enum_defaults, 1713 Fruit, 1714 'Enum option that can occur multiple times', 1715 flag_values=fv) 1716 fv.mark_as_parsed() 1717 1718 self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE]) 1719 1720 def test_from_command_line_returns_multiple(self): 1721 fv = flags.FlagValues() 1722 enum_defaults = [Fruit.APPLE] 1723 flags.DEFINE_multi_enum_class( 1724 'fruit', 1725 enum_defaults, 1726 Fruit, 1727 'Enum option that can occur multiple times', 1728 flag_values=fv) 1729 argv = ('./program', '--fruit=Apple', '--fruit=orange') 1730 fv(argv) 1731 self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE]) 1732 1733 def test_bad_multi_enum_class_flags_from_definition(self): 1734 with self.assertRaisesRegex( 1735 flags.IllegalFlagValueError, 1736 'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'): 1737 flags.DEFINE_multi_enum_class('fruit', ['INVALID'], Fruit, 'desc') 1738 1739 def test_bad_multi_enum_class_flags_from_commandline(self): 1740 fv = flags.FlagValues() 1741 enum_defaults = [Fruit.APPLE] 1742 flags.DEFINE_multi_enum_class( 1743 'fruit', enum_defaults, Fruit, 'desc', flag_values=fv) 1744 argv = ('./program', '--fruit=INVALID') 1745 with self.assertRaisesRegex( 1746 flags.IllegalFlagValueError, 1747 'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'): 1748 fv(argv) 1749 1750 1751class UnicodeFlagsTest(absltest.TestCase): 1752 """Testing proper unicode support for flags.""" 1753 1754 def test_unicode_default_and_helpstring(self): 1755 fv = flags.FlagValues() 1756 flags.DEFINE_string( 1757 'unicode_str', 1758 b'\xC3\x80\xC3\xBD'.decode('utf-8'), 1759 b'help:\xC3\xAA'.decode('utf-8'), 1760 flag_values=fv) 1761 argv = ('./program',) 1762 fv(argv) # should not raise any exceptions 1763 1764 argv = ('./program', '--unicode_str=foo') 1765 fv(argv) # should not raise any exceptions 1766 1767 def test_unicode_in_list(self): 1768 fv = flags.FlagValues() 1769 flags.DEFINE_list( 1770 'unicode_list', 1771 ['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')], 1772 b'help:\xC3\xAB'.decode('utf-8'), 1773 flag_values=fv) 1774 argv = ('./program',) 1775 fv(argv) # should not raise any exceptions 1776 1777 argv = ('./program', '--unicode_list=hello,there') 1778 fv(argv) # should not raise any exceptions 1779 1780 def test_xmloutput(self): 1781 fv = flags.FlagValues() 1782 flags.DEFINE_string( 1783 'unicode1', 1784 b'\xC3\x80\xC3\xBD'.decode('utf-8'), 1785 b'help:\xC3\xAC'.decode('utf-8'), 1786 flag_values=fv) 1787 flags.DEFINE_list( 1788 'unicode2', 1789 ['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')], 1790 b'help:\xC3\xAD'.decode('utf-8'), 1791 flag_values=fv) 1792 flags.DEFINE_list( 1793 'non_unicode', ['abc', 'def', 'ghi'], 1794 b'help:\xC3\xAD'.decode('utf-8'), 1795 flag_values=fv) 1796 1797 outfile = io.StringIO() 1798 fv.write_help_in_xml_format(outfile) 1799 actual_output = outfile.getvalue() 1800 1801 # The xml output is large, so we just check parts of it. 1802 self.assertIn( 1803 b'<name>unicode1</name>\n' 1804 b' <meaning>help:\xc3\xac</meaning>\n' 1805 b' <default>\xc3\x80\xc3\xbd</default>\n' 1806 b' <current>\xc3\x80\xc3\xbd</current>'.decode('utf-8'), 1807 actual_output) 1808 self.assertIn( 1809 b'<name>unicode2</name>\n' 1810 b' <meaning>help:\xc3\xad</meaning>\n' 1811 b' <default>abc,\xc3\x80,\xc3\xbd</default>\n' 1812 b" <current>['abc', '\xc3\x80', '\xc3\xbd']" 1813 b'</current>'.decode('utf-8'), actual_output) 1814 self.assertIn( 1815 b'<name>non_unicode</name>\n' 1816 b' <meaning>help:\xc3\xad</meaning>\n' 1817 b' <default>abc,def,ghi</default>\n' 1818 b" <current>['abc', 'def', 'ghi']" 1819 b'</current>'.decode('utf-8'), actual_output) 1820 1821 1822class LoadFromFlagFileTest(absltest.TestCase): 1823 """Testing loading flags from a file and parsing them.""" 1824 1825 def setUp(self): 1826 self.flag_values = flags.FlagValues() 1827 flags.DEFINE_string( 1828 'unittest_message1', 1829 'Foo!', 1830 'You Add Here.', 1831 flag_values=self.flag_values) 1832 flags.DEFINE_string( 1833 'unittest_message2', 1834 'Bar!', 1835 'Hello, Sailor!', 1836 flag_values=self.flag_values) 1837 flags.DEFINE_boolean( 1838 'unittest_boolflag', 1839 0, 1840 'Some Boolean thing', 1841 flag_values=self.flag_values) 1842 flags.DEFINE_integer( 1843 'unittest_number', 1844 12345, 1845 'Some integer', 1846 lower_bound=0, 1847 flag_values=self.flag_values) 1848 flags.DEFINE_list( 1849 'UnitTestList', '1,2,3', 'Some list', flag_values=self.flag_values) 1850 self.tmp_path = None 1851 self.flag_values.mark_as_parsed() 1852 1853 def tearDown(self): 1854 self._remove_test_files() 1855 1856 def _setup_test_files(self): 1857 """Creates and sets up some dummy flagfile files with bogus flags.""" 1858 1859 # Figure out where to create temporary files 1860 self.assertFalse(self.tmp_path) 1861 self.tmp_path = tempfile.mkdtemp() 1862 1863 tmp_flag_file_1 = open(self.tmp_path + '/UnitTestFile1.tst', 'w') 1864 tmp_flag_file_2 = open(self.tmp_path + '/UnitTestFile2.tst', 'w') 1865 tmp_flag_file_3 = open(self.tmp_path + '/UnitTestFile3.tst', 'w') 1866 tmp_flag_file_4 = open(self.tmp_path + '/UnitTestFile4.tst', 'w') 1867 1868 # put some dummy flags in our test files 1869 tmp_flag_file_1.write('#A Fake Comment\n') 1870 tmp_flag_file_1.write('--unittest_message1=tempFile1!\n') 1871 tmp_flag_file_1.write('\n') 1872 tmp_flag_file_1.write('--unittest_number=54321\n') 1873 tmp_flag_file_1.write('--nounittest_boolflag\n') 1874 file_list = [tmp_flag_file_1.name] 1875 # this one includes test file 1 1876 tmp_flag_file_2.write('//A Different Fake Comment\n') 1877 tmp_flag_file_2.write('--flagfile=%s\n' % tmp_flag_file_1.name) 1878 tmp_flag_file_2.write('--unittest_message2=setFromTempFile2\n') 1879 tmp_flag_file_2.write('\t\t\n') 1880 tmp_flag_file_2.write('--unittest_number=6789a\n') 1881 file_list.append(tmp_flag_file_2.name) 1882 # this file points to itself 1883 tmp_flag_file_3.write('--flagfile=%s\n' % tmp_flag_file_3.name) 1884 tmp_flag_file_3.write('--unittest_message1=setFromTempFile3\n') 1885 tmp_flag_file_3.write('#YAFC\n') 1886 tmp_flag_file_3.write('--unittest_boolflag\n') 1887 file_list.append(tmp_flag_file_3.name) 1888 # this file is unreadable 1889 tmp_flag_file_4.write('--flagfile=%s\n' % tmp_flag_file_3.name) 1890 tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n') 1891 tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n') 1892 os.chmod(self.tmp_path + '/UnitTestFile4.tst', 0) 1893 file_list.append(tmp_flag_file_4.name) 1894 1895 tmp_flag_file_1.close() 1896 tmp_flag_file_2.close() 1897 tmp_flag_file_3.close() 1898 tmp_flag_file_4.close() 1899 1900 return file_list # these are just the file names 1901 1902 def _remove_test_files(self): 1903 """Removes the files we just created.""" 1904 if self.tmp_path: 1905 shutil.rmtree(self.tmp_path, ignore_errors=True) 1906 self.tmp_path = None 1907 1908 def _read_flags_from_files(self, argv, force_gnu): 1909 return argv[:1] + self.flag_values.read_flags_from_files( 1910 argv[1:], force_gnu=force_gnu) 1911 1912 #### Flagfile Unit Tests #### 1913 def test_method_flagfiles_1(self): 1914 """Test trivial case with no flagfile based options.""" 1915 fake_cmd_line = 'fooScript --unittest_boolflag' 1916 fake_argv = fake_cmd_line.split(' ') 1917 self.flag_values(fake_argv) 1918 self.assertEqual(self.flag_values.unittest_boolflag, 1) 1919 self.assertListEqual(fake_argv, 1920 self._read_flags_from_files(fake_argv, False)) 1921 1922 def test_method_flagfiles_2(self): 1923 """Tests parsing one file + arguments off simulated argv.""" 1924 tmp_files = self._setup_test_files() 1925 # specify our temp file on the fake cmd line 1926 fake_cmd_line = 'fooScript --q --flagfile=%s' % tmp_files[0] 1927 fake_argv = fake_cmd_line.split(' ') 1928 1929 # We should see the original cmd line with the file's contents spliced in. 1930 # Flags from the file will appear in the order order they are specified 1931 # in the file, in the same position as the flagfile argument. 1932 expected_results = [ 1933 'fooScript', '--q', '--unittest_message1=tempFile1!', 1934 '--unittest_number=54321', '--nounittest_boolflag' 1935 ] 1936 test_results = self._read_flags_from_files(fake_argv, False) 1937 self.assertListEqual(expected_results, test_results) 1938 1939 # end testTwo def 1940 1941 def test_method_flagfiles_3(self): 1942 """Tests parsing nested files + arguments of simulated argv.""" 1943 tmp_files = self._setup_test_files() 1944 # specify our temp file on the fake cmd line 1945 fake_cmd_line = ('fooScript --unittest_number=77 --flagfile=%s' % 1946 tmp_files[1]) 1947 fake_argv = fake_cmd_line.split(' ') 1948 1949 expected_results = [ 1950 'fooScript', '--unittest_number=77', '--unittest_message1=tempFile1!', 1951 '--unittest_number=54321', '--nounittest_boolflag', 1952 '--unittest_message2=setFromTempFile2', '--unittest_number=6789a' 1953 ] 1954 test_results = self._read_flags_from_files(fake_argv, False) 1955 self.assertListEqual(expected_results, test_results) 1956 1957 # end testThree def 1958 1959 def test_method_flagfiles_3_spaces(self): 1960 """Tests parsing nested files + arguments of simulated argv. 1961 1962 The arguments include a pair that is actually an arg with a value, so it 1963 doesn't stop processing. 1964 """ 1965 tmp_files = self._setup_test_files() 1966 # specify our temp file on the fake cmd line 1967 fake_cmd_line = ('fooScript --unittest_number 77 --flagfile=%s' % 1968 tmp_files[1]) 1969 fake_argv = fake_cmd_line.split(' ') 1970 1971 expected_results = [ 1972 'fooScript', '--unittest_number', '77', 1973 '--unittest_message1=tempFile1!', '--unittest_number=54321', 1974 '--nounittest_boolflag', '--unittest_message2=setFromTempFile2', 1975 '--unittest_number=6789a' 1976 ] 1977 test_results = self._read_flags_from_files(fake_argv, False) 1978 self.assertListEqual(expected_results, test_results) 1979 1980 def test_method_flagfiles_3_spaces_boolean(self): 1981 """Tests parsing nested files + arguments of simulated argv. 1982 1983 The arguments include a pair that looks like a --x y arg with value, but 1984 since the flag is a boolean it's actually not. 1985 """ 1986 tmp_files = self._setup_test_files() 1987 # specify our temp file on the fake cmd line 1988 fake_cmd_line = ('fooScript --unittest_boolflag 77 --flagfile=%s' % 1989 tmp_files[1]) 1990 fake_argv = fake_cmd_line.split(' ') 1991 1992 expected_results = [ 1993 'fooScript', '--unittest_boolflag', '77', 1994 '--flagfile=%s' % tmp_files[1] 1995 ] 1996 with _use_gnu_getopt(self.flag_values, False): 1997 test_results = self._read_flags_from_files(fake_argv, False) 1998 self.assertListEqual(expected_results, test_results) 1999 2000 def test_method_flagfiles_4(self): 2001 """Tests parsing self-referential files + arguments of simulated argv. 2002 2003 This test should print a warning to stderr of some sort. 2004 """ 2005 tmp_files = self._setup_test_files() 2006 # specify our temp file on the fake cmd line 2007 fake_cmd_line = ('fooScript --flagfile=%s --nounittest_boolflag' % 2008 tmp_files[2]) 2009 fake_argv = fake_cmd_line.split(' ') 2010 expected_results = [ 2011 'fooScript', '--unittest_message1=setFromTempFile3', 2012 '--unittest_boolflag', '--nounittest_boolflag' 2013 ] 2014 2015 test_results = self._read_flags_from_files(fake_argv, False) 2016 self.assertListEqual(expected_results, test_results) 2017 2018 def test_method_flagfiles_5(self): 2019 """Test that --flagfile parsing respects the '--' end-of-options marker.""" 2020 tmp_files = self._setup_test_files() 2021 # specify our temp file on the fake cmd line 2022 fake_cmd_line = 'fooScript --some_flag -- --flagfile=%s' % tmp_files[0] 2023 fake_argv = fake_cmd_line.split(' ') 2024 expected_results = [ 2025 'fooScript', '--some_flag', '--', 2026 '--flagfile=%s' % tmp_files[0] 2027 ] 2028 2029 test_results = self._read_flags_from_files(fake_argv, False) 2030 self.assertListEqual(expected_results, test_results) 2031 2032 def test_method_flagfiles_6(self): 2033 """Test that --flagfile parsing stops at non-options (non-GNU behavior).""" 2034 tmp_files = self._setup_test_files() 2035 # specify our temp file on the fake cmd line 2036 fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' % 2037 tmp_files[0]) 2038 fake_argv = fake_cmd_line.split(' ') 2039 expected_results = [ 2040 'fooScript', '--some_flag', 'some_arg', 2041 '--flagfile=%s' % tmp_files[0] 2042 ] 2043 2044 with _use_gnu_getopt(self.flag_values, False): 2045 test_results = self._read_flags_from_files(fake_argv, False) 2046 self.assertListEqual(expected_results, test_results) 2047 2048 def test_method_flagfiles_7(self): 2049 """Test that --flagfile parsing skips over a non-option (GNU behavior).""" 2050 self.flag_values.set_gnu_getopt() 2051 tmp_files = self._setup_test_files() 2052 # specify our temp file on the fake cmd line 2053 fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' % 2054 tmp_files[0]) 2055 fake_argv = fake_cmd_line.split(' ') 2056 expected_results = [ 2057 'fooScript', '--some_flag', 'some_arg', 2058 '--unittest_message1=tempFile1!', '--unittest_number=54321', 2059 '--nounittest_boolflag' 2060 ] 2061 2062 test_results = self._read_flags_from_files(fake_argv, False) 2063 self.assertListEqual(expected_results, test_results) 2064 2065 def test_method_flagfiles_8(self): 2066 """Test that --flagfile parsing respects force_gnu=True.""" 2067 tmp_files = self._setup_test_files() 2068 # specify our temp file on the fake cmd line 2069 fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' % 2070 tmp_files[0]) 2071 fake_argv = fake_cmd_line.split(' ') 2072 expected_results = [ 2073 'fooScript', '--some_flag', 'some_arg', 2074 '--unittest_message1=tempFile1!', '--unittest_number=54321', 2075 '--nounittest_boolflag' 2076 ] 2077 2078 test_results = self._read_flags_from_files(fake_argv, True) 2079 self.assertListEqual(expected_results, test_results) 2080 2081 def test_method_flagfiles_repeated_non_circular(self): 2082 """Tests that parsing repeated non-circular flagfiles works.""" 2083 tmp_files = self._setup_test_files() 2084 # specify our temp files on the fake cmd line 2085 fake_cmd_line = ('fooScript --flagfile=%s --flagfile=%s' % 2086 (tmp_files[1], tmp_files[0])) 2087 fake_argv = fake_cmd_line.split(' ') 2088 expected_results = [ 2089 'fooScript', '--unittest_message1=tempFile1!', 2090 '--unittest_number=54321', '--nounittest_boolflag', 2091 '--unittest_message2=setFromTempFile2', '--unittest_number=6789a', 2092 '--unittest_message1=tempFile1!', '--unittest_number=54321', 2093 '--nounittest_boolflag' 2094 ] 2095 2096 test_results = self._read_flags_from_files(fake_argv, False) 2097 self.assertListEqual(expected_results, test_results) 2098 2099 @unittest.skipIf( 2100 os.name == 'nt', 2101 'There is no good way to create an unreadable file on Windows.') 2102 def test_method_flagfiles_no_permissions(self): 2103 """Test that --flagfile raises except on file that is unreadable.""" 2104 tmp_files = self._setup_test_files() 2105 # specify our temp file on the fake cmd line 2106 fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' % 2107 tmp_files[3]) 2108 fake_argv = fake_cmd_line.split(' ') 2109 self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files, 2110 fake_argv, True) 2111 2112 def test_method_flagfiles_not_found(self): 2113 """Test that --flagfile raises except on file that does not exist.""" 2114 tmp_files = self._setup_test_files() 2115 # specify our temp file on the fake cmd line 2116 fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%sNOTEXIST' % 2117 tmp_files[3]) 2118 fake_argv = fake_cmd_line.split(' ') 2119 self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files, 2120 fake_argv, True) 2121 2122 def test_flagfiles_user_path_expansion(self): 2123 """Test that user directory referenced paths are correctly expanded. 2124 2125 Test paths like ~/foo. This test depends on whatever account's running 2126 the unit test to have read/write access to their own home directory, 2127 otherwise it'll FAIL. 2128 """ 2129 fake_flagfile_item_style_1 = '--flagfile=~/foo.file' 2130 fake_flagfile_item_style_2 = '-flagfile=~/foo.file' 2131 2132 expected_results = os.path.expanduser('~/foo.file') 2133 2134 test_results = self.flag_values._extract_filename( 2135 fake_flagfile_item_style_1) 2136 self.assertEqual(expected_results, test_results) 2137 2138 test_results = self.flag_values._extract_filename( 2139 fake_flagfile_item_style_2) 2140 self.assertEqual(expected_results, test_results) 2141 2142 def test_no_touchy_non_flags(self): 2143 """Test that the flags parser does not mutilate arguments. 2144 2145 The arguments are not supposed to be flags 2146 """ 2147 fake_argv = [ 2148 'fooScript', '--unittest_boolflag', 'command', '--command_arg1', 2149 '--UnitTestBoom', '--UnitTestB' 2150 ] 2151 with _use_gnu_getopt(self.flag_values, False): 2152 argv = self.flag_values(fake_argv) 2153 self.assertListEqual(argv, fake_argv[:1] + fake_argv[2:]) 2154 2155 def test_parse_flags_after_args_if_using_gnugetopt(self): 2156 """Test that flags given after arguments are parsed if using gnu_getopt.""" 2157 self.flag_values.set_gnu_getopt() 2158 fake_argv = [ 2159 'fooScript', '--unittest_boolflag', 'command', '--unittest_number=54321' 2160 ] 2161 argv = self.flag_values(fake_argv) 2162 self.assertListEqual(argv, ['fooScript', 'command']) 2163 2164 def test_set_default(self): 2165 """Test changing flag defaults.""" 2166 # Test that set_default changes both the default and the value, 2167 # and that the value is changed when one is given as an option. 2168 self.flag_values.set_default('unittest_message1', 'New value') 2169 self.assertEqual(self.flag_values.unittest_message1, 'New value') 2170 self.assertEqual(self.flag_values['unittest_message1'].default_as_str, 2171 "'New value'") 2172 self.flag_values(['dummyscript', '--unittest_message1=Newer value']) 2173 self.assertEqual(self.flag_values.unittest_message1, 'Newer value') 2174 2175 # Test that setting the default to None works correctly. 2176 self.flag_values.set_default('unittest_number', None) 2177 self.assertEqual(self.flag_values.unittest_number, None) 2178 self.assertEqual(self.flag_values['unittest_number'].default_as_str, None) 2179 self.flag_values(['dummyscript', '--unittest_number=56']) 2180 self.assertEqual(self.flag_values.unittest_number, 56) 2181 2182 # Test that setting the default to zero works correctly. 2183 self.flag_values.set_default('unittest_number', 0) 2184 self.assertEqual(self.flag_values['unittest_number'].default, 0) 2185 self.assertEqual(self.flag_values.unittest_number, 56) 2186 self.assertEqual(self.flag_values['unittest_number'].default_as_str, "'0'") 2187 self.flag_values(['dummyscript', '--unittest_number=56']) 2188 self.assertEqual(self.flag_values.unittest_number, 56) 2189 2190 # Test that setting the default to '' works correctly. 2191 self.flag_values.set_default('unittest_message1', '') 2192 self.assertEqual(self.flag_values['unittest_message1'].default, '') 2193 self.assertEqual(self.flag_values.unittest_message1, 'Newer value') 2194 self.assertEqual(self.flag_values['unittest_message1'].default_as_str, "''") 2195 self.flag_values(['dummyscript', '--unittest_message1=fifty-six']) 2196 self.assertEqual(self.flag_values.unittest_message1, 'fifty-six') 2197 2198 # Test that setting the default to false works correctly. 2199 self.flag_values.set_default('unittest_boolflag', False) 2200 self.assertEqual(self.flag_values.unittest_boolflag, False) 2201 self.assertEqual(self.flag_values['unittest_boolflag'].default_as_str, 2202 "'false'") 2203 self.flag_values(['dummyscript', '--unittest_boolflag=true']) 2204 self.assertEqual(self.flag_values.unittest_boolflag, True) 2205 2206 # Test that setting a list default works correctly. 2207 self.flag_values.set_default('UnitTestList', '4,5,6') 2208 self.assertListEqual(self.flag_values.UnitTestList, ['4', '5', '6']) 2209 self.assertEqual(self.flag_values['UnitTestList'].default_as_str, "'4,5,6'") 2210 self.flag_values(['dummyscript', '--UnitTestList=7,8,9']) 2211 self.assertListEqual(self.flag_values.UnitTestList, ['7', '8', '9']) 2212 2213 # Test that setting invalid defaults raises exceptions 2214 with self.assertRaises(flags.IllegalFlagValueError): 2215 self.flag_values.set_default('unittest_number', 'oops') 2216 with self.assertRaises(flags.IllegalFlagValueError): 2217 self.flag_values.set_default('unittest_number', -1) 2218 2219 2220class FlagsParsingTest(absltest.TestCase): 2221 """Testing different aspects of parsing: '-f' vs '--flag', etc.""" 2222 2223 def setUp(self): 2224 self.flag_values = flags.FlagValues() 2225 2226 def test_two_dash_arg_first(self): 2227 flags.DEFINE_string( 2228 'twodash_name', 'Bob', 'namehelp', flag_values=self.flag_values) 2229 flags.DEFINE_string( 2230 'twodash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values) 2231 argv = ('./program', '--', '--twodash_name=Harry') 2232 argv = self.flag_values(argv) 2233 self.assertEqual('Bob', self.flag_values.twodash_name) 2234 self.assertEqual(argv[1], '--twodash_name=Harry') 2235 2236 def test_two_dash_arg_middle(self): 2237 flags.DEFINE_string( 2238 'twodash2_name', 'Bob', 'namehelp', flag_values=self.flag_values) 2239 flags.DEFINE_string( 2240 'twodash2_blame', 'Rob', 'blamehelp', flag_values=self.flag_values) 2241 argv = ('./program', '--twodash2_blame=Larry', '--', 2242 '--twodash2_name=Harry') 2243 argv = self.flag_values(argv) 2244 self.assertEqual('Bob', self.flag_values.twodash2_name) 2245 self.assertEqual('Larry', self.flag_values.twodash2_blame) 2246 self.assertEqual(argv[1], '--twodash2_name=Harry') 2247 2248 def test_one_dash_arg_first(self): 2249 flags.DEFINE_string( 2250 'onedash_name', 'Bob', 'namehelp', flag_values=self.flag_values) 2251 flags.DEFINE_string( 2252 'onedash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values) 2253 argv = ('./program', '-', '--onedash_name=Harry') 2254 with _use_gnu_getopt(self.flag_values, False): 2255 argv = self.flag_values(argv) 2256 self.assertEqual(len(argv), 3) 2257 self.assertEqual(argv[1], '-') 2258 self.assertEqual(argv[2], '--onedash_name=Harry') 2259 2260 def test_required_flag_not_specified(self): 2261 flags.DEFINE_string( 2262 'str_flag', 2263 default=None, 2264 help='help', 2265 required=True, 2266 flag_values=self.flag_values) 2267 argv = ('./program',) 2268 with _use_gnu_getopt(self.flag_values, False): 2269 with self.assertRaises(flags.IllegalFlagValueError): 2270 self.flag_values(argv) 2271 2272 def test_required_arg_works_with_other_validators(self): 2273 flags.DEFINE_integer( 2274 'int_flag', 2275 default=None, 2276 help='help', 2277 required=True, 2278 lower_bound=4, 2279 flag_values=self.flag_values) 2280 argv = ('./program', '--int_flag=2') 2281 with _use_gnu_getopt(self.flag_values, False): 2282 with self.assertRaises(flags.IllegalFlagValueError): 2283 self.flag_values(argv) 2284 2285 def test_unrecognized_flags(self): 2286 flags.DEFINE_string('name', 'Bob', 'namehelp', flag_values=self.flag_values) 2287 # Unknown flag --nosuchflag 2288 try: 2289 argv = ('./program', '--nosuchflag', '--name=Bob', 'extra') 2290 self.flag_values(argv) 2291 raise AssertionError('Unknown flag exception not raised') 2292 except flags.UnrecognizedFlagError as e: 2293 self.assertEqual(e.flagname, 'nosuchflag') 2294 self.assertEqual(e.flagvalue, '--nosuchflag') 2295 2296 # Unknown flag -w (short option) 2297 try: 2298 argv = ('./program', '-w', '--name=Bob', 'extra') 2299 self.flag_values(argv) 2300 raise AssertionError('Unknown flag exception not raised') 2301 except flags.UnrecognizedFlagError as e: 2302 self.assertEqual(e.flagname, 'w') 2303 self.assertEqual(e.flagvalue, '-w') 2304 2305 # Unknown flag --nosuchflagwithparam=foo 2306 try: 2307 argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 'extra') 2308 self.flag_values(argv) 2309 raise AssertionError('Unknown flag exception not raised') 2310 except flags.UnrecognizedFlagError as e: 2311 self.assertEqual(e.flagname, 'nosuchflagwithparam') 2312 self.assertEqual(e.flagvalue, '--nosuchflagwithparam=foo') 2313 2314 # Allow unknown flag --nosuchflag if specified with undefok 2315 argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchflag', 2316 'extra') 2317 argv = self.flag_values(argv) 2318 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2319 self.assertEqual(argv[0], './program', 'program name not preserved') 2320 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2321 2322 # Allow unknown flag --noboolflag if undefok=boolflag is specified 2323 argv = ('./program', '--noboolflag', '--name=Bob', '--undefok=boolflag', 2324 'extra') 2325 argv = self.flag_values(argv) 2326 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2327 self.assertEqual(argv[0], './program', 'program name not preserved') 2328 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2329 2330 # But not if the flagname is misspelled: 2331 try: 2332 argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchfla', 2333 'extra') 2334 self.flag_values(argv) 2335 raise AssertionError('Unknown flag exception not raised') 2336 except flags.UnrecognizedFlagError as e: 2337 self.assertEqual(e.flagname, 'nosuchflag') 2338 2339 try: 2340 argv = ('./program', '--nosuchflag', '--name=Bob', 2341 '--undefok=nosuchflagg', 'extra') 2342 self.flag_values(argv) 2343 raise AssertionError('Unknown flag exception not raised') 2344 except flags.UnrecognizedFlagError as e: 2345 self.assertEqual(e.flagname, 'nosuchflag') 2346 2347 # Allow unknown short flag -w if specified with undefok 2348 argv = ('./program', '-w', '--name=Bob', '--undefok=w', 'extra') 2349 argv = self.flag_values(argv) 2350 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2351 self.assertEqual(argv[0], './program', 'program name not preserved') 2352 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2353 2354 # Allow unknown flag --nosuchflagwithparam=foo if specified 2355 # with undefok 2356 argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 2357 '--undefok=nosuchflagwithparam', 'extra') 2358 argv = self.flag_values(argv) 2359 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2360 self.assertEqual(argv[0], './program', 'program name not preserved') 2361 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2362 2363 # Even if undefok specifies multiple flags 2364 argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo', 2365 '--name=Bob', '--undefok=nosuchflag,w,nosuchflagwithparam', 'extra') 2366 argv = self.flag_values(argv) 2367 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2368 self.assertEqual(argv[0], './program', 'program name not preserved') 2369 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2370 2371 # However, not if undefok doesn't specify the flag 2372 try: 2373 argv = ('./program', '--nosuchflag', '--name=Bob', 2374 '--undefok=another_such', 'extra') 2375 self.flag_values(argv) 2376 raise AssertionError('Unknown flag exception not raised') 2377 except flags.UnrecognizedFlagError as e: 2378 self.assertEqual(e.flagname, 'nosuchflag') 2379 2380 # Make sure --undefok doesn't mask other option errors. 2381 try: 2382 # Provide an option requiring a parameter but not giving it one. 2383 argv = ('./program', '--undefok=name', '--name') 2384 self.flag_values(argv) 2385 raise AssertionError('Missing option parameter exception not raised') 2386 except flags.UnrecognizedFlagError: 2387 raise AssertionError('Wrong kind of error exception raised') 2388 except flags.Error: 2389 pass 2390 2391 # Test --undefok <list> 2392 argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo', 2393 '--name=Bob', '--undefok', 'nosuchflag,w,nosuchflagwithparam', 2394 'extra') 2395 argv = self.flag_values(argv) 2396 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2397 self.assertEqual(argv[0], './program', 'program name not preserved') 2398 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2399 2400 # Test incorrect --undefok with no value. 2401 argv = ('./program', '--name=Bob', '--undefok') 2402 with self.assertRaises(flags.Error): 2403 self.flag_values(argv) 2404 2405 2406class NonGlobalFlagsTest(absltest.TestCase): 2407 2408 def test_nonglobal_flags(self): 2409 """Test use of non-global FlagValues.""" 2410 nonglobal_flags = flags.FlagValues() 2411 flags.DEFINE_string('nonglobal_flag', 'Bob', 'flaghelp', nonglobal_flags) 2412 argv = ('./program', '--nonglobal_flag=Mary', 'extra') 2413 argv = nonglobal_flags(argv) 2414 self.assertEqual(len(argv), 2, 'wrong number of arguments pulled') 2415 self.assertEqual(argv[0], './program', 'program name not preserved') 2416 self.assertEqual(argv[1], 'extra', 'extra argument not preserved') 2417 self.assertEqual(nonglobal_flags['nonglobal_flag'].value, 'Mary') 2418 2419 def test_unrecognized_nonglobal_flags(self): 2420 """Test unrecognized non-global flags.""" 2421 nonglobal_flags = flags.FlagValues() 2422 argv = ('./program', '--nosuchflag') 2423 try: 2424 argv = nonglobal_flags(argv) 2425 raise AssertionError('Unknown flag exception not raised') 2426 except flags.UnrecognizedFlagError as e: 2427 self.assertEqual(e.flagname, 'nosuchflag') 2428 2429 argv = ('./program', '--nosuchflag', '--undefok=nosuchflag') 2430 2431 argv = nonglobal_flags(argv) 2432 self.assertEqual(len(argv), 1, 'wrong number of arguments pulled') 2433 self.assertEqual(argv[0], './program', 'program name not preserved') 2434 2435 def test_create_flag_errors(self): 2436 # Since the exception classes are exposed, nothing stops users 2437 # from creating their own instances. This test makes sure that 2438 # people modifying the flags module understand that the external 2439 # mechanisms for creating the exceptions should continue to work. 2440 _ = flags.Error() 2441 _ = flags.Error('message') 2442 _ = flags.DuplicateFlagError() 2443 _ = flags.DuplicateFlagError('message') 2444 _ = flags.IllegalFlagValueError() 2445 _ = flags.IllegalFlagValueError('message') 2446 2447 def test_flag_values_del_attr(self): 2448 """Checks that del self.flag_values.flag_id works.""" 2449 default_value = 'default value for test_flag_values_del_attr' 2450 # 1. Declare and delete a flag with no short name. 2451 flag_values = flags.FlagValues() 2452 flags.DEFINE_string( 2453 'delattr_foo', default_value, 'A simple flag.', flag_values=flag_values) 2454 2455 flag_values.mark_as_parsed() 2456 self.assertEqual(flag_values.delattr_foo, default_value) 2457 flag_obj = flag_values['delattr_foo'] 2458 # We also check that _FlagIsRegistered works as expected :) 2459 self.assertTrue(flag_values._flag_is_registered(flag_obj)) 2460 del flag_values.delattr_foo 2461 self.assertFalse('delattr_foo' in flag_values._flags()) 2462 self.assertFalse(flag_values._flag_is_registered(flag_obj)) 2463 # If the previous del FLAGS.delattr_foo did not work properly, the 2464 # next definition will trigger a redefinition error. 2465 flags.DEFINE_integer( 2466 'delattr_foo', 3, 'A simple flag.', flag_values=flag_values) 2467 del flag_values.delattr_foo 2468 2469 self.assertFalse('delattr_foo' in flag_values) 2470 2471 # 2. Declare and delete a flag with a short name. 2472 flags.DEFINE_string( 2473 'delattr_bar', 2474 default_value, 2475 'flag with short name', 2476 short_name='x5', 2477 flag_values=flag_values) 2478 flag_obj = flag_values['delattr_bar'] 2479 self.assertTrue(flag_values._flag_is_registered(flag_obj)) 2480 del flag_values.x5 2481 self.assertTrue(flag_values._flag_is_registered(flag_obj)) 2482 del flag_values.delattr_bar 2483 self.assertFalse(flag_values._flag_is_registered(flag_obj)) 2484 2485 # 3. Just like 2, but del flag_values.name last 2486 flags.DEFINE_string( 2487 'delattr_bar', 2488 default_value, 2489 'flag with short name', 2490 short_name='x5', 2491 flag_values=flag_values) 2492 flag_obj = flag_values['delattr_bar'] 2493 self.assertTrue(flag_values._flag_is_registered(flag_obj)) 2494 del flag_values.delattr_bar 2495 self.assertTrue(flag_values._flag_is_registered(flag_obj)) 2496 del flag_values.x5 2497 self.assertFalse(flag_values._flag_is_registered(flag_obj)) 2498 2499 self.assertFalse('delattr_bar' in flag_values) 2500 self.assertFalse('x5' in flag_values) 2501 2502 def test_list_flag_format(self): 2503 """Tests for correctly-formatted list flags.""" 2504 fv = flags.FlagValues() 2505 flags.DEFINE_list('listflag', '', 'A list of arguments', flag_values=fv) 2506 2507 def _check_parsing(listval): 2508 """Parse a particular value for our test flag, --listflag.""" 2509 argv = fv(['./program', '--listflag=' + listval, 'plain-arg']) 2510 self.assertEqual(['./program', 'plain-arg'], argv) 2511 return fv.listflag 2512 2513 # Basic success case 2514 self.assertEqual(_check_parsing('foo,bar'), ['foo', 'bar']) 2515 # Success case: newline in argument is quoted. 2516 self.assertEqual(_check_parsing('"foo","bar\nbar"'), ['foo', 'bar\nbar']) 2517 # Failure case: newline in argument is unquoted. 2518 self.assertRaises(flags.IllegalFlagValueError, _check_parsing, 2519 '"foo",bar\nbar') 2520 # Failure case: unmatched ". 2521 self.assertRaises(flags.IllegalFlagValueError, _check_parsing, 2522 '"foo,barbar') 2523 2524 def test_flag_definition_via_setitem(self): 2525 with self.assertRaises(flags.IllegalFlagValueError): 2526 flag_values = flags.FlagValues() 2527 flag_values['flag_name'] = 'flag_value' # type: ignore 2528 2529 2530class SetDefaultTest(absltest.TestCase): 2531 2532 def setUp(self): 2533 super().setUp() 2534 self.flag_values = flags.FlagValues() 2535 2536 def test_success(self): 2537 int_holder = flags.DEFINE_integer( 2538 'an_int', 1, 'an int', flag_values=self.flag_values) 2539 2540 flags.set_default(int_holder, 2) 2541 self.flag_values.mark_as_parsed() 2542 2543 self.assertEqual(int_holder.value, 2) 2544 2545 def test_update_after_parse(self): 2546 int_holder = flags.DEFINE_integer( 2547 'an_int', 1, 'an int', flag_values=self.flag_values) 2548 2549 self.flag_values.mark_as_parsed() 2550 flags.set_default(int_holder, 2) 2551 2552 self.assertEqual(int_holder.value, 2) 2553 2554 def test_overridden_by_explicit_assignment(self): 2555 int_holder = flags.DEFINE_integer( 2556 'an_int', 1, 'an int', flag_values=self.flag_values) 2557 2558 self.flag_values.mark_as_parsed() 2559 self.flag_values.an_int = 3 2560 flags.set_default(int_holder, 2) 2561 2562 self.assertEqual(int_holder.value, 3) 2563 2564 def test_restores_back_to_none(self): 2565 int_holder = flags.DEFINE_integer( 2566 'an_int', None, 'an int', flag_values=self.flag_values) 2567 2568 self.flag_values.mark_as_parsed() 2569 flags.set_default(int_holder, 3) 2570 flags.set_default(int_holder, None) 2571 2572 self.assertIsNone(int_holder.value) 2573 2574 def test_failure_on_invalid_type(self): 2575 int_holder = flags.DEFINE_integer( 2576 'an_int', 1, 'an int', flag_values=self.flag_values) 2577 2578 self.flag_values.mark_as_parsed() 2579 2580 with self.assertRaises(flags.IllegalFlagValueError): 2581 flags.set_default(int_holder, 'a') # type: ignore 2582 2583 def test_failure_on_type_protected_none_default(self): 2584 int_holder = flags.DEFINE_integer( 2585 'an_int', 1, 'an int', flag_values=self.flag_values) 2586 2587 self.flag_values.mark_as_parsed() 2588 2589 flags.set_default(int_holder, None) # type: ignore 2590 2591 with self.assertRaises(flags.IllegalFlagValueError): 2592 _ = int_holder.value # Will also fail on later access. 2593 2594 2595class OverrideValueTest(absltest.TestCase): 2596 2597 def setUp(self): 2598 super().setUp() 2599 self.flag_values = flags.FlagValues() 2600 2601 def test_success(self): 2602 int_holder = flags.DEFINE_integer( 2603 'an_int', 1, 'an int', flag_values=self.flag_values 2604 ) 2605 2606 flags.override_value(int_holder, 2) 2607 self.flag_values.mark_as_parsed() 2608 2609 self.assertEqual(int_holder.value, 2) 2610 2611 def test_update_after_parse(self): 2612 int_holder = flags.DEFINE_integer( 2613 'an_int', 1, 'an int', flag_values=self.flag_values 2614 ) 2615 2616 self.flag_values.mark_as_parsed() 2617 flags.override_value(int_holder, 2) 2618 2619 self.assertEqual(int_holder.value, 2) 2620 2621 def test_overrides_explicit_assignment(self): 2622 int_holder = flags.DEFINE_integer( 2623 'an_int', 1, 'an int', flag_values=self.flag_values 2624 ) 2625 2626 self.flag_values.mark_as_parsed() 2627 self.flag_values.an_int = 3 2628 flags.override_value(int_holder, 2) 2629 2630 self.assertEqual(int_holder.value, 2) 2631 2632 def test_overriden_by_explicit_assignment(self): 2633 int_holder = flags.DEFINE_integer( 2634 'an_int', 1, 'an int', flag_values=self.flag_values 2635 ) 2636 2637 self.flag_values.mark_as_parsed() 2638 flags.override_value(int_holder, 2) 2639 self.flag_values.an_int = 3 2640 2641 self.assertEqual(int_holder.value, 3) 2642 2643 def test_multi_flag(self): 2644 multi_holder = flags.DEFINE_multi_string( 2645 'strs', [], 'some strs', flag_values=self.flag_values 2646 ) 2647 2648 flags.override_value(multi_holder, ['a', 'b']) 2649 self.flag_values.mark_as_parsed() 2650 2651 self.assertEqual(multi_holder.value, ['a', 'b']) 2652 2653 def test_failure_on_invalid_type(self): 2654 int_holder = flags.DEFINE_integer( 2655 'an_int', 1, 'an int', flag_values=self.flag_values 2656 ) 2657 2658 self.flag_values.mark_as_parsed() 2659 2660 with self.assertRaises(flags.IllegalFlagValueError): 2661 flags.override_value(int_holder, 'a') # pytype: disable=wrong-arg-types 2662 2663 self.assertEqual(int_holder.value, 1) 2664 2665 def test_failure_on_unparsed_value(self): 2666 int_holder = flags.DEFINE_integer( 2667 'an_int', 1, 'an int', flag_values=self.flag_values 2668 ) 2669 2670 self.flag_values.mark_as_parsed() 2671 2672 with self.assertRaises(flags.IllegalFlagValueError): 2673 flags.override_value(int_holder, '2') # pytype: disable=wrong-arg-types 2674 2675 def test_failure_on_parser_rejection(self): 2676 int_holder = flags.DEFINE_integer( 2677 'an_int', 1, 'an int', flag_values=self.flag_values, upper_bound=5 2678 ) 2679 2680 self.flag_values.mark_as_parsed() 2681 2682 with self.assertRaises(flags.IllegalFlagValueError): 2683 flags.override_value(int_holder, 6) 2684 2685 self.assertEqual(int_holder.value, 1) 2686 2687 def test_failure_on_validator_rejection(self): 2688 int_holder = flags.DEFINE_integer( 2689 'an_int', 1, 'an int', flag_values=self.flag_values 2690 ) 2691 flags.register_validator( 2692 int_holder.name, lambda x: x < 5, flag_values=self.flag_values 2693 ) 2694 2695 self.flag_values.mark_as_parsed() 2696 2697 with self.assertRaises(flags.IllegalFlagValueError): 2698 flags.override_value(int_holder, 6) 2699 2700 self.assertEqual(int_holder.value, 1) 2701 2702 2703class KeyFlagsTest(absltest.TestCase): 2704 2705 def setUp(self): 2706 self.flag_values = flags.FlagValues() 2707 2708 def _get_names_of_defined_flags(self, module, flag_values): 2709 """Returns the list of names of flags defined by a module. 2710 2711 Auxiliary for the test_key_flags* methods. 2712 2713 Args: 2714 module: A module object or a string module name. 2715 flag_values: A FlagValues object. 2716 2717 Returns: 2718 A list of strings. 2719 """ 2720 return [f.name for f in flag_values.get_flags_for_module(module)] 2721 2722 def _get_names_of_key_flags(self, module, flag_values): 2723 """Returns the list of names of key flags for a module. 2724 2725 Auxiliary for the test_key_flags* methods. 2726 2727 Args: 2728 module: A module object or a string module name. 2729 flag_values: A FlagValues object. 2730 2731 Returns: 2732 A list of strings. 2733 """ 2734 return [f.name for f in flag_values.get_key_flags_for_module(module)] 2735 2736 def _assert_lists_have_same_elements(self, list_1, list_2): 2737 # Checks that two lists have the same elements with the same 2738 # multiplicity, in possibly different order. 2739 list_1 = list(list_1) 2740 list_1.sort() 2741 list_2 = list(list_2) 2742 list_2.sort() 2743 self.assertListEqual(list_1, list_2) 2744 2745 def test_key_flags(self): 2746 flag_values = flags.FlagValues() 2747 # Before starting any testing, make sure no flags are already 2748 # defined for module_foo and module_bar. 2749 self.assertListEqual( 2750 self._get_names_of_key_flags(module_foo, flag_values), []) 2751 self.assertListEqual( 2752 self._get_names_of_key_flags(module_bar, flag_values), []) 2753 self.assertListEqual( 2754 self._get_names_of_defined_flags(module_foo, flag_values), []) 2755 self.assertListEqual( 2756 self._get_names_of_defined_flags(module_bar, flag_values), []) 2757 2758 # Defines a few flags in module_foo and module_bar. 2759 module_foo.define_flags(flag_values=flag_values) 2760 2761 try: 2762 # Part 1. Check that all flags defined by module_foo are key for 2763 # that module, and similarly for module_bar. 2764 for module in [module_foo, module_bar]: 2765 self._assert_lists_have_same_elements( 2766 flag_values.get_flags_for_module(module), 2767 flag_values.get_key_flags_for_module(module)) 2768 # Also check that each module defined the expected flags. 2769 self._assert_lists_have_same_elements( 2770 self._get_names_of_defined_flags(module, flag_values), 2771 module.names_of_defined_flags()) 2772 2773 # Part 2. Check that flags.declare_key_flag works fine. 2774 # Declare that some flags from module_bar are key for 2775 # module_foo. 2776 module_foo.declare_key_flags(flag_values=flag_values) 2777 2778 # Check that module_foo has the expected list of defined flags. 2779 self._assert_lists_have_same_elements( 2780 self._get_names_of_defined_flags(module_foo, flag_values), 2781 module_foo.names_of_defined_flags()) 2782 2783 # Check that module_foo has the expected list of key flags. 2784 self._assert_lists_have_same_elements( 2785 self._get_names_of_key_flags(module_foo, flag_values), 2786 module_foo.names_of_declared_key_flags()) 2787 2788 # Part 3. Check that flags.adopt_module_key_flags works fine. 2789 # Trigger a call to flags.adopt_module_key_flags(module_bar) 2790 # inside module_foo. This should declare a few more key 2791 # flags in module_foo. 2792 module_foo.declare_extra_key_flags(flag_values=flag_values) 2793 2794 # Check that module_foo has the expected list of key flags. 2795 self._assert_lists_have_same_elements( 2796 self._get_names_of_key_flags(module_foo, flag_values), 2797 module_foo.names_of_declared_key_flags() + 2798 module_foo.names_of_declared_extra_key_flags()) 2799 finally: 2800 module_foo.remove_flags(flag_values=flag_values) 2801 2802 def test_key_flags_with_non_default_flag_values_object(self): 2803 # Check that key flags work even when we use a FlagValues object 2804 # that is not the default flags.self.flag_values object. Otherwise, this 2805 # test is similar to test_key_flags, but it uses only module_bar. 2806 # The other test module (module_foo) uses only the default values 2807 # for the flag_values keyword arguments. This way, test_key_flags 2808 # and this method test both the default FlagValues, the explicitly 2809 # specified one, and a mixed usage of the two. 2810 2811 # A brand-new FlagValues object, to use instead of flags.self.flag_values. 2812 fv = flags.FlagValues() 2813 2814 # Before starting any testing, make sure no flags are already 2815 # defined for module_foo and module_bar. 2816 self.assertListEqual(self._get_names_of_key_flags(module_bar, fv), []) 2817 self.assertListEqual(self._get_names_of_defined_flags(module_bar, fv), []) 2818 2819 module_bar.define_flags(flag_values=fv) 2820 2821 # Check that all flags defined by module_bar are key for that 2822 # module, and that module_bar defined the expected flags. 2823 self._assert_lists_have_same_elements( 2824 fv.get_flags_for_module(module_bar), 2825 fv.get_key_flags_for_module(module_bar)) 2826 self._assert_lists_have_same_elements( 2827 self._get_names_of_defined_flags(module_bar, fv), 2828 module_bar.names_of_defined_flags()) 2829 2830 # Pick two flags from module_bar, declare them as key for the 2831 # current (i.e., main) module (via flags.declare_key_flag), and 2832 # check that we get the expected effect. The important thing is 2833 # that we always use flags_values=fv (instead of the default 2834 # self.flag_values). 2835 main_module = sys.argv[0] 2836 names_of_flags_defined_by_bar = module_bar.names_of_defined_flags() 2837 flag_name_0 = names_of_flags_defined_by_bar[0] 2838 flag_name_2 = names_of_flags_defined_by_bar[2] 2839 2840 flags.declare_key_flag(flag_name_0, flag_values=fv) 2841 self._assert_lists_have_same_elements( 2842 self._get_names_of_key_flags(main_module, fv), [flag_name_0]) 2843 2844 flags.declare_key_flag(flag_name_2, flag_values=fv) 2845 self._assert_lists_have_same_elements( 2846 self._get_names_of_key_flags(main_module, fv), 2847 [flag_name_0, flag_name_2]) 2848 2849 # Try with a special (not user-defined) flag too: 2850 flags.declare_key_flag('undefok', flag_values=fv) 2851 self._assert_lists_have_same_elements( 2852 self._get_names_of_key_flags(main_module, fv), 2853 [flag_name_0, flag_name_2, 'undefok']) 2854 2855 flags.adopt_module_key_flags(module_bar, fv) 2856 self._assert_lists_have_same_elements( 2857 self._get_names_of_key_flags(main_module, fv), 2858 names_of_flags_defined_by_bar + ['undefok']) 2859 2860 # Adopt key flags from the flags module itself. 2861 flags.adopt_module_key_flags(flags, flag_values=fv) 2862 self._assert_lists_have_same_elements( 2863 self._get_names_of_key_flags(main_module, fv), 2864 names_of_flags_defined_by_bar + ['flagfile', 'undefok']) 2865 2866 def test_key_flags_with_flagholders(self): 2867 main_module = sys.argv[0] 2868 2869 self.assertListEqual( 2870 self._get_names_of_key_flags(main_module, self.flag_values), []) 2871 self.assertListEqual( 2872 self._get_names_of_defined_flags(main_module, self.flag_values), []) 2873 2874 int_holder = flags.DEFINE_integer( 2875 'main_module_int_fg', 2876 1, 2877 'Integer flag in the main module.', 2878 flag_values=self.flag_values) 2879 2880 flags.declare_key_flag(int_holder, self.flag_values) 2881 2882 self.assertCountEqual( 2883 self.flag_values.get_flags_for_module(main_module), 2884 self.flag_values.get_key_flags_for_module(main_module)) 2885 2886 bool_holder = flags.DEFINE_boolean( 2887 'main_module_bool_fg', 2888 False, 2889 'Boolean flag in the main module.', 2890 flag_values=self.flag_values) 2891 2892 flags.declare_key_flag(bool_holder) # omitted flag_values 2893 2894 self.assertCountEqual( 2895 self.flag_values.get_flags_for_module(main_module), 2896 self.flag_values.get_key_flags_for_module(main_module)) 2897 2898 self.assertLen(self.flag_values.get_flags_for_module(main_module), 2) 2899 2900 def test_main_module_help_with_key_flags(self): 2901 # Similar to test_main_module_help, but this time we make sure to 2902 # declare some key flags. 2903 2904 # Safety check that the main module does not declare any flags 2905 # at the beginning of this test. 2906 expected_help = '' 2907 self.assertMultiLineEqual(expected_help, 2908 self.flag_values.main_module_help()) 2909 2910 # Define one flag in this main module and some flags in modules 2911 # a and b. Also declare one flag from module a and one flag 2912 # from module b as key flags for the main module. 2913 flags.DEFINE_integer( 2914 'main_module_int_fg', 2915 1, 2916 'Integer flag in the main module.', 2917 flag_values=self.flag_values) 2918 2919 try: 2920 main_module_int_fg_help = ( 2921 ' --main_module_int_fg: Integer flag in the main module.\n' 2922 " (default: '1')\n" 2923 ' (an integer)') 2924 2925 expected_help += '\n%s:\n%s' % (sys.argv[0], main_module_int_fg_help) 2926 self.assertMultiLineEqual(expected_help, 2927 self.flag_values.main_module_help()) 2928 2929 # The following call should be a no-op: any flag declared by a 2930 # module is automatically key for that module. 2931 flags.declare_key_flag('main_module_int_fg', flag_values=self.flag_values) 2932 self.assertMultiLineEqual(expected_help, 2933 self.flag_values.main_module_help()) 2934 2935 # The definition of a few flags in an imported module should not 2936 # change the main module help. 2937 module_foo.define_flags(flag_values=self.flag_values) 2938 self.assertMultiLineEqual(expected_help, 2939 self.flag_values.main_module_help()) 2940 2941 flags.declare_key_flag('tmod_foo_bool', flag_values=self.flag_values) 2942 tmod_foo_bool_help = ( 2943 ' --[no]tmod_foo_bool: Boolean flag from module foo.\n' 2944 " (default: 'true')") 2945 expected_help += '\n' + tmod_foo_bool_help 2946 self.assertMultiLineEqual(expected_help, 2947 self.flag_values.main_module_help()) 2948 2949 flags.declare_key_flag('tmod_bar_z', flag_values=self.flag_values) 2950 tmod_bar_z_help = ( 2951 ' --[no]tmod_bar_z: Another boolean flag from module bar.\n' 2952 " (default: 'false')") 2953 # Unfortunately, there is some flag sorting inside 2954 # main_module_help, so we can't keep incrementally extending 2955 # the expected_help string ... 2956 expected_help = ('\n%s:\n%s\n%s\n%s' % 2957 (sys.argv[0], main_module_int_fg_help, tmod_bar_z_help, 2958 tmod_foo_bool_help)) 2959 self.assertMultiLineEqual(self.flag_values.main_module_help(), 2960 expected_help) 2961 2962 finally: 2963 # At the end, delete all the flag information we created. 2964 self.flag_values.__delattr__('main_module_int_fg') 2965 module_foo.remove_flags(flag_values=self.flag_values) 2966 2967 def test_adoptmodule_key_flags(self): 2968 # Check that adopt_module_key_flags raises an exception when 2969 # called with a module name (as opposed to a module object). 2970 self.assertRaises(flags.Error, flags.adopt_module_key_flags, 'pyglib.app') 2971 2972 def test_disclaimkey_flags(self): 2973 original_disclaim_module_ids = _helpers.disclaim_module_ids 2974 _helpers.disclaim_module_ids = set(_helpers.disclaim_module_ids) 2975 try: 2976 module_bar.disclaim_key_flags() 2977 module_foo.define_bar_flags(flag_values=self.flag_values) 2978 module_name = self.flag_values.find_module_defining_flag('tmod_bar_x') 2979 self.assertEqual(module_foo.__name__, module_name) 2980 finally: 2981 _helpers.disclaim_module_ids = original_disclaim_module_ids 2982 2983 2984class FindModuleTest(absltest.TestCase): 2985 """Testing methods that find a module that defines a given flag.""" 2986 2987 def test_find_module_defining_flag(self): 2988 self.assertEqual( 2989 'default', 2990 FLAGS.find_module_defining_flag('__NON_EXISTENT_FLAG__', 'default')) 2991 self.assertEqual(module_baz.__name__, 2992 FLAGS.find_module_defining_flag('tmod_baz_x')) 2993 2994 def test_find_module_id_defining_flag(self): 2995 self.assertEqual( 2996 'default', 2997 FLAGS.find_module_id_defining_flag('__NON_EXISTENT_FLAG__', 'default')) 2998 self.assertEqual( 2999 id(module_baz), FLAGS.find_module_id_defining_flag('tmod_baz_x')) 3000 3001 def test_find_module_defining_flag_passing_module_name(self): 3002 my_flags = flags.FlagValues() 3003 module_name = sys.__name__ # Must use an existing module. 3004 flags.DEFINE_boolean( 3005 'flag_name', 3006 True, 3007 'Flag with a different module name.', 3008 flag_values=my_flags, 3009 module_name=module_name) 3010 self.assertEqual(module_name, 3011 my_flags.find_module_defining_flag('flag_name')) 3012 3013 def test_find_module_id_defining_flag_passing_module_name(self): 3014 my_flags = flags.FlagValues() 3015 module_name = sys.__name__ # Must use an existing module. 3016 flags.DEFINE_boolean( 3017 'flag_name', 3018 True, 3019 'Flag with a different module name.', 3020 flag_values=my_flags, 3021 module_name=module_name) 3022 self.assertEqual( 3023 id(sys), my_flags.find_module_id_defining_flag('flag_name')) 3024 3025 3026class FlagsErrorMessagesTest(absltest.TestCase): 3027 """Testing special cases for integer and float flags error messages.""" 3028 3029 def setUp(self): 3030 self.flag_values = flags.FlagValues() 3031 3032 def test_integer_error_text(self): 3033 # Make sure we get proper error text 3034 flags.DEFINE_integer( 3035 'positive', 3036 4, 3037 'non-negative flag', 3038 lower_bound=1, 3039 flag_values=self.flag_values) 3040 flags.DEFINE_integer( 3041 'non_negative', 3042 4, 3043 'positive flag', 3044 lower_bound=0, 3045 flag_values=self.flag_values) 3046 flags.DEFINE_integer( 3047 'negative', 3048 -4, 3049 'negative flag', 3050 upper_bound=-1, 3051 flag_values=self.flag_values) 3052 flags.DEFINE_integer( 3053 'non_positive', 3054 -4, 3055 'non-positive flag', 3056 upper_bound=0, 3057 flag_values=self.flag_values) 3058 flags.DEFINE_integer( 3059 'greater', 3060 19, 3061 'greater-than flag', 3062 lower_bound=4, 3063 flag_values=self.flag_values) 3064 flags.DEFINE_integer( 3065 'smaller', 3066 -19, 3067 'smaller-than flag', 3068 upper_bound=4, 3069 flag_values=self.flag_values) 3070 flags.DEFINE_integer( 3071 'usual', 3072 4, 3073 'usual flag', 3074 lower_bound=0, 3075 upper_bound=10000, 3076 flag_values=self.flag_values) 3077 flags.DEFINE_integer( 3078 'another_usual', 3079 0, 3080 'usual flag', 3081 lower_bound=-1, 3082 upper_bound=1, 3083 flag_values=self.flag_values) 3084 3085 self._check_error_message('positive', -4, 'a positive integer') 3086 self._check_error_message('non_negative', -4, 'a non-negative integer') 3087 self._check_error_message('negative', 0, 'a negative integer') 3088 self._check_error_message('non_positive', 4, 'a non-positive integer') 3089 self._check_error_message('usual', -4, 'an integer in the range [0, 10000]') 3090 self._check_error_message('another_usual', 4, 3091 'an integer in the range [-1, 1]') 3092 self._check_error_message('greater', -5, 'integer >= 4') 3093 self._check_error_message('smaller', 5, 'integer <= 4') 3094 3095 def test_float_error_text(self): 3096 flags.DEFINE_float( 3097 'positive', 3098 4, 3099 'non-negative flag', 3100 lower_bound=1, 3101 flag_values=self.flag_values) 3102 flags.DEFINE_float( 3103 'non_negative', 3104 4, 3105 'positive flag', 3106 lower_bound=0, 3107 flag_values=self.flag_values) 3108 flags.DEFINE_float( 3109 'negative', 3110 -4, 3111 'negative flag', 3112 upper_bound=-1, 3113 flag_values=self.flag_values) 3114 flags.DEFINE_float( 3115 'non_positive', 3116 -4, 3117 'non-positive flag', 3118 upper_bound=0, 3119 flag_values=self.flag_values) 3120 flags.DEFINE_float( 3121 'greater', 3122 19, 3123 'greater-than flag', 3124 lower_bound=4, 3125 flag_values=self.flag_values) 3126 flags.DEFINE_float( 3127 'smaller', 3128 -19, 3129 'smaller-than flag', 3130 upper_bound=4, 3131 flag_values=self.flag_values) 3132 flags.DEFINE_float( 3133 'usual', 3134 4, 3135 'usual flag', 3136 lower_bound=0, 3137 upper_bound=10000, 3138 flag_values=self.flag_values) 3139 flags.DEFINE_float( 3140 'another_usual', 3141 0, 3142 'usual flag', 3143 lower_bound=-1, 3144 upper_bound=1, 3145 flag_values=self.flag_values) 3146 3147 self._check_error_message('positive', 0.5, 'number >= 1') 3148 self._check_error_message('non_negative', -4.0, 'a non-negative number') 3149 self._check_error_message('negative', 0.5, 'number <= -1') 3150 self._check_error_message('non_positive', 4.0, 'a non-positive number') 3151 self._check_error_message('usual', -4.0, 'a number in the range [0, 10000]') 3152 self._check_error_message('another_usual', 4.0, 3153 'a number in the range [-1, 1]') 3154 self._check_error_message('smaller', 5.0, 'number <= 4') 3155 3156 def _check_error_message(self, flag_name, flag_value, 3157 expected_message_suffix): 3158 """Set a flag to a given value and make sure we get expected message.""" 3159 3160 try: 3161 self.flag_values.__setattr__(flag_name, flag_value) 3162 raise AssertionError('Bounds exception not raised!') 3163 except flags.IllegalFlagValueError as e: 3164 expected = ('flag --%(name)s=%(value)s: %(value)s is not %(suffix)s' % { 3165 'name': flag_name, 3166 'value': flag_value, 3167 'suffix': expected_message_suffix 3168 }) 3169 self.assertEqual(str(e), expected) 3170 3171 3172if __name__ == '__main__': 3173 absltest.main() 3174