aboutsummaryrefslogtreecommitdiff
path: root/absl/flags/_flagvalues.py
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/_flagvalues.py')
-rw-r--r--absl/flags/_flagvalues.py139
1 files changed, 85 insertions, 54 deletions
diff --git a/absl/flags/_flagvalues.py b/absl/flags/_flagvalues.py
index 1b54fb3..937dc6c 100644
--- a/absl/flags/_flagvalues.py
+++ b/absl/flags/_flagvalues.py
@@ -37,36 +37,41 @@ _T = TypeVar('_T')
class FlagValues:
- """Registry of 'Flag' objects.
+ """Registry of :class:`~absl.flags.Flag` objects.
- A 'FlagValues' can then scan command line arguments, passing flag
+ A :class:`FlagValues` can then scan command line arguments, passing flag
arguments through to the 'Flag' objects that it owns. It also
provides easy access to the flag values. Typically only one
- 'FlagValues' object is needed by an application: flags.FLAGS
+ :class:`FlagValues` object is needed by an application:
+ :const:`FLAGS`.
This class is heavily overloaded:
- 'Flag' objects are registered via __setitem__:
+ :class:`Flag` objects are registered via ``__setitem__``::
+
FLAGS['longname'] = x # register a new flag
- The .value attribute of the registered 'Flag' objects can be accessed
- as attributes of this 'FlagValues' object, through __getattr__. Both
- the long and short name of the original 'Flag' objects can be used to
- access its value:
- FLAGS.longname # parsed flag value
- FLAGS.x # parsed flag value (short name)
+ The ``.value`` attribute of the registered :class:`~absl.flags.Flag` objects
+ can be accessed as attributes of this :class:`FlagValues` object, through
+ ``__getattr__``. Both the long and short name of the original
+ :class:`~absl.flags.Flag` objects can be used to access its value::
+
+ FLAGS.longname # parsed flag value
+ FLAGS.x # parsed flag value (short name)
+
+ Command line arguments are scanned and passed to the registered
+ :class:`~absl.flags.Flag` objects through the ``__call__`` method. Unparsed
+ arguments, including ``argv[0]`` (e.g. the program name) are returned::
- Command line arguments are scanned and passed to the registered 'Flag'
- objects through the __call__ method. Unparsed arguments, including
- argv[0] (e.g. the program name) are returned.
argv = FLAGS(sys.argv) # scan command line arguments
- The original registered Flag objects can be retrieved through the use
- of the dictionary-like operator, __getitem__:
+ The original registered :class:`~absl.flags.Flag` objects can be retrieved
+ through the use of the dictionary-like operator, ``__getitem__``::
+
x = FLAGS['longname'] # access the registered Flag object
- The str() operator of a 'FlagValues' object provides help for all of
- the registered 'Flag' objects.
+ The ``str()`` operator of a :class:`absl.flags.FlagValues` object provides
+ help for all of the registered :class:`~absl.flags.Flag` objects.
"""
# A note on collections.abc.Mapping:
@@ -407,11 +412,7 @@ class FlagValues:
fl = self._flags()
if not isinstance(flag, _flag.Flag):
raise _exceptions.IllegalFlagValueError(flag)
- if str is bytes and isinstance(name, unicode):
- # When using Python 2 with unicode_literals, allow it but encode it
- # into the bytes type we require.
- name = name.encode('utf-8')
- if not isinstance(name, type('')):
+ if not isinstance(name, str):
raise _exceptions.Error('Flag name must be a string')
if not name:
raise _exceptions.Error('Flag name cannot be empty')
@@ -627,7 +628,7 @@ class FlagValues:
TypeError: Raised on passing wrong type of arguments.
ValueError: Raised on flag value parsing error.
"""
- if _helpers.is_bytes_or_string(argv):
+ if isinstance(argv, (str, bytes)):
raise TypeError(
'argv should be a tuple/list of strings, not bytes or string.')
if not argv:
@@ -821,7 +822,7 @@ class FlagValues:
"""Explicitly marks flags as parsed.
Use this when the caller knows that this FlagValues has been parsed as if
- a __call__() invocation has happened. This is only a public method for
+ a ``__call__()`` invocation has happened. This is only a public method for
use by things like appcommands which do additional command like parsing.
"""
self.__dict__['__flags_parsed'] = True
@@ -1001,7 +1002,7 @@ class FlagValues:
def _is_flag_file_directive(self, flag_string):
"""Checks whether flag_string contain a --flagfile=<foo> directive."""
- if isinstance(flag_string, type('')):
+ if isinstance(flag_string, str):
if flag_string.startswith('--flagfile='):
return 1
elif flag_string == '--flagfile':
@@ -1133,14 +1134,15 @@ class FlagValues:
using absl.flags DEFINE_flag() type functions.
Notes (assuming we're getting a commandline of some sort as our input):
- --> For duplicate flags, the last one we hit should "win".
- --> Since flags that appear later win, a flagfile's settings can be "weak"
+
+ * For duplicate flags, the last one we hit should "win".
+ * Since flags that appear later win, a flagfile's settings can be "weak"
if the --flagfile comes at the beginning of the argument sequence,
and it can be "strong" if the --flagfile comes at the end.
- --> A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
+ * A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
It will be expanded in exactly the spot where it is found.
- --> In a flagfile, a line beginning with # or // is a comment.
- --> Entirely blank lines _should_ be ignored.
+ * In a flagfile, a line beginning with # or // is a comment.
+ * Entirely blank lines _should_ be ignored.
"""
rest_of_args = argv
new_argv = []
@@ -1296,29 +1298,25 @@ FLAGS = FlagValues()
class FlagHolder(Generic[_T]):
"""Holds a defined flag.
- This facilitates a cleaner api around global state. Instead of
-
- ```
- flags.DEFINE_integer('foo', ...)
- flags.DEFINE_integer('bar', ...)
- ...
- def method():
- # prints parsed value of 'bar' flag
- print(flags.FLAGS.foo)
- # runtime error due to typo or possibly bad coding style.
- print(flags.FLAGS.baz)
- ```
-
- it encourages code like
-
- ```
- FOO_FLAG = flags.DEFINE_integer('foo', ...)
- BAR_FLAG = flags.DEFINE_integer('bar', ...)
- ...
- def method():
- print(FOO_FLAG.value)
- print(BAR_FLAG.value)
- ```
+ This facilitates a cleaner api around global state. Instead of::
+
+ flags.DEFINE_integer('foo', ...)
+ flags.DEFINE_integer('bar', ...)
+
+ def method():
+ # prints parsed value of 'bar' flag
+ print(flags.FLAGS.foo)
+ # runtime error due to typo or possibly bad coding style.
+ print(flags.FLAGS.baz)
+
+ it encourages code like::
+
+ _FOO_FLAG = flags.DEFINE_integer('foo', ...)
+ _BAR_FLAG = flags.DEFINE_integer('bar', ...)
+
+ def method():
+ print(_FOO_FLAG.value)
+ print(_BAR_FLAG.value)
since the name of the flag appears only once in the source code.
"""
@@ -1364,7 +1362,8 @@ class FlagHolder(Generic[_T]):
def value(self):
"""Returns the value of the flag.
- If _ensure_non_none_value is True, then return value is not None.
+ If ``_ensure_non_none_value`` is ``True``, then return value is not
+ ``None``.
Raises:
UnparsedFlagAccessError: if flag parsing has not finished.
@@ -1385,3 +1384,35 @@ class FlagHolder(Generic[_T]):
def present(self):
"""Returns True if the flag was parsed from command-line flags."""
return bool(self._flagvalues[self._name].present)
+
+
+def resolve_flag_ref(flag_ref, flag_values):
+ """Helper to validate and resolve a flag reference argument."""
+ if isinstance(flag_ref, FlagHolder):
+ new_flag_values = flag_ref._flagvalues # pylint: disable=protected-access
+ if flag_values != FLAGS and flag_values != new_flag_values:
+ raise ValueError(
+ 'flag_values must not be customized when operating on a FlagHolder')
+ return flag_ref.name, new_flag_values
+ return flag_ref, flag_values
+
+
+def resolve_flag_refs(flag_refs, flag_values):
+ """Helper to validate and resolve flag reference list arguments."""
+ fv = None
+ names = []
+ for ref in flag_refs:
+ if isinstance(ref, FlagHolder):
+ newfv = ref._flagvalues # pylint: disable=protected-access
+ name = ref.name
+ else:
+ newfv = flag_values
+ name = ref
+ if fv and fv != newfv:
+ raise ValueError(
+ 'multiple FlagValues instances used in invocation. '
+ 'FlagHolders must be registered to the same FlagValues instance as '
+ 'do flag names, if provided.')
+ fv = newfv
+ names.append(name)
+ return names, fv