• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(flag)
415    if not isinstance(name, str):
416      raise _exceptions.Error('Flag name must be a string')
417    if not name:
418      raise _exceptions.Error('Flag name cannot be empty')
419    if ' ' in name:
420      raise _exceptions.Error('Flag name cannot contain a space')
421    self._check_method_name_conflicts(name, flag)
422    if name in fl and not flag.allow_override and not fl[name].allow_override:
423      module, module_name = _helpers.get_calling_module_object_and_name()
424      if (self.find_module_defining_flag(name) == module_name and
425          id(module) != self.find_module_id_defining_flag(name)):
426        # If the flag has already been defined by a module with the same name,
427        # but a different ID, we can stop here because it indicates that the
428        # module is simply being imported a subsequent time.
429        return
430      raise _exceptions.DuplicateFlagError.from_flag(name, self)
431    short_name = flag.short_name
432    # If a new flag overrides an old one, we need to cleanup the old flag's
433    # modules if it's not registered.
434    flags_to_cleanup = set()
435    if short_name is not None:
436      if (short_name in fl and not flag.allow_override and
437          not fl[short_name].allow_override):
438        raise _exceptions.DuplicateFlagError.from_flag(short_name, self)
439      if short_name in fl and fl[short_name] != flag:
440        flags_to_cleanup.add(fl[short_name])
441      fl[short_name] = flag
442    if (name not in fl  # new flag
443        or fl[name].using_default_value or not flag.using_default_value):
444      if name in fl and fl[name] != flag:
445        flags_to_cleanup.add(fl[name])
446      fl[name] = flag
447    for f in flags_to_cleanup:
448      self._cleanup_unregistered_flag_from_module_dicts(f)
449
450  def __dir__(self):
451    """Returns list of names of all defined flags.
452
453    Useful for TAB-completion in ipython.
454
455    Returns:
456      [str], a list of names of all defined flags.
457    """
458    return sorted(self.__dict__['__flags'])
459
460  def __getitem__(self, name):
461    """Returns the Flag object for the flag --name."""
462    return self._flags()[name]
463
464  def _hide_flag(self, name):
465    """Marks the flag --name as hidden."""
466    self.__dict__['__hiddenflags'].add(name)
467
468  def __getattr__(self, name):
469    """Retrieves the 'value' attribute of the flag --name."""
470    fl = self._flags()
471    if name not in fl:
472      raise AttributeError(name)
473    if name in self.__dict__['__hiddenflags']:
474      raise AttributeError(name)
475
476    if self.__dict__['__flags_parsed'] or fl[name].present:
477      return fl[name].value
478    else:
479      raise _exceptions.UnparsedFlagAccessError(
480          'Trying to access flag --%s before flags were parsed.' % name)
481
482  def __setattr__(self, name, value):
483    """Sets the 'value' attribute of the flag --name."""
484    self._set_attributes(**{name: value})
485    return value
486
487  def _set_attributes(self, **attributes):
488    """Sets multiple flag values together, triggers validators afterwards."""
489    fl = self._flags()
490    known_flags = set()
491    for name, value in attributes.items():
492      if name in self.__dict__['__hiddenflags']:
493        raise AttributeError(name)
494      if name in fl:
495        fl[name].value = value
496        known_flags.add(name)
497      else:
498        self._set_unknown_flag(name, value)
499    for name in known_flags:
500      self._assert_validators(fl[name].validators)
501      fl[name].using_default_value = False
502
503  def validate_all_flags(self):
504    """Verifies whether all flags pass validation.
505
506    Raises:
507      AttributeError: Raised if validators work with a non-existing flag.
508      IllegalFlagValueError: Raised if validation fails for at least one
509          validator.
510    """
511    all_validators = set()
512    for flag in self._flags().values():
513      all_validators.update(flag.validators)
514    self._assert_validators(all_validators)
515
516  def _assert_validators(self, validators):
517    """Asserts if all validators in the list are satisfied.
518
519    It asserts validators in the order they were created.
520
521    Args:
522      validators: Iterable(validators.Validator), validators to be verified.
523
524    Raises:
525      AttributeError: Raised if validators work with a non-existing flag.
526      IllegalFlagValueError: Raised if validation fails for at least one
527          validator.
528    """
529    messages = []
530    bad_flags = set()
531    for validator in sorted(
532        validators, key=lambda validator: validator.insertion_index):
533      try:
534        if isinstance(validator, _validators_classes.SingleFlagValidator):
535          if validator.flag_name in bad_flags:
536            continue
537        elif isinstance(validator, _validators_classes.MultiFlagsValidator):
538          if bad_flags & set(validator.flag_names):
539            continue
540        validator.verify(self)
541      except _exceptions.ValidationError as e:
542        if isinstance(validator, _validators_classes.SingleFlagValidator):
543          bad_flags.add(validator.flag_name)
544        elif isinstance(validator, _validators_classes.MultiFlagsValidator):
545          bad_flags.update(set(validator.flag_names))
546        message = validator.print_flags_with_values(self)
547        messages.append('%s: %s' % (message, str(e)))
548    if messages:
549      raise _exceptions.IllegalFlagValueError('\n'.join(messages))
550
551  def __delattr__(self, flag_name):
552    """Deletes a previously-defined flag from a flag object.
553
554    This method makes sure we can delete a flag by using
555
556      del FLAGS.<flag_name>
557
558    E.g.,
559
560      flags.DEFINE_integer('foo', 1, 'Integer flag.')
561      del flags.FLAGS.foo
562
563    If a flag is also registered by its the other name (long name or short
564    name), the other name won't be deleted.
565
566    Args:
567      flag_name: str, the name of the flag to be deleted.
568
569    Raises:
570      AttributeError: Raised when there is no registered flag named flag_name.
571    """
572    fl = self._flags()
573    if flag_name not in fl:
574      raise AttributeError(flag_name)
575
576    flag_obj = fl[flag_name]
577    del fl[flag_name]
578
579    self._cleanup_unregistered_flag_from_module_dicts(flag_obj)
580
581  def set_default(self, name, value):
582    """Changes the default value of the named flag object.
583
584    The flag's current value is also updated if the flag is currently using
585    the default value, i.e. not specified in the command line, and not set
586    by FLAGS.name = value.
587
588    Args:
589      name: str, the name of the flag to modify.
590      value: The new default value.
591
592    Raises:
593      UnrecognizedFlagError: Raised when there is no registered flag named name.
594      IllegalFlagValueError: Raised when value is not valid.
595    """
596    fl = self._flags()
597    if name not in fl:
598      self._set_unknown_flag(name, value)
599      return
600    fl[name]._set_default(value)  # pylint: disable=protected-access
601    self._assert_validators(fl[name].validators)
602
603  def __contains__(self, name):
604    """Returns True if name is a value (flag) in the dict."""
605    return name in self._flags()
606
607  def __len__(self):
608    return len(self.__dict__['__flags'])
609
610  def __iter__(self):
611    return iter(self._flags())
612
613  def __call__(self, argv, known_only=False):
614    """Parses flags from argv; stores parsed flags into this FlagValues object.
615
616    All unparsed arguments are returned.
617
618    Args:
619       argv: a tuple/list of strings.
620       known_only: bool, if True, parse and remove known flags; return the rest
621         untouched. Unknown flags specified by --undefok are not returned.
622
623    Returns:
624       The list of arguments not parsed as options, including argv[0].
625
626    Raises:
627       Error: Raised on any parsing error.
628       TypeError: Raised on passing wrong type of arguments.
629       ValueError: Raised on flag value parsing error.
630    """
631    if isinstance(argv, (str, bytes)):
632      raise TypeError(
633          'argv should be a tuple/list of strings, not bytes or string.')
634    if not argv:
635      raise ValueError(
636          'argv cannot be an empty list, and must contain the program name as '
637          'the first element.')
638
639    # This pre parses the argv list for --flagfile=<> options.
640    program_name = argv[0]
641    args = self.read_flags_from_files(argv[1:], force_gnu=False)
642
643    # Parse the arguments.
644    unknown_flags, unparsed_args = self._parse_args(args, known_only)
645
646    # Handle unknown flags by raising UnrecognizedFlagError.
647    # Note some users depend on us raising this particular error.
648    for name, value in unknown_flags:
649      suggestions = _helpers.get_flag_suggestions(name, list(self))
650      raise _exceptions.UnrecognizedFlagError(
651          name, value, suggestions=suggestions)
652
653    self.mark_as_parsed()
654    self.validate_all_flags()
655    return [program_name] + unparsed_args
656
657  def __getstate__(self):
658    raise TypeError("can't pickle FlagValues")
659
660  def __copy__(self):
661    raise TypeError('FlagValues does not support shallow copies. '
662                    'Use absl.testing.flagsaver or copy.deepcopy instead.')
663
664  def __deepcopy__(self, memo):
665    result = object.__new__(type(self))
666    result.__dict__.update(copy.deepcopy(self.__dict__, memo))
667    return result
668
669  def _set_is_retired_flag_func(self, is_retired_flag_func):
670    """Sets a function for checking retired flags.
671
672    Do not use it. This is a private absl API used to check retired flags
673    registered by the absl C++ flags library.
674
675    Args:
676      is_retired_flag_func: Callable(str) -> (bool, bool), a function takes flag
677        name as parameter, returns a tuple (is_retired, type_is_bool).
678    """
679    self.__dict__['__is_retired_flag_func'] = is_retired_flag_func
680
681  def _parse_args(self, args, known_only):
682    """Helper function to do the main argument parsing.
683
684    This function goes through args and does the bulk of the flag parsing.
685    It will find the corresponding flag in our flag dictionary, and call its
686    .parse() method on the flag value.
687
688    Args:
689      args: [str], a list of strings with the arguments to parse.
690      known_only: bool, if True, parse and remove known flags; return the rest
691        untouched. Unknown flags specified by --undefok are not returned.
692
693    Returns:
694      A tuple with the following:
695          unknown_flags: List of (flag name, arg) for flags we don't know about.
696          unparsed_args: List of arguments we did not parse.
697
698    Raises:
699       Error: Raised on any parsing error.
700       ValueError: Raised on flag value parsing error.
701    """
702    unparsed_names_and_args = []  # A list of (flag name or None, arg).
703    undefok = set()
704    retired_flag_func = self.__dict__['__is_retired_flag_func']
705
706    flag_dict = self._flags()
707    args = iter(args)
708    for arg in args:
709      value = None
710
711      def get_value():
712        # pylint: disable=cell-var-from-loop
713        try:
714          return next(args) if value is None else value
715        except StopIteration:
716          raise _exceptions.Error('Missing value for flag ' + arg)  # pylint: disable=undefined-loop-variable
717
718      if not arg.startswith('-'):
719        # A non-argument: default is break, GNU is skip.
720        unparsed_names_and_args.append((None, arg))
721        if self.is_gnu_getopt():
722          continue
723        else:
724          break
725
726      if arg == '--':
727        if known_only:
728          unparsed_names_and_args.append((None, arg))
729        break
730
731      # At this point, arg must start with '-'.
732      if arg.startswith('--'):
733        arg_without_dashes = arg[2:]
734      else:
735        arg_without_dashes = arg[1:]
736
737      if '=' in arg_without_dashes:
738        name, value = arg_without_dashes.split('=', 1)
739      else:
740        name, value = arg_without_dashes, None
741
742      if not name:
743        # The argument is all dashes (including one dash).
744        unparsed_names_and_args.append((None, arg))
745        if self.is_gnu_getopt():
746          continue
747        else:
748          break
749
750      # --undefok is a special case.
751      if name == 'undefok':
752        value = get_value()
753        undefok.update(v.strip() for v in value.split(','))
754        undefok.update('no' + v.strip() for v in value.split(','))
755        continue
756
757      flag = flag_dict.get(name)
758      if flag is not None:
759        if flag.boolean and value is None:
760          value = 'true'
761        else:
762          value = get_value()
763      elif name.startswith('no') and len(name) > 2:
764        # Boolean flags can take the form of --noflag, with no value.
765        noflag = flag_dict.get(name[2:])
766        if noflag is not None and noflag.boolean:
767          if value is not None:
768            raise ValueError(arg + ' does not take an argument')
769          flag = noflag
770          value = 'false'
771
772      if retired_flag_func and flag is None:
773        is_retired, is_bool = retired_flag_func(name)
774
775        # If we didn't recognize that flag, but it starts with
776        # "no" then maybe it was a boolean flag specified in the
777        # --nofoo form.
778        if not is_retired and name.startswith('no'):
779          is_retired, is_bool = retired_flag_func(name[2:])
780          is_retired = is_retired and is_bool
781
782        if is_retired:
783          if not is_bool and value is None:
784            # This happens when a non-bool retired flag is specified
785            # in format of "--flag value".
786            get_value()
787          logging.error(
788              'Flag "%s" is retired and should no longer '
789              'be specified. See go/totw/90.', name)
790          continue
791
792      if flag is not None:
793        flag.parse(value)
794        flag.using_default_value = False
795      else:
796        unparsed_names_and_args.append((name, arg))
797
798    unknown_flags = []
799    unparsed_args = []
800    for name, arg in unparsed_names_and_args:
801      if name is None:
802        # Positional arguments.
803        unparsed_args.append(arg)
804      elif name in undefok:
805        # Remove undefok flags.
806        continue
807      else:
808        # This is an unknown flag.
809        if known_only:
810          unparsed_args.append(arg)
811        else:
812          unknown_flags.append((name, arg))
813
814    unparsed_args.extend(list(args))
815    return unknown_flags, unparsed_args
816
817  def is_parsed(self):
818    """Returns whether flags were parsed."""
819    return self.__dict__['__flags_parsed']
820
821  def mark_as_parsed(self):
822    """Explicitly marks flags as parsed.
823
824    Use this when the caller knows that this FlagValues has been parsed as if
825    a ``__call__()`` invocation has happened.  This is only a public method for
826    use by things like appcommands which do additional command like parsing.
827    """
828    self.__dict__['__flags_parsed'] = True
829
830  def unparse_flags(self):
831    """Unparses all flags to the point before any FLAGS(argv) was called."""
832    for f in self._flags().values():
833      f.unparse()
834    # We log this message before marking flags as unparsed to avoid a
835    # problem when the logging library causes flags access.
836    logging.info('unparse_flags() called; flags access will now raise errors.')
837    self.__dict__['__flags_parsed'] = False
838    self.__dict__['__unparse_flags_called'] = True
839
840  def flag_values_dict(self):
841    """Returns a dictionary that maps flag names to flag values."""
842    return {name: flag.value for name, flag in self._flags().items()}
843
844  def __str__(self):
845    """Returns a help string for all known flags."""
846    return self.get_help()
847
848  def get_help(self, prefix='', include_special_flags=True):
849    """Returns a help string for all known flags.
850
851    Args:
852      prefix: str, per-line output prefix.
853      include_special_flags: bool, whether to include description of
854        SPECIAL_FLAGS, i.e. --flagfile and --undefok.
855
856    Returns:
857      str, formatted help message.
858    """
859    flags_by_module = self.flags_by_module_dict()
860    if flags_by_module:
861      modules = sorted(flags_by_module)
862      # Print the help for the main module first, if possible.
863      main_module = sys.argv[0]
864      if main_module in modules:
865        modules.remove(main_module)
866        modules = [main_module] + modules
867      return self._get_help_for_modules(modules, prefix, include_special_flags)
868    else:
869      output_lines = []
870      # Just print one long list of flags.
871      values = self._flags().values()
872      if include_special_flags:
873        values = itertools.chain(
874            values, _helpers.SPECIAL_FLAGS._flags().values())  # pylint: disable=protected-access
875      self._render_flag_list(values, output_lines, prefix)
876      return '\n'.join(output_lines)
877
878  def _get_help_for_modules(self, modules, prefix, include_special_flags):
879    """Returns the help string for a list of modules.
880
881    Private to absl.flags package.
882
883    Args:
884      modules: List[str], a list of modules to get the help string for.
885      prefix: str, a string that is prepended to each generated help line.
886      include_special_flags: bool, whether to include description of
887        SPECIAL_FLAGS, i.e. --flagfile and --undefok.
888    """
889    output_lines = []
890    for module in modules:
891      self._render_our_module_flags(module, output_lines, prefix)
892    if include_special_flags:
893      self._render_module_flags(
894          'absl.flags',
895          _helpers.SPECIAL_FLAGS._flags().values(),  # pylint: disable=protected-access
896          output_lines,
897          prefix)
898    return '\n'.join(output_lines)
899
900  def _render_module_flags(self, module, flags, output_lines, prefix=''):
901    """Returns a help string for a given module."""
902    if not isinstance(module, str):
903      module = module.__name__
904    output_lines.append('\n%s%s:' % (prefix, module))
905    self._render_flag_list(flags, output_lines, prefix + '  ')
906
907  def _render_our_module_flags(self, module, output_lines, prefix=''):
908    """Returns a help string for a given module."""
909    flags = self.get_flags_for_module(module)
910    if flags:
911      self._render_module_flags(module, flags, output_lines, prefix)
912
913  def _render_our_module_key_flags(self, module, output_lines, prefix=''):
914    """Returns a help string for the key flags of a given module.
915
916    Args:
917      module: module|str, the module to render key flags for.
918      output_lines: [str], a list of strings.  The generated help message lines
919        will be appended to this list.
920      prefix: str, a string that is prepended to each generated help line.
921    """
922    key_flags = self.get_key_flags_for_module(module)
923    if key_flags:
924      self._render_module_flags(module, key_flags, output_lines, prefix)
925
926  def module_help(self, module):
927    """Describes the key flags of a module.
928
929    Args:
930      module: module|str, the module to describe the key flags for.
931
932    Returns:
933      str, describing the key flags of a module.
934    """
935    helplist = []
936    self._render_our_module_key_flags(module, helplist)
937    return '\n'.join(helplist)
938
939  def main_module_help(self):
940    """Describes the key flags of the main module.
941
942    Returns:
943      str, describing the key flags of the main module.
944    """
945    return self.module_help(sys.argv[0])
946
947  def _render_flag_list(self, flaglist, output_lines, prefix='  '):
948    fl = self._flags()
949    special_fl = _helpers.SPECIAL_FLAGS._flags()  # pylint: disable=protected-access
950    flaglist = [(flag.name, flag) for flag in flaglist]
951    flaglist.sort()
952    flagset = {}
953    for (name, flag) in flaglist:
954      # It's possible this flag got deleted or overridden since being
955      # registered in the per-module flaglist.  Check now against the
956      # canonical source of current flag information, the _flags.
957      if fl.get(name, None) != flag and special_fl.get(name, None) != flag:
958        # a different flag is using this name now
959        continue
960      # only print help once
961      if flag in flagset:
962        continue
963      flagset[flag] = 1
964      flaghelp = ''
965      if flag.short_name:
966        flaghelp += '-%s,' % flag.short_name
967      if flag.boolean:
968        flaghelp += '--[no]%s:' % flag.name
969      else:
970        flaghelp += '--%s:' % flag.name
971      flaghelp += ' '
972      if flag.help:
973        flaghelp += flag.help
974      flaghelp = _helpers.text_wrap(
975          flaghelp, indent=prefix + '  ', firstline_indent=prefix)
976      if flag.default_as_str:
977        flaghelp += '\n'
978        flaghelp += _helpers.text_wrap(
979            '(default: %s)' % flag.default_as_str, indent=prefix + '  ')
980      if flag.parser.syntactic_help:
981        flaghelp += '\n'
982        flaghelp += _helpers.text_wrap(
983            '(%s)' % flag.parser.syntactic_help, indent=prefix + '  ')
984      output_lines.append(flaghelp)
985
986  def get_flag_value(self, name, default):  # pylint: disable=invalid-name
987    """Returns the value of a flag (if not None) or a default value.
988
989    Args:
990      name: str, the name of a flag.
991      default: Default value to use if the flag value is None.
992
993    Returns:
994      Requested flag value or default.
995    """
996
997    value = self.__getattr__(name)
998    if value is not None:  # Can't do if not value, b/c value might be '0' or ""
999      return value
1000    else:
1001      return default
1002
1003  def _is_flag_file_directive(self, flag_string):
1004    """Checks whether flag_string contain a --flagfile=<foo> directive."""
1005    if isinstance(flag_string, str):
1006      if flag_string.startswith('--flagfile='):
1007        return 1
1008      elif flag_string == '--flagfile':
1009        return 1
1010      elif flag_string.startswith('-flagfile='):
1011        return 1
1012      elif flag_string == '-flagfile':
1013        return 1
1014      else:
1015        return 0
1016    return 0
1017
1018  def _extract_filename(self, flagfile_str):
1019    """Returns filename from a flagfile_str of form -[-]flagfile=filename.
1020
1021    The cases of --flagfile foo and -flagfile foo shouldn't be hitting
1022    this function, as they are dealt with in the level above this
1023    function.
1024
1025    Args:
1026      flagfile_str: str, the flagfile string.
1027
1028    Returns:
1029      str, the filename from a flagfile_str of form -[-]flagfile=filename.
1030
1031    Raises:
1032      Error: Raised when illegal --flagfile is provided.
1033    """
1034    if flagfile_str.startswith('--flagfile='):
1035      return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip())
1036    elif flagfile_str.startswith('-flagfile='):
1037      return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip())
1038    else:
1039      raise _exceptions.Error('Hit illegal --flagfile type: %s' % flagfile_str)
1040
1041  def _get_flag_file_lines(self, filename, parsed_file_stack=None):
1042    """Returns the useful (!=comments, etc) lines from a file with flags.
1043
1044    Args:
1045      filename: str, the name of the flag file.
1046      parsed_file_stack: [str], a list of the names of the files that we have
1047        recursively encountered at the current depth. MUTATED BY THIS FUNCTION
1048        (but the original value is preserved upon successfully returning from
1049        function call).
1050
1051    Returns:
1052      List of strings. See the note below.
1053
1054    NOTE(springer): This function checks for a nested --flagfile=<foo>
1055    tag and handles the lower file recursively. It returns a list of
1056    all the lines that _could_ contain command flags. This is
1057    EVERYTHING except whitespace lines and comments (lines starting
1058    with '#' or '//').
1059    """
1060    # For consistency with the cpp version, ignore empty values.
1061    if not filename:
1062      return []
1063    if parsed_file_stack is None:
1064      parsed_file_stack = []
1065    # We do a little safety check for reparsing a file we've already encountered
1066    # at a previous depth.
1067    if filename in parsed_file_stack:
1068      sys.stderr.write('Warning: Hit circular flagfile dependency. Ignoring'
1069                       ' flagfile: %s\n' % (filename,))
1070      return []
1071    else:
1072      parsed_file_stack.append(filename)
1073
1074    line_list = []  # All line from flagfile.
1075    flag_line_list = []  # Subset of lines w/o comments, blanks, flagfile= tags.
1076    try:
1077      file_obj = open(filename, 'r')
1078    except IOError as e_msg:
1079      raise _exceptions.CantOpenFlagFileError(
1080          'ERROR:: Unable to open flagfile: %s' % e_msg)
1081
1082    with file_obj:
1083      line_list = file_obj.readlines()
1084
1085    # This is where we check each line in the file we just read.
1086    for line in line_list:
1087      if line.isspace():
1088        pass
1089      # Checks for comment (a line that starts with '#').
1090      elif line.startswith('#') or line.startswith('//'):
1091        pass
1092      # Checks for a nested "--flagfile=<bar>" flag in the current file.
1093      # If we find one, recursively parse down into that file.
1094      elif self._is_flag_file_directive(line):
1095        sub_filename = self._extract_filename(line)
1096        included_flags = self._get_flag_file_lines(
1097            sub_filename, parsed_file_stack=parsed_file_stack)
1098        flag_line_list.extend(included_flags)
1099      else:
1100        # Any line that's not a comment or a nested flagfile should get
1101        # copied into 2nd position.  This leaves earlier arguments
1102        # further back in the list, thus giving them higher priority.
1103        flag_line_list.append(line.strip())
1104
1105    parsed_file_stack.pop()
1106    return flag_line_list
1107
1108  def read_flags_from_files(self, argv, force_gnu=True):
1109    """Processes command line args, but also allow args to be read from file.
1110
1111    Args:
1112      argv: [str], a list of strings, usually sys.argv[1:], which may contain
1113        one or more flagfile directives of the form --flagfile="./filename".
1114        Note that the name of the program (sys.argv[0]) should be omitted.
1115      force_gnu: bool, if False, --flagfile parsing obeys the
1116        FLAGS.is_gnu_getopt() value. If True, ignore the value and always follow
1117        gnu_getopt semantics.
1118
1119    Returns:
1120      A new list which has the original list combined with what we read
1121      from any flagfile(s).
1122
1123    Raises:
1124      IllegalFlagValueError: Raised when --flagfile is provided with no
1125          argument.
1126
1127    This function is called by FLAGS(argv).
1128    It scans the input list for a flag that looks like:
1129    --flagfile=<somefile>. Then it opens <somefile>, reads all valid key
1130    and value pairs and inserts them into the input list in exactly the
1131    place where the --flagfile arg is found.
1132
1133    Note that your application's flags are still defined the usual way
1134    using absl.flags DEFINE_flag() type functions.
1135
1136    Notes (assuming we're getting a commandline of some sort as our input):
1137
1138    * For duplicate flags, the last one we hit should "win".
1139    * Since flags that appear later win, a flagfile's settings can be "weak"
1140        if the --flagfile comes at the beginning of the argument sequence,
1141        and it can be "strong" if the --flagfile comes at the end.
1142    * A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
1143        It will be expanded in exactly the spot where it is found.
1144    * In a flagfile, a line beginning with # or // is a comment.
1145    * Entirely blank lines _should_ be ignored.
1146    """
1147    rest_of_args = argv
1148    new_argv = []
1149    while rest_of_args:
1150      current_arg = rest_of_args[0]
1151      rest_of_args = rest_of_args[1:]
1152      if self._is_flag_file_directive(current_arg):
1153        # This handles the case of -(-)flagfile foo.  In this case the
1154        # next arg really is part of this one.
1155        if current_arg == '--flagfile' or current_arg == '-flagfile':
1156          if not rest_of_args:
1157            raise _exceptions.IllegalFlagValueError(
1158                '--flagfile with no argument')
1159          flag_filename = os.path.expanduser(rest_of_args[0])
1160          rest_of_args = rest_of_args[1:]
1161        else:
1162          # This handles the case of (-)-flagfile=foo.
1163          flag_filename = self._extract_filename(current_arg)
1164        new_argv.extend(self._get_flag_file_lines(flag_filename))
1165      else:
1166        new_argv.append(current_arg)
1167        # Stop parsing after '--', like getopt and gnu_getopt.
1168        if current_arg == '--':
1169          break
1170        # Stop parsing after a non-flag, like getopt.
1171        if not current_arg.startswith('-'):
1172          if not force_gnu and not self.__dict__['__use_gnu_getopt']:
1173            break
1174        else:
1175          if ('=' not in current_arg and rest_of_args and
1176              not rest_of_args[0].startswith('-')):
1177            # If this is an occurrence of a legitimate --x y, skip the value
1178            # so that it won't be mistaken for a standalone arg.
1179            fl = self._flags()
1180            name = current_arg.lstrip('-')
1181            if name in fl and not fl[name].boolean:
1182              current_arg = rest_of_args[0]
1183              rest_of_args = rest_of_args[1:]
1184              new_argv.append(current_arg)
1185
1186    if rest_of_args:
1187      new_argv.extend(rest_of_args)
1188
1189    return new_argv
1190
1191  def flags_into_string(self):
1192    """Returns a string with the flags assignments from this FlagValues object.
1193
1194    This function ignores flags whose value is None.  Each flag
1195    assignment is separated by a newline.
1196
1197    NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString
1198    from https://github.com/gflags/gflags.
1199
1200    Returns:
1201      str, the string with the flags assignments from this FlagValues object.
1202      The flags are ordered by (module_name, flag_name).
1203    """
1204    module_flags = sorted(self.flags_by_module_dict().items())
1205    s = ''
1206    for unused_module_name, flags in module_flags:
1207      flags = sorted(flags, key=lambda f: f.name)
1208      for flag in flags:
1209        if flag.value is not None:
1210          s += flag.serialize() + '\n'
1211    return s
1212
1213  def append_flags_into_file(self, filename):
1214    """Appends all flags assignments from this FlagInfo object to a file.
1215
1216    Output will be in the format of a flagfile.
1217
1218    NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile
1219    from https://github.com/gflags/gflags.
1220
1221    Args:
1222      filename: str, name of the file.
1223    """
1224    with open(filename, 'a') as out_file:
1225      out_file.write(self.flags_into_string())
1226
1227  def write_help_in_xml_format(self, outfile=None):
1228    """Outputs flag documentation in XML format.
1229
1230    NOTE: We use element names that are consistent with those used by
1231    the C++ command-line flag library, from
1232    https://github.com/gflags/gflags.
1233    We also use a few new elements (e.g., <key>), but we do not
1234    interfere / overlap with existing XML elements used by the C++
1235    library.  Please maintain this consistency.
1236
1237    Args:
1238      outfile: File object we write to.  Default None means sys.stdout.
1239    """
1240    doc = minidom.Document()
1241    all_flag = doc.createElement('AllFlags')
1242    doc.appendChild(all_flag)
1243
1244    all_flag.appendChild(
1245        _helpers.create_xml_dom_element(doc, 'program',
1246                                        os.path.basename(sys.argv[0])))
1247
1248    usage_doc = sys.modules['__main__'].__doc__
1249    if not usage_doc:
1250      usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
1251    else:
1252      usage_doc = usage_doc.replace('%s', sys.argv[0])
1253    all_flag.appendChild(
1254        _helpers.create_xml_dom_element(doc, 'usage', usage_doc))
1255
1256    # Get list of key flags for the main module.
1257    key_flags = self.get_key_flags_for_module(sys.argv[0])
1258
1259    # Sort flags by declaring module name and next by flag name.
1260    flags_by_module = self.flags_by_module_dict()
1261    all_module_names = list(flags_by_module.keys())
1262    all_module_names.sort()
1263    for module_name in all_module_names:
1264      flag_list = [(f.name, f) for f in flags_by_module[module_name]]
1265      flag_list.sort()
1266      for unused_flag_name, flag in flag_list:
1267        is_key = flag in key_flags
1268        all_flag.appendChild(
1269            flag._create_xml_dom_element(  # pylint: disable=protected-access
1270                doc,
1271                module_name,
1272                is_key=is_key))
1273
1274    outfile = outfile or sys.stdout
1275    outfile.write(
1276        doc.toprettyxml(indent='  ', encoding='utf-8').decode('utf-8'))
1277    outfile.flush()
1278
1279  def _check_method_name_conflicts(self, name, flag):
1280    if flag.allow_using_method_names:
1281      return
1282    short_name = flag.short_name
1283    flag_names = {name} if short_name is None else {name, short_name}
1284    for flag_name in flag_names:
1285      if flag_name in self.__dict__['__banned_flag_names']:
1286        raise _exceptions.FlagNameConflictsWithMethodError(
1287            'Cannot define a flag named "{name}". It conflicts with a method '
1288            'on class "{class_name}". To allow defining it, use '
1289            'allow_using_method_names and access the flag value with '
1290            "FLAGS['{name}'].value. FLAGS.{name} returns the method, "
1291            'not the flag value.'.format(
1292                name=flag_name, class_name=type(self).__name__))
1293
1294
1295FLAGS = FlagValues()
1296
1297
1298class FlagHolder(Generic[_T]):
1299  """Holds a defined flag.
1300
1301  This facilitates a cleaner api around global state. Instead of::
1302
1303      flags.DEFINE_integer('foo', ...)
1304      flags.DEFINE_integer('bar', ...)
1305
1306      def method():
1307        # prints parsed value of 'bar' flag
1308        print(flags.FLAGS.foo)
1309        # runtime error due to typo or possibly bad coding style.
1310        print(flags.FLAGS.baz)
1311
1312  it encourages code like::
1313
1314      _FOO_FLAG = flags.DEFINE_integer('foo', ...)
1315      _BAR_FLAG = flags.DEFINE_integer('bar', ...)
1316
1317      def method():
1318        print(_FOO_FLAG.value)
1319        print(_BAR_FLAG.value)
1320
1321  since the name of the flag appears only once in the source code.
1322  """
1323
1324  def __init__(self, flag_values, flag, ensure_non_none_value=False):
1325    """Constructs a FlagHolder instance providing typesafe access to flag.
1326
1327    Args:
1328      flag_values: The container the flag is registered to.
1329      flag: The flag object for this flag.
1330      ensure_non_none_value: Is the value of the flag allowed to be None.
1331    """
1332    self._flagvalues = flag_values
1333    # We take the entire flag object, but only keep the name. Why?
1334    # - We want FlagHolder[T] to be generic container
1335    # - flag_values contains all flags, so has no reference to T.
1336    # - typecheckers don't like to see a generic class where none of the ctor
1337    #   arguments refer to the generic type.
1338    self._name = flag.name
1339    # We intentionally do NOT check if the default value is None.
1340    # This allows future use of this for "required flags with None default"
1341    self._ensure_non_none_value = ensure_non_none_value
1342
1343  def __eq__(self, other):
1344    raise TypeError(
1345        "unsupported operand type(s) for ==: '{0}' and '{1}' "
1346        "(did you mean to use '{0}.value' instead?)".format(
1347            type(self).__name__, type(other).__name__))
1348
1349  def __bool__(self):
1350    raise TypeError(
1351        "bool() not supported for instances of type '{0}' "
1352        "(did you mean to use '{0}.value' instead?)".format(
1353            type(self).__name__))
1354
1355  __nonzero__ = __bool__
1356
1357  @property
1358  def name(self):
1359    return self._name
1360
1361  @property
1362  def value(self):
1363    """Returns the value of the flag.
1364
1365    If ``_ensure_non_none_value`` is ``True``, then return value is not
1366    ``None``.
1367
1368    Raises:
1369      UnparsedFlagAccessError: if flag parsing has not finished.
1370      IllegalFlagValueError: if value is None unexpectedly.
1371    """
1372    val = getattr(self._flagvalues, self._name)
1373    if self._ensure_non_none_value and val is None:
1374      raise _exceptions.IllegalFlagValueError(
1375          'Unexpected None value for flag %s' % self._name)
1376    return val
1377
1378  @property
1379  def default(self):
1380    """Returns the default value of the flag."""
1381    return self._flagvalues[self._name].default
1382
1383  @property
1384  def present(self):
1385    """Returns True if the flag was parsed from command-line flags."""
1386    return bool(self._flagvalues[self._name].present)
1387
1388
1389def resolve_flag_ref(flag_ref, flag_values):
1390  """Helper to validate and resolve a flag reference argument."""
1391  if isinstance(flag_ref, FlagHolder):
1392    new_flag_values = flag_ref._flagvalues  # pylint: disable=protected-access
1393    if flag_values != FLAGS and flag_values != new_flag_values:
1394      raise ValueError(
1395          'flag_values must not be customized when operating on a FlagHolder')
1396    return flag_ref.name, new_flag_values
1397  return flag_ref, flag_values
1398
1399
1400def resolve_flag_refs(flag_refs, flag_values):
1401  """Helper to validate and resolve flag reference list arguments."""
1402  fv = None
1403  names = []
1404  for ref in flag_refs:
1405    if isinstance(ref, FlagHolder):
1406      newfv = ref._flagvalues  # pylint: disable=protected-access
1407      name = ref.name
1408    else:
1409      newfv = flag_values
1410      name = ref
1411    if fv and fv != newfv:
1412      raise ValueError(
1413          'multiple FlagValues instances used in invocation. '
1414          'FlagHolders must be registered to the same FlagValues instance as '
1415          'do flag names, if provided.')
1416    fv = newfv
1417    names.append(name)
1418  return names, fv
1419