Skip to content

Commit

Permalink
Merge commit for internal changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
yilei committed Oct 13, 2022
2 parents a0ae316 + c75edbb commit 042ca20
Show file tree
Hide file tree
Showing 17 changed files with 501 additions and 98 deletions.
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com).

## Unreleased

Nothing notable unreleased.

## 1.3.0 (2022-10-11)

### Added

* (flags) Added a new `absl.flags.set_default` function that updates the flag
default for a provided `FlagHolder`. This parallels the
`absl.flags.FlagValues.set_default` interface which takes a flag name.
* (flags) The following functions now also accept `FlagHolder` instance(s) in
addition to flag name(s) as their first positional argument:
- `flags.register_validator`
- `flags.validator`
- `flags.register_multi_flags_validator`
- `flags.multi_flags_validator`
- `flags.mark_flag_as_required`
- `flags.mark_flags_as_required`
- `flags.mark_flags_as_mutual_exclusive`
- `flags.mark_bool_flags_as_mutual_exclusive`
- `flags.declare_key_flag`

### Changed

* (testing) Assertions `assertRaisesWithPredicateMatch` and
`assertRaisesWithLiteralMatch` now capture the raised `Exception` for
further analysis when used as a context manager.
* (testing) TextAndXMLTestRunner now produces time duration values with
millisecond precision in XML test result output.
* (flags) Keyword access to `flag_name` arguments in the following functions
is deprecated. This parameter will be renamed in a future 2.0.0 release.
- `flags.register_validator`
- `flags.validator`
- `flags.register_multi_flags_validator`
- `flags.multi_flags_validator`
- `flags.mark_flag_as_required`
- `flags.mark_flags_as_required`
- `flags.mark_flags_as_mutual_exclusive`
- `flags.mark_bool_flags_as_mutual_exclusive`
- `flags.declare_key_flag`

## 1.2.0 (2022-07-18)

Expand Down
5 changes: 5 additions & 0 deletions absl/flags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
'mark_flags_as_required',
'mark_flags_as_mutual_exclusive',
'mark_bool_flags_as_mutual_exclusive',
# Flag modifiers.
'set_default',
# Key flag related functions.
'declare_key_flag',
'adopt_module_key_flags',
Expand Down Expand Up @@ -152,6 +154,9 @@
mark_flags_as_mutual_exclusive = _validators.mark_flags_as_mutual_exclusive
mark_bool_flags_as_mutual_exclusive = _validators.mark_bool_flags_as_mutual_exclusive

# Flag modifiers.
set_default = _defines.set_default

# Key flag related functions.
declare_key_flag = _defines.declare_key_flag
adopt_module_key_flags = _defines.adopt_module_key_flags
Expand Down
8 changes: 4 additions & 4 deletions absl/flags/_argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class ArgumentSerializer(object):

def serialize(self, value):
"""Returns a serialized string of the value."""
return _helpers.str_or_unicode(value)
return str(value)


class NumericParser(ArgumentParser):
Expand Down Expand Up @@ -454,7 +454,7 @@ def __init__(self, list_sep):

def serialize(self, value):
"""See base class."""
return self.list_sep.join([_helpers.str_or_unicode(x) for x in value])
return self.list_sep.join([str(x) for x in value])


class EnumClassListSerializer(ListSerializer):
Expand Down Expand Up @@ -498,7 +498,7 @@ def serialize(self, value):

# We need the returned value to be pure ascii or Unicodes so that
# when the xml help is generated they are usefully encodable.
return _helpers.str_or_unicode(serialized_value)
return str(serialized_value)


class EnumClassSerializer(ArgumentSerializer):
Expand All @@ -514,7 +514,7 @@ def __init__(self, lowercase):

def serialize(self, value):
"""Returns a serialized string of the Enum class value."""
as_string = _helpers.str_or_unicode(value.name)
as_string = str(value.name)
return as_string.lower() if self._lowercase else as_string


Expand Down
31 changes: 24 additions & 7 deletions absl/flags/_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,23 @@ def DEFINE_flag( # pylint: disable=invalid-name
fv, flag, ensure_non_none_value=ensure_non_none_value)


def set_default(flag_holder, value):
"""Changes the default value of the provided flag object.
The flag's current value is also updated if the flag is currently using
the default value, i.e. not specified in the command line, and not set
by FLAGS.name = value.
Args:
flag_holder: FlagHolder, the flag to modify.
value: The new default value.
Raises:
IllegalFlagValueError: Raised when value is not valid.
"""
flag_holder._flagvalues.set_default(flag_holder.name, value) # pylint: disable=protected-access


def _internal_declare_key_flags(flag_names,
flag_values=_flagvalues.FLAGS,
key_flag_values=None):
Expand All @@ -157,8 +174,7 @@ def _internal_declare_key_flags(flag_names,
adopt_module_key_flags instead.
Args:
flag_names: [str], a list of strings that are names of already-registered
Flag objects.
flag_names: [str], a list of names of already-registered Flag objects.
flag_values: :class:`FlagValues`, the FlagValues instance with which the
flags listed in flag_names have registered (the value of the flag_values
argument from the ``DEFINE_*`` calls that defined those flags). This
Expand All @@ -176,8 +192,7 @@ def _internal_declare_key_flags(flag_names,
module = _helpers.get_calling_module()

for flag_name in flag_names:
flag = flag_values[flag_name]
key_flag_values.register_key_flag_for_module(module, flag)
key_flag_values.register_key_flag_for_module(module, flag_values[flag_name])


def declare_key_flag(flag_name, flag_values=_flagvalues.FLAGS):
Expand All @@ -194,16 +209,18 @@ def declare_key_flag(flag_name, flag_values=_flagvalues.FLAGS):
flags.declare_key_flag('flag_1')
Args:
flag_name: str, the name of an already declared flag. (Redeclaring flags as
key, including flags implicitly key because they were declared in this
module, is a no-op.)
flag_name: str | :class:`FlagHolder`, the name or holder of an already
declared flag. (Redeclaring flags as key, including flags implicitly key
because they were declared in this module, is a no-op.)
Positional-only parameter.
flag_values: :class:`FlagValues`, the FlagValues instance in which the
flag will be declared as a key flag. This should almost never need to be
overridden.
Raises:
ValueError: Raised if flag_name not defined as a Python flag.
"""
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
if flag_name in _helpers.SPECIAL_FLAGS:
# Take care of the special flags, e.g., --flagfile, --undefok.
# These flags are defined in SPECIAL_FLAGS, and are treated
Expand Down
5 changes: 4 additions & 1 deletion absl/flags/_defines.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,11 @@ def DEFINE_alias(
...


def set_default(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
...


def declare_key_flag(flag_name: Text,
def declare_key_flag(flag_name: Union[Text, _flagvalues.FlagHolder],
flag_values: _flagvalues.FlagValues = ...) -> None:
...

Expand Down
2 changes: 1 addition & 1 deletion absl/flags/_flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def _get_parsed_value_as_string(self, value):
return repr('true')
else:
return repr('false')
return repr(_helpers.str_or_unicode(value))
return repr(str(value))

def parse(self, argument):
"""Parses string and sets flag value.
Expand Down
42 changes: 35 additions & 7 deletions absl/flags/_flagvalues.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,7 @@ def __setitem__(self, name, flag):
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')
Expand Down Expand Up @@ -632,7 +628,7 @@ def __call__(self, argv, known_only=False):
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:
Expand Down Expand Up @@ -1006,7 +1002,7 @@ def get_flag_value(self, name, default): # pylint: disable=invalid-name

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':
Expand Down Expand Up @@ -1388,3 +1384,35 @@ def default(self):
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
40 changes: 4 additions & 36 deletions absl/flags/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@


_DEFAULT_HELP_WIDTH = 80 # Default width of help output.
_MIN_HELP_WIDTH = 40 # Minimal "sane" width of help output. We assume that any
# value below 40 is unreasonable.
# Minimal "sane" width of help output. We assume that any value below 40 is
# unreasonable.
_MIN_HELP_WIDTH = 40

# Define the allowed error rate in an input string to get suggestions.
#
Expand Down Expand Up @@ -125,32 +126,6 @@ def get_calling_module():
return get_calling_module_object_and_name().module_name


def str_or_unicode(value):
"""Converts a value to a python string.
Behavior of this function is intentionally different in Python2/3.
In Python2, the given value is attempted to convert to a str (byte string).
If it contains non-ASCII characters, it is converted to a unicode instead.
In Python3, the given value is always converted to a str (unicode string).
This behavior reflects the (bad) practice in Python2 to try to represent
a string as str as long as it contains ASCII characters only.
Args:
value: An object to be converted to a string.
Returns:
A string representation of the given value. See the description above
for its type.
"""
try:
return str(value)
except UnicodeEncodeError:
return unicode(value) # Python3 should never come here


def create_xml_dom_element(doc, name, value):
"""Returns an XML DOM element with name and text value.
Expand All @@ -164,7 +139,7 @@ def create_xml_dom_element(doc, name, value):
Returns:
An instance of minidom.Element.
"""
s = str_or_unicode(value)
s = str(value)
if isinstance(value, bool):
# Display boolean values as the C++ flag library does: no caps.
s = s.lower()
Expand Down Expand Up @@ -424,10 +399,3 @@ def doc_to_help(doc):
doc = re.sub(r'(?<=\S)\n(?=\S)', ' ', doc, flags=re.M)

return doc


def is_bytes_or_string(maybe_string):
if str is bytes:
return isinstance(maybe_string, basestring)
else:
return isinstance(maybe_string, (str, bytes))
Loading

0 comments on commit 042ca20

Please sign in to comment.