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"""Defines the FlagValues class - registry of 'Flag' objects. 15 16Do NOT import this module directly. Import the flags package and use the 17aliases defined at the package level instead. 18""" 19 20import copy 21import itertools 22import logging 23import os 24import sys 25from typing import Generic, TypeVar 26from xml.dom import minidom 27 28from absl.flags import _exceptions 29from absl.flags import _flag 30from absl.flags import _helpers 31from absl.flags import _validators_classes 32 33# Add flagvalues module to disclaimed module ids. 34_helpers.disclaim_module_ids.add(id(sys.modules[__name__])) 35 36_T = TypeVar('_T') 37 38 39class FlagValues: 40 """Registry of :class:`~absl.flags.Flag` objects. 41 42 A :class:`FlagValues` can then scan command line arguments, passing flag 43 arguments through to the 'Flag' objects that it owns. It also 44 provides easy access to the flag values. Typically only one 45 :class:`FlagValues` object is needed by an application: 46 :const:`FLAGS`. 47 48 This class is heavily overloaded: 49 50 :class:`Flag` objects are registered via ``__setitem__``:: 51 52 FLAGS['longname'] = x # register a new flag 53 54 The ``.value`` attribute of the registered :class:`~absl.flags.Flag` objects 55 can be accessed as attributes of this :class:`FlagValues` object, through 56 ``__getattr__``. Both the long and short name of the original 57 :class:`~absl.flags.Flag` objects can be used to access its value:: 58 59 FLAGS.longname # parsed flag value 60 FLAGS.x # parsed flag value (short name) 61 62 Command line arguments are scanned and passed to the registered 63 :class:`~absl.flags.Flag` objects through the ``__call__`` method. Unparsed 64 arguments, including ``argv[0]`` (e.g. the program name) are returned:: 65 66 argv = FLAGS(sys.argv) # scan command line arguments 67 68 The original registered :class:`~absl.flags.Flag` objects can be retrieved 69 through the use of the dictionary-like operator, ``__getitem__``:: 70 71 x = FLAGS['longname'] # access the registered Flag object 72 73 The ``str()`` operator of a :class:`absl.flags.FlagValues` object provides 74 help for all of the registered :class:`~absl.flags.Flag` objects. 75 """ 76 77 # A note on collections.abc.Mapping: 78 # FlagValues defines __getitem__, __iter__, and __len__. It makes perfect 79 # sense to let it be a collections.abc.Mapping class. However, we are not 80 # able to do so. The mixin methods, e.g. keys, values, are not uncommon flag 81 # names. Those flag values would not be accessible via the FLAGS.xxx form. 82 83 def __init__(self): 84 # Since everything in this class is so heavily overloaded, the only 85 # way of defining and using fields is to access __dict__ directly. 86 87 # Dictionary: flag name (string) -> Flag object. 88 self.__dict__['__flags'] = {} 89 90 # Set: name of hidden flag (string). 91 # Holds flags that should not be directly accessible from Python. 92 self.__dict__['__hiddenflags'] = set() 93 94 # Dictionary: module name (string) -> list of Flag objects that are defined 95 # by that module. 96 self.__dict__['__flags_by_module'] = {} 97 # Dictionary: module id (int) -> list of Flag objects that are defined by 98 # that module. 99 self.__dict__['__flags_by_module_id'] = {} 100 # Dictionary: module name (string) -> list of Flag objects that are 101 # key for that module. 102 self.__dict__['__key_flags_by_module'] = {} 103 104 # Bool: True if flags were parsed. 105 self.__dict__['__flags_parsed'] = False 106 107 # Bool: True if unparse_flags() was called. 108 self.__dict__['__unparse_flags_called'] = False 109 110 # None or Method(name, value) to call from __setattr__ for an unknown flag. 111 self.__dict__['__set_unknown'] = None 112 113 # A set of banned flag names. This is to prevent users from accidentally 114 # defining a flag that has the same name as a method on this class. 115 # Users can still allow defining the flag by passing 116 # allow_using_method_names=True in DEFINE_xxx functions. 117 self.__dict__['__banned_flag_names'] = frozenset(dir(FlagValues)) 118 119 # Bool: Whether to use GNU style scanning. 120 self.__dict__['__use_gnu_getopt'] = True 121 122 # Bool: Whether use_gnu_getopt has been explicitly set by the user. 123 self.__dict__['__use_gnu_getopt_explicitly_set'] = False 124 125 # Function: Takes a flag name as parameter, returns a tuple 126 # (is_retired, type_is_bool). 127 self.__dict__['__is_retired_flag_func'] = None 128 129 def set_gnu_getopt(self, gnu_getopt=True): 130 """Sets whether or not to use GNU style scanning. 131 132 GNU style allows mixing of flag and non-flag arguments. See 133 http://docs.python.org/library/getopt.html#getopt.gnu_getopt 134 135 Args: 136 gnu_getopt: bool, whether or not to use GNU style scanning. 137 """ 138 self.__dict__['__use_gnu_getopt'] = gnu_getopt 139 self.__dict__['__use_gnu_getopt_explicitly_set'] = True 140 141 def is_gnu_getopt(self): 142 return self.__dict__['__use_gnu_getopt'] 143 144 def _flags(self): 145 return self.__dict__['__flags'] 146 147 def flags_by_module_dict(self): 148 """Returns the dictionary of module_name -> list of defined flags. 149 150 Returns: 151 A dictionary. Its keys are module names (strings). Its values 152 are lists of Flag objects. 153 """ 154 return self.__dict__['__flags_by_module'] 155 156 def flags_by_module_id_dict(self): 157 """Returns the dictionary of module_id -> list of defined flags. 158 159 Returns: 160 A dictionary. Its keys are module IDs (ints). Its values 161 are lists of Flag objects. 162 """ 163 return self.__dict__['__flags_by_module_id'] 164 165 def key_flags_by_module_dict(self): 166 """Returns the dictionary of module_name -> list of key flags. 167 168 Returns: 169 A dictionary. Its keys are module names (strings). Its values 170 are lists of Flag objects. 171 """ 172 return self.__dict__['__key_flags_by_module'] 173 174 def register_flag_by_module(self, module_name, flag): 175 """Records the module that defines a specific flag. 176 177 We keep track of which flag is defined by which module so that we 178 can later sort the flags by module. 179 180 Args: 181 module_name: str, the name of a Python module. 182 flag: Flag, the Flag instance that is key to the module. 183 """ 184 flags_by_module = self.flags_by_module_dict() 185 flags_by_module.setdefault(module_name, []).append(flag) 186 187 def register_flag_by_module_id(self, module_id, flag): 188 """Records the module that defines a specific flag. 189 190 Args: 191 module_id: int, the ID of the Python module. 192 flag: Flag, the Flag instance that is key to the module. 193 """ 194 flags_by_module_id = self.flags_by_module_id_dict() 195 flags_by_module_id.setdefault(module_id, []).append(flag) 196 197 def register_key_flag_for_module(self, module_name, flag): 198 """Specifies that a flag is a key flag for a module. 199 200 Args: 201 module_name: str, the name of a Python module. 202 flag: Flag, the Flag instance that is key to the module. 203 """ 204 key_flags_by_module = self.key_flags_by_module_dict() 205 # The list of key flags for the module named module_name. 206 key_flags = key_flags_by_module.setdefault(module_name, []) 207 # Add flag, but avoid duplicates. 208 if flag not in key_flags: 209 key_flags.append(flag) 210 211 def _flag_is_registered(self, flag_obj): 212 """Checks whether a Flag object is registered under long name or short name. 213 214 Args: 215 flag_obj: Flag, the Flag instance to check for. 216 217 Returns: 218 bool, True iff flag_obj is registered under long name or short name. 219 """ 220 flag_dict = self._flags() 221 # Check whether flag_obj is registered under its long name. 222 name = flag_obj.name 223 if flag_dict.get(name, None) == flag_obj: 224 return True 225 # Check whether flag_obj is registered under its short name. 226 short_name = flag_obj.short_name 227 if (short_name is not None and flag_dict.get(short_name, None) == flag_obj): 228 return True 229 return False 230 231 def _cleanup_unregistered_flag_from_module_dicts(self, flag_obj): 232 """Cleans up unregistered flags from all module -> [flags] dictionaries. 233 234 If flag_obj is registered under either its long name or short name, it 235 won't be removed from the dictionaries. 236 237 Args: 238 flag_obj: Flag, the Flag instance to clean up for. 239 """ 240 if self._flag_is_registered(flag_obj): 241 return 242 for flags_by_module_dict in (self.flags_by_module_dict(), 243 self.flags_by_module_id_dict(), 244 self.key_flags_by_module_dict()): 245 for flags_in_module in flags_by_module_dict.values(): 246 # While (as opposed to if) takes care of multiple occurrences of a 247 # flag in the list for the same module. 248 while flag_obj in flags_in_module: 249 flags_in_module.remove(flag_obj) 250 251 def get_flags_for_module(self, module): 252 """Returns the list of flags defined by a module. 253 254 Args: 255 module: module|str, the module to get flags from. 256 257 Returns: 258 [Flag], a new list of Flag instances. Caller may update this list as 259 desired: none of those changes will affect the internals of this 260 FlagValue instance. 261 """ 262 if not isinstance(module, str): 263 module = module.__name__ 264 if module == '__main__': 265 module = sys.argv[0] 266 267 return list(self.flags_by_module_dict().get(module, [])) 268 269 def get_key_flags_for_module(self, module): 270 """Returns the list of key flags for a module. 271 272 Args: 273 module: module|str, the module to get key flags from. 274 275 Returns: 276 [Flag], a new list of Flag instances. Caller may update this list as 277 desired: none of those changes will affect the internals of this 278 FlagValue instance. 279 """ 280 if not isinstance(module, str): 281 module = module.__name__ 282 if module == '__main__': 283 module = sys.argv[0] 284 285 # Any flag is a key flag for the module that defined it. NOTE: 286 # key_flags is a fresh list: we can update it without affecting the 287 # internals of this FlagValues object. 288 key_flags = self.get_flags_for_module(module) 289 290 # Take into account flags explicitly declared as key for a module. 291 for flag in self.key_flags_by_module_dict().get(module, []): 292 if flag not in key_flags: 293 key_flags.append(flag) 294 return key_flags 295 296 def find_module_defining_flag(self, flagname, default=None): 297 """Return the name of the module defining this flag, or default. 298 299 Args: 300 flagname: str, name of the flag to lookup. 301 default: Value to return if flagname is not defined. Defaults to None. 302 303 Returns: 304 The name of the module which registered the flag with this name. 305 If no such module exists (i.e. no flag with this name exists), 306 we return default. 307 """ 308 registered_flag = self._flags().get(flagname) 309 if registered_flag is None: 310 return default 311 for module, flags in self.flags_by_module_dict().items(): 312 for flag in flags: 313 # It must compare the flag with the one in _flags. This is because a 314 # flag might be overridden only for its long name (or short name), 315 # and only its short name (or long name) is considered registered. 316 if (flag.name == registered_flag.name and 317 flag.short_name == registered_flag.short_name): 318 return module 319 return default 320 321 def find_module_id_defining_flag(self, flagname, default=None): 322 """Return the ID of the module defining this flag, or default. 323 324 Args: 325 flagname: str, name of the flag to lookup. 326 default: Value to return if flagname is not defined. Defaults to None. 327 328 Returns: 329 The ID of the module which registered the flag with this name. 330 If no such module exists (i.e. no flag with this name exists), 331 we return default. 332 """ 333 registered_flag = self._flags().get(flagname) 334 if registered_flag is None: 335 return default 336 for module_id, flags in self.flags_by_module_id_dict().items(): 337 for flag in flags: 338 # It must compare the flag with the one in _flags. This is because a 339 # flag might be overridden only for its long name (or short name), 340 # and only its short name (or long name) is considered registered. 341 if (flag.name == registered_flag.name and 342 flag.short_name == registered_flag.short_name): 343 return module_id 344 return default 345 346 def _register_unknown_flag_setter(self, setter): 347 """Allow set default values for undefined flags. 348 349 Args: 350 setter: Method(name, value) to call to __setattr__ an unknown flag. Must 351 raise NameError or ValueError for invalid name/value. 352 """ 353 self.__dict__['__set_unknown'] = setter 354 355 def _set_unknown_flag(self, name, value): 356 """Returns value if setting flag |name| to |value| returned True. 357 358 Args: 359 name: str, name of the flag to set. 360 value: Value to set. 361 362 Returns: 363 Flag value on successful call. 364 365 Raises: 366 UnrecognizedFlagError 367 IllegalFlagValueError 368 """ 369 setter = self.__dict__['__set_unknown'] 370 if setter: 371 try: 372 setter(name, value) 373 return value 374 except (TypeError, ValueError): # Flag value is not valid. 375 raise _exceptions.IllegalFlagValueError( 376 '"{1}" is not valid for --{0}'.format(name, value)) 377 except NameError: # Flag name is not valid. 378 pass 379 raise _exceptions.UnrecognizedFlagError(name, value) 380 381 def append_flag_values(self, flag_values): 382 """Appends flags registered in another FlagValues instance. 383 384 Args: 385 flag_values: FlagValues, the FlagValues instance from which to copy flags. 386 """ 387 for flag_name, flag in flag_values._flags().items(): # pylint: disable=protected-access 388 # Each flags with short_name appears here twice (once under its 389 # normal name, and again with its short name). To prevent 390 # problems (DuplicateFlagError) with double flag registration, we 391 # perform a check to make sure that the entry we're looking at is 392 # for its normal name. 393 if flag_name == flag.name: 394 try: 395 self[flag_name] = flag 396 except _exceptions.DuplicateFlagError: 397 raise _exceptions.DuplicateFlagError.from_flag( 398 flag_name, self, other_flag_values=flag_values) 399 400 def remove_flag_values(self, flag_values): 401 """Remove flags that were previously appended from another FlagValues. 402 403 Args: 404 flag_values: FlagValues, the FlagValues instance containing flags to 405 remove. 406 """ 407 for flag_name in flag_values: 408 self.__delattr__(flag_name) 409 410 def __setitem__(self, name, flag): 411 """Registers a new flag variable.""" 412 fl = self._flags() 413 if not isinstance(flag, _flag.Flag): 414 raise _exceptions.IllegalFlagValueError( 415 f'Expect Flag instances, found type {type(flag)}. ' 416 "Maybe you didn't mean to use FlagValue.__setitem__?") 417 if not isinstance(name, str): 418 raise _exceptions.Error('Flag name must be a string') 419 if not name: 420 raise _exceptions.Error('Flag name cannot be empty') 421 if ' ' in name: 422 raise _exceptions.Error('Flag name cannot contain a space') 423 self._check_method_name_conflicts(name, flag) 424 if name in fl and not flag.allow_override and not fl[name].allow_override: 425 module, module_name = _helpers.get_calling_module_object_and_name() 426 if (self.find_module_defining_flag(name) == module_name and 427 id(module) != self.find_module_id_defining_flag(name)): 428 # If the flag has already been defined by a module with the same name, 429 # but a different ID, we can stop here because it indicates that the 430 # module is simply being imported a subsequent time. 431 return 432 raise _exceptions.DuplicateFlagError.from_flag(name, self) 433 short_name = flag.short_name 434 # If a new flag overrides an old one, we need to cleanup the old flag's 435 # modules if it's not registered. 436 flags_to_cleanup = set() 437 if short_name is not None: 438 if (short_name in fl and not flag.allow_override and 439 not fl[short_name].allow_override): 440 raise _exceptions.DuplicateFlagError.from_flag(short_name, self) 441 if short_name in fl and fl[short_name] != flag: 442 flags_to_cleanup.add(fl[short_name]) 443 fl[short_name] = flag 444 if (name not in fl # new flag 445 or fl[name].using_default_value or not flag.using_default_value): 446 if name in fl and fl[name] != flag: 447 flags_to_cleanup.add(fl[name]) 448 fl[name] = flag 449 for f in flags_to_cleanup: 450 self._cleanup_unregistered_flag_from_module_dicts(f) 451 452 def __dir__(self): 453 """Returns list of names of all defined flags. 454 455 Useful for TAB-completion in ipython. 456 457 Returns: 458 [str], a list of names of all defined flags. 459 """ 460 return sorted(self.__dict__['__flags']) 461 462 def __getitem__(self, name): 463 """Returns the Flag object for the flag --name.""" 464 return self._flags()[name] 465 466 def _hide_flag(self, name): 467 """Marks the flag --name as hidden.""" 468 self.__dict__['__hiddenflags'].add(name) 469 470 def __getattr__(self, name): 471 """Retrieves the 'value' attribute of the flag --name.""" 472 fl = self._flags() 473 if name not in fl: 474 raise AttributeError(name) 475 if name in self.__dict__['__hiddenflags']: 476 raise AttributeError(name) 477 478 if self.__dict__['__flags_parsed'] or fl[name].present: 479 return fl[name].value 480 else: 481 raise _exceptions.UnparsedFlagAccessError( 482 'Trying to access flag --%s before flags were parsed.' % name) 483 484 def __setattr__(self, name, value): 485 """Sets the 'value' attribute of the flag --name.""" 486 self._set_attributes(**{name: value}) 487 return value 488 489 def _set_attributes(self, **attributes): 490 """Sets multiple flag values together, triggers validators afterwards.""" 491 fl = self._flags() 492 known_flags = set() 493 for name, value in attributes.items(): 494 if name in self.__dict__['__hiddenflags']: 495 raise AttributeError(name) 496 if name in fl: 497 fl[name].value = value 498 known_flags.add(name) 499 else: 500 self._set_unknown_flag(name, value) 501 for name in known_flags: 502 self._assert_validators(fl[name].validators) 503 fl[name].using_default_value = False 504 505 def validate_all_flags(self): 506 """Verifies whether all flags pass validation. 507 508 Raises: 509 AttributeError: Raised if validators work with a non-existing flag. 510 IllegalFlagValueError: Raised if validation fails for at least one 511 validator. 512 """ 513 all_validators = set() 514 for flag in self._flags().values(): 515 all_validators.update(flag.validators) 516 self._assert_validators(all_validators) 517 518 def _assert_validators(self, validators): 519 """Asserts if all validators in the list are satisfied. 520 521 It asserts validators in the order they were created. 522 523 Args: 524 validators: Iterable(validators.Validator), validators to be verified. 525 526 Raises: 527 AttributeError: Raised if validators work with a non-existing flag. 528 IllegalFlagValueError: Raised if validation fails for at least one 529 validator. 530 """ 531 messages = [] 532 bad_flags = set() 533 for validator in sorted( 534 validators, key=lambda validator: validator.insertion_index): 535 try: 536 if isinstance(validator, _validators_classes.SingleFlagValidator): 537 if validator.flag_name in bad_flags: 538 continue 539 elif isinstance(validator, _validators_classes.MultiFlagsValidator): 540 if bad_flags & set(validator.flag_names): 541 continue 542 validator.verify(self) 543 except _exceptions.ValidationError as e: 544 if isinstance(validator, _validators_classes.SingleFlagValidator): 545 bad_flags.add(validator.flag_name) 546 elif isinstance(validator, _validators_classes.MultiFlagsValidator): 547 bad_flags.update(set(validator.flag_names)) 548 message = validator.print_flags_with_values(self) 549 messages.append('%s: %s' % (message, str(e))) 550 if messages: 551 raise _exceptions.IllegalFlagValueError('\n'.join(messages)) 552 553 def __delattr__(self, flag_name): 554 """Deletes a previously-defined flag from a flag object. 555 556 This method makes sure we can delete a flag by using 557 558 del FLAGS.<flag_name> 559 560 E.g., 561 562 flags.DEFINE_integer('foo', 1, 'Integer flag.') 563 del flags.FLAGS.foo 564 565 If a flag is also registered by its the other name (long name or short 566 name), the other name won't be deleted. 567 568 Args: 569 flag_name: str, the name of the flag to be deleted. 570 571 Raises: 572 AttributeError: Raised when there is no registered flag named flag_name. 573 """ 574 fl = self._flags() 575 if flag_name not in fl: 576 raise AttributeError(flag_name) 577 578 flag_obj = fl[flag_name] 579 del fl[flag_name] 580 581 self._cleanup_unregistered_flag_from_module_dicts(flag_obj) 582 583 def set_default(self, name, value): 584 """Changes the default value of the named flag object. 585 586 The flag's current value is also updated if the flag is currently using 587 the default value, i.e. not specified in the command line, and not set 588 by FLAGS.name = value. 589 590 Args: 591 name: str, the name of the flag to modify. 592 value: The new default value. 593 594 Raises: 595 UnrecognizedFlagError: Raised when there is no registered flag named name. 596 IllegalFlagValueError: Raised when value is not valid. 597 """ 598 fl = self._flags() 599 if name not in fl: 600 self._set_unknown_flag(name, value) 601 return 602 fl[name]._set_default(value) # pylint: disable=protected-access 603 self._assert_validators(fl[name].validators) 604 605 def __contains__(self, name): 606 """Returns True if name is a value (flag) in the dict.""" 607 return name in self._flags() 608 609 def __len__(self): 610 return len(self.__dict__['__flags']) 611 612 def __iter__(self): 613 return iter(self._flags()) 614 615 def __call__(self, argv, known_only=False): 616 """Parses flags from argv; stores parsed flags into this FlagValues object. 617 618 All unparsed arguments are returned. 619 620 Args: 621 argv: a tuple/list of strings. 622 known_only: bool, if True, parse and remove known flags; return the rest 623 untouched. Unknown flags specified by --undefok are not returned. 624 625 Returns: 626 The list of arguments not parsed as options, including argv[0]. 627 628 Raises: 629 Error: Raised on any parsing error. 630 TypeError: Raised on passing wrong type of arguments. 631 ValueError: Raised on flag value parsing error. 632 """ 633 if isinstance(argv, (str, bytes)): 634 raise TypeError( 635 'argv should be a tuple/list of strings, not bytes or string.') 636 if not argv: 637 raise ValueError( 638 'argv cannot be an empty list, and must contain the program name as ' 639 'the first element.') 640 641 # This pre parses the argv list for --flagfile=<> options. 642 program_name = argv[0] 643 args = self.read_flags_from_files(argv[1:], force_gnu=False) 644 645 # Parse the arguments. 646 unknown_flags, unparsed_args = self._parse_args(args, known_only) 647 648 # Handle unknown flags by raising UnrecognizedFlagError. 649 # Note some users depend on us raising this particular error. 650 for name, value in unknown_flags: 651 suggestions = _helpers.get_flag_suggestions(name, list(self)) 652 raise _exceptions.UnrecognizedFlagError( 653 name, value, suggestions=suggestions) 654 655 self.mark_as_parsed() 656 self.validate_all_flags() 657 return [program_name] + unparsed_args 658 659 def __getstate__(self): 660 raise TypeError("can't pickle FlagValues") 661 662 def __copy__(self): 663 raise TypeError('FlagValues does not support shallow copies. ' 664 'Use absl.testing.flagsaver or copy.deepcopy instead.') 665 666 def __deepcopy__(self, memo): 667 result = object.__new__(type(self)) 668 result.__dict__.update(copy.deepcopy(self.__dict__, memo)) 669 return result 670 671 def _set_is_retired_flag_func(self, is_retired_flag_func): 672 """Sets a function for checking retired flags. 673 674 Do not use it. This is a private absl API used to check retired flags 675 registered by the absl C++ flags library. 676 677 Args: 678 is_retired_flag_func: Callable(str) -> (bool, bool), a function takes flag 679 name as parameter, returns a tuple (is_retired, type_is_bool). 680 """ 681 self.__dict__['__is_retired_flag_func'] = is_retired_flag_func 682 683 def _parse_args(self, args, known_only): 684 """Helper function to do the main argument parsing. 685 686 This function goes through args and does the bulk of the flag parsing. 687 It will find the corresponding flag in our flag dictionary, and call its 688 .parse() method on the flag value. 689 690 Args: 691 args: [str], a list of strings with the arguments to parse. 692 known_only: bool, if True, parse and remove known flags; return the rest 693 untouched. Unknown flags specified by --undefok are not returned. 694 695 Returns: 696 A tuple with the following: 697 unknown_flags: List of (flag name, arg) for flags we don't know about. 698 unparsed_args: List of arguments we did not parse. 699 700 Raises: 701 Error: Raised on any parsing error. 702 ValueError: Raised on flag value parsing error. 703 """ 704 unparsed_names_and_args = [] # A list of (flag name or None, arg). 705 undefok = set() 706 retired_flag_func = self.__dict__['__is_retired_flag_func'] 707 708 flag_dict = self._flags() 709 args = iter(args) 710 for arg in args: 711 value = None 712 713 def get_value(): 714 # pylint: disable=cell-var-from-loop 715 try: 716 return next(args) if value is None else value 717 except StopIteration: 718 raise _exceptions.Error('Missing value for flag ' + arg) # pylint: disable=undefined-loop-variable 719 720 if not arg.startswith('-'): 721 # A non-argument: default is break, GNU is skip. 722 unparsed_names_and_args.append((None, arg)) 723 if self.is_gnu_getopt(): 724 continue 725 else: 726 break 727 728 if arg == '--': 729 if known_only: 730 unparsed_names_and_args.append((None, arg)) 731 break 732 733 # At this point, arg must start with '-'. 734 if arg.startswith('--'): 735 arg_without_dashes = arg[2:] 736 else: 737 arg_without_dashes = arg[1:] 738 739 if '=' in arg_without_dashes: 740 name, value = arg_without_dashes.split('=', 1) 741 else: 742 name, value = arg_without_dashes, None 743 744 if not name: 745 # The argument is all dashes (including one dash). 746 unparsed_names_and_args.append((None, arg)) 747 if self.is_gnu_getopt(): 748 continue 749 else: 750 break 751 752 # --undefok is a special case. 753 if name == 'undefok': 754 value = get_value() 755 undefok.update(v.strip() for v in value.split(',')) 756 undefok.update('no' + v.strip() for v in value.split(',')) 757 continue 758 759 flag = flag_dict.get(name) 760 if flag is not None: 761 if flag.boolean and value is None: 762 value = 'true' 763 else: 764 value = get_value() 765 elif name.startswith('no') and len(name) > 2: 766 # Boolean flags can take the form of --noflag, with no value. 767 noflag = flag_dict.get(name[2:]) 768 if noflag is not None and noflag.boolean: 769 if value is not None: 770 raise ValueError(arg + ' does not take an argument') 771 flag = noflag 772 value = 'false' 773 774 if retired_flag_func and flag is None: 775 is_retired, is_bool = retired_flag_func(name) 776 777 # If we didn't recognize that flag, but it starts with 778 # "no" then maybe it was a boolean flag specified in the 779 # --nofoo form. 780 if not is_retired and name.startswith('no'): 781 is_retired, is_bool = retired_flag_func(name[2:]) 782 is_retired = is_retired and is_bool 783 784 if is_retired: 785 if not is_bool and value is None: 786 # This happens when a non-bool retired flag is specified 787 # in format of "--flag value". 788 get_value() 789 logging.error( 790 'Flag "%s" is retired and should no longer ' 791 'be specified. See go/totw/90.', name) 792 continue 793 794 if flag is not None: 795 # LINT.IfChange 796 flag.parse(value) 797 flag.using_default_value = False 798 # LINT.ThenChange(../testing/flagsaver.py:flag_override_parsing) 799 else: 800 unparsed_names_and_args.append((name, arg)) 801 802 unknown_flags = [] 803 unparsed_args = [] 804 for name, arg in unparsed_names_and_args: 805 if name is None: 806 # Positional arguments. 807 unparsed_args.append(arg) 808 elif name in undefok: 809 # Remove undefok flags. 810 continue 811 else: 812 # This is an unknown flag. 813 if known_only: 814 unparsed_args.append(arg) 815 else: 816 unknown_flags.append((name, arg)) 817 818 unparsed_args.extend(list(args)) 819 return unknown_flags, unparsed_args 820 821 def is_parsed(self): 822 """Returns whether flags were parsed.""" 823 return self.__dict__['__flags_parsed'] 824 825 def mark_as_parsed(self): 826 """Explicitly marks flags as parsed. 827 828 Use this when the caller knows that this FlagValues has been parsed as if 829 a ``__call__()`` invocation has happened. This is only a public method for 830 use by things like appcommands which do additional command like parsing. 831 """ 832 self.__dict__['__flags_parsed'] = True 833 834 def unparse_flags(self): 835 """Unparses all flags to the point before any FLAGS(argv) was called.""" 836 for f in self._flags().values(): 837 f.unparse() 838 # We log this message before marking flags as unparsed to avoid a 839 # problem when the logging library causes flags access. 840 logging.info('unparse_flags() called; flags access will now raise errors.') 841 self.__dict__['__flags_parsed'] = False 842 self.__dict__['__unparse_flags_called'] = True 843 844 def flag_values_dict(self): 845 """Returns a dictionary that maps flag names to flag values.""" 846 return {name: flag.value for name, flag in self._flags().items()} 847 848 def __str__(self): 849 """Returns a help string for all known flags.""" 850 return self.get_help() 851 852 def get_help(self, prefix='', include_special_flags=True): 853 """Returns a help string for all known flags. 854 855 Args: 856 prefix: str, per-line output prefix. 857 include_special_flags: bool, whether to include description of 858 SPECIAL_FLAGS, i.e. --flagfile and --undefok. 859 860 Returns: 861 str, formatted help message. 862 """ 863 flags_by_module = self.flags_by_module_dict() 864 if flags_by_module: 865 modules = sorted(flags_by_module) 866 # Print the help for the main module first, if possible. 867 main_module = sys.argv[0] 868 if main_module in modules: 869 modules.remove(main_module) 870 modules = [main_module] + modules 871 return self._get_help_for_modules(modules, prefix, include_special_flags) 872 else: 873 output_lines = [] 874 # Just print one long list of flags. 875 values = self._flags().values() 876 if include_special_flags: 877 values = itertools.chain( 878 values, _helpers.SPECIAL_FLAGS._flags().values()) # pylint: disable=protected-access 879 self._render_flag_list(values, output_lines, prefix) 880 return '\n'.join(output_lines) 881 882 def _get_help_for_modules(self, modules, prefix, include_special_flags): 883 """Returns the help string for a list of modules. 884 885 Private to absl.flags package. 886 887 Args: 888 modules: List[str], a list of modules to get the help string for. 889 prefix: str, a string that is prepended to each generated help line. 890 include_special_flags: bool, whether to include description of 891 SPECIAL_FLAGS, i.e. --flagfile and --undefok. 892 """ 893 output_lines = [] 894 for module in modules: 895 self._render_our_module_flags(module, output_lines, prefix) 896 if include_special_flags: 897 self._render_module_flags( 898 'absl.flags', 899 _helpers.SPECIAL_FLAGS._flags().values(), # pylint: disable=protected-access 900 output_lines, 901 prefix) 902 return '\n'.join(output_lines) 903 904 def _render_module_flags(self, module, flags, output_lines, prefix=''): 905 """Returns a help string for a given module.""" 906 if not isinstance(module, str): 907 module = module.__name__ 908 output_lines.append('\n%s%s:' % (prefix, module)) 909 self._render_flag_list(flags, output_lines, prefix + ' ') 910 911 def _render_our_module_flags(self, module, output_lines, prefix=''): 912 """Returns a help string for a given module.""" 913 flags = self.get_flags_for_module(module) 914 if flags: 915 self._render_module_flags(module, flags, output_lines, prefix) 916 917 def _render_our_module_key_flags(self, module, output_lines, prefix=''): 918 """Returns a help string for the key flags of a given module. 919 920 Args: 921 module: module|str, the module to render key flags for. 922 output_lines: [str], a list of strings. The generated help message lines 923 will be appended to this list. 924 prefix: str, a string that is prepended to each generated help line. 925 """ 926 key_flags = self.get_key_flags_for_module(module) 927 if key_flags: 928 self._render_module_flags(module, key_flags, output_lines, prefix) 929 930 def module_help(self, module): 931 """Describes the key flags of a module. 932 933 Args: 934 module: module|str, the module to describe the key flags for. 935 936 Returns: 937 str, describing the key flags of a module. 938 """ 939 helplist = [] 940 self._render_our_module_key_flags(module, helplist) 941 return '\n'.join(helplist) 942 943 def main_module_help(self): 944 """Describes the key flags of the main module. 945 946 Returns: 947 str, describing the key flags of the main module. 948 """ 949 return self.module_help(sys.argv[0]) 950 951 def _render_flag_list(self, flaglist, output_lines, prefix=' '): 952 fl = self._flags() 953 special_fl = _helpers.SPECIAL_FLAGS._flags() # pylint: disable=protected-access 954 flaglist = [(flag.name, flag) for flag in flaglist] 955 flaglist.sort() 956 flagset = {} 957 for (name, flag) in flaglist: 958 # It's possible this flag got deleted or overridden since being 959 # registered in the per-module flaglist. Check now against the 960 # canonical source of current flag information, the _flags. 961 if fl.get(name, None) != flag and special_fl.get(name, None) != flag: 962 # a different flag is using this name now 963 continue 964 # only print help once 965 if flag in flagset: 966 continue 967 flagset[flag] = 1 968 flaghelp = '' 969 if flag.short_name: 970 flaghelp += '-%s,' % flag.short_name 971 if flag.boolean: 972 flaghelp += '--[no]%s:' % flag.name 973 else: 974 flaghelp += '--%s:' % flag.name 975 flaghelp += ' ' 976 if flag.help: 977 flaghelp += flag.help 978 flaghelp = _helpers.text_wrap( 979 flaghelp, indent=prefix + ' ', firstline_indent=prefix) 980 if flag.default_as_str: 981 flaghelp += '\n' 982 flaghelp += _helpers.text_wrap( 983 '(default: %s)' % flag.default_as_str, indent=prefix + ' ') 984 if flag.parser.syntactic_help: 985 flaghelp += '\n' 986 flaghelp += _helpers.text_wrap( 987 '(%s)' % flag.parser.syntactic_help, indent=prefix + ' ') 988 output_lines.append(flaghelp) 989 990 def get_flag_value(self, name, default): # pylint: disable=invalid-name 991 """Returns the value of a flag (if not None) or a default value. 992 993 Args: 994 name: str, the name of a flag. 995 default: Default value to use if the flag value is None. 996 997 Returns: 998 Requested flag value or default. 999 """ 1000 1001 value = self.__getattr__(name) 1002 if value is not None: # Can't do if not value, b/c value might be '0' or "" 1003 return value 1004 else: 1005 return default 1006 1007 def _is_flag_file_directive(self, flag_string): 1008 """Checks whether flag_string contain a --flagfile=<foo> directive.""" 1009 if isinstance(flag_string, str): 1010 if flag_string.startswith('--flagfile='): 1011 return 1 1012 elif flag_string == '--flagfile': 1013 return 1 1014 elif flag_string.startswith('-flagfile='): 1015 return 1 1016 elif flag_string == '-flagfile': 1017 return 1 1018 else: 1019 return 0 1020 return 0 1021 1022 def _extract_filename(self, flagfile_str): 1023 """Returns filename from a flagfile_str of form -[-]flagfile=filename. 1024 1025 The cases of --flagfile foo and -flagfile foo shouldn't be hitting 1026 this function, as they are dealt with in the level above this 1027 function. 1028 1029 Args: 1030 flagfile_str: str, the flagfile string. 1031 1032 Returns: 1033 str, the filename from a flagfile_str of form -[-]flagfile=filename. 1034 1035 Raises: 1036 Error: Raised when illegal --flagfile is provided. 1037 """ 1038 if flagfile_str.startswith('--flagfile='): 1039 return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip()) 1040 elif flagfile_str.startswith('-flagfile='): 1041 return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip()) 1042 else: 1043 raise _exceptions.Error('Hit illegal --flagfile type: %s' % flagfile_str) 1044 1045 def _get_flag_file_lines(self, filename, parsed_file_stack=None): 1046 """Returns the useful (!=comments, etc) lines from a file with flags. 1047 1048 Args: 1049 filename: str, the name of the flag file. 1050 parsed_file_stack: [str], a list of the names of the files that we have 1051 recursively encountered at the current depth. MUTATED BY THIS FUNCTION 1052 (but the original value is preserved upon successfully returning from 1053 function call). 1054 1055 Returns: 1056 List of strings. See the note below. 1057 1058 NOTE(springer): This function checks for a nested --flagfile=<foo> 1059 tag and handles the lower file recursively. It returns a list of 1060 all the lines that _could_ contain command flags. This is 1061 EVERYTHING except whitespace lines and comments (lines starting 1062 with '#' or '//'). 1063 """ 1064 # For consistency with the cpp version, ignore empty values. 1065 if not filename: 1066 return [] 1067 if parsed_file_stack is None: 1068 parsed_file_stack = [] 1069 # We do a little safety check for reparsing a file we've already encountered 1070 # at a previous depth. 1071 if filename in parsed_file_stack: 1072 sys.stderr.write('Warning: Hit circular flagfile dependency. Ignoring' 1073 ' flagfile: %s\n' % (filename,)) 1074 return [] 1075 else: 1076 parsed_file_stack.append(filename) 1077 1078 line_list = [] # All line from flagfile. 1079 flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags. 1080 try: 1081 file_obj = open(filename, 'r') 1082 except IOError as e_msg: 1083 raise _exceptions.CantOpenFlagFileError( 1084 'ERROR:: Unable to open flagfile: %s' % e_msg) 1085 1086 with file_obj: 1087 line_list = file_obj.readlines() 1088 1089 # This is where we check each line in the file we just read. 1090 for line in line_list: 1091 if line.isspace(): 1092 pass 1093 # Checks for comment (a line that starts with '#'). 1094 elif line.startswith('#') or line.startswith('//'): 1095 pass 1096 # Checks for a nested "--flagfile=<bar>" flag in the current file. 1097 # If we find one, recursively parse down into that file. 1098 elif self._is_flag_file_directive(line): 1099 sub_filename = self._extract_filename(line) 1100 included_flags = self._get_flag_file_lines( 1101 sub_filename, parsed_file_stack=parsed_file_stack) 1102 flag_line_list.extend(included_flags) 1103 else: 1104 # Any line that's not a comment or a nested flagfile should get 1105 # copied into 2nd position. This leaves earlier arguments 1106 # further back in the list, thus giving them higher priority. 1107 flag_line_list.append(line.strip()) 1108 1109 parsed_file_stack.pop() 1110 return flag_line_list 1111 1112 def read_flags_from_files(self, argv, force_gnu=True): 1113 """Processes command line args, but also allow args to be read from file. 1114 1115 Args: 1116 argv: [str], a list of strings, usually sys.argv[1:], which may contain 1117 one or more flagfile directives of the form --flagfile="./filename". 1118 Note that the name of the program (sys.argv[0]) should be omitted. 1119 force_gnu: bool, if False, --flagfile parsing obeys the 1120 FLAGS.is_gnu_getopt() value. If True, ignore the value and always follow 1121 gnu_getopt semantics. 1122 1123 Returns: 1124 A new list which has the original list combined with what we read 1125 from any flagfile(s). 1126 1127 Raises: 1128 IllegalFlagValueError: Raised when --flagfile is provided with no 1129 argument. 1130 1131 This function is called by FLAGS(argv). 1132 It scans the input list for a flag that looks like: 1133 --flagfile=<somefile>. Then it opens <somefile>, reads all valid key 1134 and value pairs and inserts them into the input list in exactly the 1135 place where the --flagfile arg is found. 1136 1137 Note that your application's flags are still defined the usual way 1138 using absl.flags DEFINE_flag() type functions. 1139 1140 Notes (assuming we're getting a commandline of some sort as our input): 1141 1142 * For duplicate flags, the last one we hit should "win". 1143 * Since flags that appear later win, a flagfile's settings can be "weak" 1144 if the --flagfile comes at the beginning of the argument sequence, 1145 and it can be "strong" if the --flagfile comes at the end. 1146 * A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile. 1147 It will be expanded in exactly the spot where it is found. 1148 * In a flagfile, a line beginning with # or // is a comment. 1149 * Entirely blank lines _should_ be ignored. 1150 """ 1151 rest_of_args = argv 1152 new_argv = [] 1153 while rest_of_args: 1154 current_arg = rest_of_args[0] 1155 rest_of_args = rest_of_args[1:] 1156 if self._is_flag_file_directive(current_arg): 1157 # This handles the case of -(-)flagfile foo. In this case the 1158 # next arg really is part of this one. 1159 if current_arg == '--flagfile' or current_arg == '-flagfile': 1160 if not rest_of_args: 1161 raise _exceptions.IllegalFlagValueError( 1162 '--flagfile with no argument') 1163 flag_filename = os.path.expanduser(rest_of_args[0]) 1164 rest_of_args = rest_of_args[1:] 1165 else: 1166 # This handles the case of (-)-flagfile=foo. 1167 flag_filename = self._extract_filename(current_arg) 1168 new_argv.extend(self._get_flag_file_lines(flag_filename)) 1169 else: 1170 new_argv.append(current_arg) 1171 # Stop parsing after '--', like getopt and gnu_getopt. 1172 if current_arg == '--': 1173 break 1174 # Stop parsing after a non-flag, like getopt. 1175 if not current_arg.startswith('-'): 1176 if not force_gnu and not self.__dict__['__use_gnu_getopt']: 1177 break 1178 else: 1179 if ('=' not in current_arg and rest_of_args and 1180 not rest_of_args[0].startswith('-')): 1181 # If this is an occurrence of a legitimate --x y, skip the value 1182 # so that it won't be mistaken for a standalone arg. 1183 fl = self._flags() 1184 name = current_arg.lstrip('-') 1185 if name in fl and not fl[name].boolean: 1186 current_arg = rest_of_args[0] 1187 rest_of_args = rest_of_args[1:] 1188 new_argv.append(current_arg) 1189 1190 if rest_of_args: 1191 new_argv.extend(rest_of_args) 1192 1193 return new_argv 1194 1195 def flags_into_string(self): 1196 """Returns a string with the flags assignments from this FlagValues object. 1197 1198 This function ignores flags whose value is None. Each flag 1199 assignment is separated by a newline. 1200 1201 NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString 1202 from https://github.com/gflags/gflags. 1203 1204 Returns: 1205 str, the string with the flags assignments from this FlagValues object. 1206 The flags are ordered by (module_name, flag_name). 1207 """ 1208 module_flags = sorted(self.flags_by_module_dict().items()) 1209 s = '' 1210 for unused_module_name, flags in module_flags: 1211 flags = sorted(flags, key=lambda f: f.name) 1212 for flag in flags: 1213 if flag.value is not None: 1214 s += flag.serialize() + '\n' 1215 return s 1216 1217 def append_flags_into_file(self, filename): 1218 """Appends all flags assignments from this FlagInfo object to a file. 1219 1220 Output will be in the format of a flagfile. 1221 1222 NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile 1223 from https://github.com/gflags/gflags. 1224 1225 Args: 1226 filename: str, name of the file. 1227 """ 1228 with open(filename, 'a') as out_file: 1229 out_file.write(self.flags_into_string()) 1230 1231 def write_help_in_xml_format(self, outfile=None): 1232 """Outputs flag documentation in XML format. 1233 1234 NOTE: We use element names that are consistent with those used by 1235 the C++ command-line flag library, from 1236 https://github.com/gflags/gflags. 1237 We also use a few new elements (e.g., <key>), but we do not 1238 interfere / overlap with existing XML elements used by the C++ 1239 library. Please maintain this consistency. 1240 1241 Args: 1242 outfile: File object we write to. Default None means sys.stdout. 1243 """ 1244 doc = minidom.Document() 1245 all_flag = doc.createElement('AllFlags') 1246 doc.appendChild(all_flag) 1247 1248 all_flag.appendChild( 1249 _helpers.create_xml_dom_element(doc, 'program', 1250 os.path.basename(sys.argv[0]))) 1251 1252 usage_doc = sys.modules['__main__'].__doc__ 1253 if not usage_doc: 1254 usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0] 1255 else: 1256 usage_doc = usage_doc.replace('%s', sys.argv[0]) 1257 all_flag.appendChild( 1258 _helpers.create_xml_dom_element(doc, 'usage', usage_doc)) 1259 1260 # Get list of key flags for the main module. 1261 key_flags = self.get_key_flags_for_module(sys.argv[0]) 1262 1263 # Sort flags by declaring module name and next by flag name. 1264 flags_by_module = self.flags_by_module_dict() 1265 all_module_names = list(flags_by_module.keys()) 1266 all_module_names.sort() 1267 for module_name in all_module_names: 1268 flag_list = [(f.name, f) for f in flags_by_module[module_name]] 1269 flag_list.sort() 1270 for unused_flag_name, flag in flag_list: 1271 is_key = flag in key_flags 1272 all_flag.appendChild( 1273 flag._create_xml_dom_element( # pylint: disable=protected-access 1274 doc, 1275 module_name, 1276 is_key=is_key)) 1277 1278 outfile = outfile or sys.stdout 1279 outfile.write( 1280 doc.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')) 1281 outfile.flush() 1282 1283 def _check_method_name_conflicts(self, name, flag): 1284 if flag.allow_using_method_names: 1285 return 1286 short_name = flag.short_name 1287 flag_names = {name} if short_name is None else {name, short_name} 1288 for flag_name in flag_names: 1289 if flag_name in self.__dict__['__banned_flag_names']: 1290 raise _exceptions.FlagNameConflictsWithMethodError( 1291 'Cannot define a flag named "{name}". It conflicts with a method ' 1292 'on class "{class_name}". To allow defining it, use ' 1293 'allow_using_method_names and access the flag value with ' 1294 "FLAGS['{name}'].value. FLAGS.{name} returns the method, " 1295 'not the flag value.'.format( 1296 name=flag_name, class_name=type(self).__name__)) 1297 1298 1299FLAGS = FlagValues() 1300 1301 1302class FlagHolder(Generic[_T]): 1303 """Holds a defined flag. 1304 1305 This facilitates a cleaner api around global state. Instead of:: 1306 1307 flags.DEFINE_integer('foo', ...) 1308 flags.DEFINE_integer('bar', ...) 1309 1310 def method(): 1311 # prints parsed value of 'bar' flag 1312 print(flags.FLAGS.foo) 1313 # runtime error due to typo or possibly bad coding style. 1314 print(flags.FLAGS.baz) 1315 1316 it encourages code like:: 1317 1318 _FOO_FLAG = flags.DEFINE_integer('foo', ...) 1319 _BAR_FLAG = flags.DEFINE_integer('bar', ...) 1320 1321 def method(): 1322 print(_FOO_FLAG.value) 1323 print(_BAR_FLAG.value) 1324 1325 since the name of the flag appears only once in the source code. 1326 """ 1327 1328 def __init__(self, flag_values, flag, ensure_non_none_value=False): 1329 """Constructs a FlagHolder instance providing typesafe access to flag. 1330 1331 Args: 1332 flag_values: The container the flag is registered to. 1333 flag: The flag object for this flag. 1334 ensure_non_none_value: Is the value of the flag allowed to be None. 1335 """ 1336 self._flagvalues = flag_values 1337 # We take the entire flag object, but only keep the name. Why? 1338 # - We want FlagHolder[T] to be generic container 1339 # - flag_values contains all flags, so has no reference to T. 1340 # - typecheckers don't like to see a generic class where none of the ctor 1341 # arguments refer to the generic type. 1342 self._name = flag.name 1343 # We intentionally do NOT check if the default value is None. 1344 # This allows future use of this for "required flags with None default" 1345 self._ensure_non_none_value = ensure_non_none_value 1346 1347 def __eq__(self, other): 1348 raise TypeError( 1349 "unsupported operand type(s) for ==: '{0}' and '{1}' " 1350 "(did you mean to use '{0}.value' instead?)".format( 1351 type(self).__name__, type(other).__name__)) 1352 1353 def __bool__(self): 1354 raise TypeError( 1355 "bool() not supported for instances of type '{0}' " 1356 "(did you mean to use '{0}.value' instead?)".format( 1357 type(self).__name__)) 1358 1359 __nonzero__ = __bool__ 1360 1361 @property 1362 def name(self): 1363 return self._name 1364 1365 @property 1366 def value(self): 1367 """Returns the value of the flag. 1368 1369 If ``_ensure_non_none_value`` is ``True``, then return value is not 1370 ``None``. 1371 1372 Raises: 1373 UnparsedFlagAccessError: if flag parsing has not finished. 1374 IllegalFlagValueError: if value is None unexpectedly. 1375 """ 1376 val = getattr(self._flagvalues, self._name) 1377 if self._ensure_non_none_value and val is None: 1378 raise _exceptions.IllegalFlagValueError( 1379 'Unexpected None value for flag %s' % self._name) 1380 return val 1381 1382 @property 1383 def default(self): 1384 """Returns the default value of the flag.""" 1385 return self._flagvalues[self._name].default 1386 1387 @property 1388 def present(self): 1389 """Returns True if the flag was parsed from command-line flags.""" 1390 return bool(self._flagvalues[self._name].present) 1391 1392 1393def resolve_flag_ref(flag_ref, flag_values): 1394 """Helper to validate and resolve a flag reference argument.""" 1395 if isinstance(flag_ref, FlagHolder): 1396 new_flag_values = flag_ref._flagvalues # pylint: disable=protected-access 1397 if flag_values != FLAGS and flag_values != new_flag_values: 1398 raise ValueError( 1399 'flag_values must not be customized when operating on a FlagHolder') 1400 return flag_ref.name, new_flag_values 1401 return flag_ref, flag_values 1402 1403 1404def resolve_flag_refs(flag_refs, flag_values): 1405 """Helper to validate and resolve flag reference list arguments.""" 1406 fv = None 1407 names = [] 1408 for ref in flag_refs: 1409 if isinstance(ref, FlagHolder): 1410 newfv = ref._flagvalues # pylint: disable=protected-access 1411 name = ref.name 1412 else: 1413 newfv = flag_values 1414 name = ref 1415 if fv and fv != newfv: 1416 raise ValueError( 1417 'multiple FlagValues instances used in invocation. ' 1418 'FlagHolders must be registered to the same FlagValues instance as ' 1419 'do flag names, if provided.') 1420 fv = newfv 1421 names.append(name) 1422 return names, fv 1423