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