Skip to content

Commit 9499935

Browse files
Abseil Teamcopybara-github
Abseil Team
authored andcommitted
Add flags.override_value.
PiperOrigin-RevId: 572695699
1 parent 407b2d5 commit 9499935

File tree

4 files changed

+145
-3
lines changed

4 files changed

+145
-3
lines changed

CHANGELOG.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com).
66

77
## Unreleased
88

9+
### Added
10+
11+
* (flags) Added `absl.flags.override_value` function to provide `FlagHolder`
12+
with a construct to modify values. The new interface parallels
13+
`absl.flags.FlagValues.__setattr__` but checks that the provided value
14+
conforms to the flag's expected type.
15+
916
### Fixed
1017

1118
* The flag `foo` no longer retains the value `bar` after `FLAGS.foo = bar`
@@ -30,9 +37,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com).
3037
* (testing) `absltest.TestCase.assertSameStructure()` now uses the test case's
3138
equality functions (registered with `TestCase.addTypeEqualityFunc()`) for
3239
comparing leaves of the structure.
33-
* (testing) `abslTest.TestCase.fail()` now names its arguments
34-
`(self, msg=None, user_msg=None)`, and not `(self, msg=None, prefix=None)`,
35-
better reflecting the behavior and usage of the two message arguments.
40+
* (testing) `abslTest.TestCase.fail()` now names its arguments `(self,
41+
msg=None, user_msg=None)`, and not `(self, msg=None, prefix=None)`, better
42+
reflecting the behavior and usage of the two message arguments.
3643
* `DEFINE_enum`, `DEFINE_multi_enum`, and `EnumParser` now raise errors when
3744
`enum_values` is provided as a single string value. Additionally,
3845
`EnumParser.enum_values` is now stored as a list copy of the provided

absl/flags/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
'mark_bool_flags_as_mutual_exclusive',
7171
# Flag modifiers.
7272
'set_default',
73+
'override_value',
7374
# Key flag related functions.
7475
'declare_key_flag',
7576
'adopt_module_key_flags',
@@ -156,6 +157,7 @@
156157

157158
# Flag modifiers.
158159
set_default = _defines.set_default
160+
override_value = _defines.override_value
159161

160162
# Key flag related functions.
161163
declare_key_flag = _defines.declare_key_flag

absl/flags/_defines.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,31 @@ def set_default(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
212212
flag_holder._flagvalues.set_default(flag_holder.name, value) # pylint: disable=protected-access
213213

214214

215+
def override_value(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
216+
"""Overrides the value of the provided flag.
217+
218+
This value takes precedent over the default value and, when called after flag
219+
parsing, any value provided at the command line.
220+
221+
Args:
222+
flag_holder: FlagHolder, the flag to modify.
223+
value: The new value.
224+
225+
Raises:
226+
IllegalFlagValueError: The value did not pass the flag parser or validators.
227+
"""
228+
fv = flag_holder._flagvalues # pylint: disable=protected-access
229+
# Ensure the new value satisfies the flag's parser while avoiding side
230+
# effects of calling parse().
231+
parsed = fv[flag_holder.name]._parse(value) # pylint: disable=protected-access
232+
if parsed != value:
233+
raise _exceptions.IllegalFlagValueError(
234+
'flag %s: parsed value %r not equal to original %r'
235+
% (flag_holder.name, parsed, value)
236+
)
237+
setattr(fv, flag_holder.name, value)
238+
239+
215240
def _internal_declare_key_flags(
216241
flag_names: List[str],
217242
flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,

absl/flags/tests/flags_test.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,6 +2592,114 @@ def test_failure_on_type_protected_none_default(self):
25922592
_ = int_holder.value # Will also fail on later access.
25932593

25942594

2595+
class OverrideValueTest(absltest.TestCase):
2596+
2597+
def setUp(self):
2598+
super().setUp()
2599+
self.flag_values = flags.FlagValues()
2600+
2601+
def test_success(self):
2602+
int_holder = flags.DEFINE_integer(
2603+
'an_int', 1, 'an int', flag_values=self.flag_values
2604+
)
2605+
2606+
flags.override_value(int_holder, 2)
2607+
self.flag_values.mark_as_parsed()
2608+
2609+
self.assertEqual(int_holder.value, 2)
2610+
2611+
def test_update_after_parse(self):
2612+
int_holder = flags.DEFINE_integer(
2613+
'an_int', 1, 'an int', flag_values=self.flag_values
2614+
)
2615+
2616+
self.flag_values.mark_as_parsed()
2617+
flags.override_value(int_holder, 2)
2618+
2619+
self.assertEqual(int_holder.value, 2)
2620+
2621+
def test_overrides_explicit_assignment(self):
2622+
int_holder = flags.DEFINE_integer(
2623+
'an_int', 1, 'an int', flag_values=self.flag_values
2624+
)
2625+
2626+
self.flag_values.mark_as_parsed()
2627+
self.flag_values.an_int = 3
2628+
flags.override_value(int_holder, 2)
2629+
2630+
self.assertEqual(int_holder.value, 2)
2631+
2632+
def test_overriden_by_explicit_assignment(self):
2633+
int_holder = flags.DEFINE_integer(
2634+
'an_int', 1, 'an int', flag_values=self.flag_values
2635+
)
2636+
2637+
self.flag_values.mark_as_parsed()
2638+
flags.override_value(int_holder, 2)
2639+
self.flag_values.an_int = 3
2640+
2641+
self.assertEqual(int_holder.value, 3)
2642+
2643+
def test_multi_flag(self):
2644+
multi_holder = flags.DEFINE_multi_string(
2645+
'strs', [], 'some strs', flag_values=self.flag_values
2646+
)
2647+
2648+
flags.override_value(multi_holder, ['a', 'b'])
2649+
self.flag_values.mark_as_parsed()
2650+
2651+
self.assertEqual(multi_holder.value, ['a', 'b'])
2652+
2653+
def test_failure_on_invalid_type(self):
2654+
int_holder = flags.DEFINE_integer(
2655+
'an_int', 1, 'an int', flag_values=self.flag_values
2656+
)
2657+
2658+
self.flag_values.mark_as_parsed()
2659+
2660+
with self.assertRaises(flags.IllegalFlagValueError):
2661+
flags.override_value(int_holder, 'a') # pytype: disable=wrong-arg-types
2662+
2663+
self.assertEqual(int_holder.value, 1)
2664+
2665+
def test_failure_on_unparsed_value(self):
2666+
int_holder = flags.DEFINE_integer(
2667+
'an_int', 1, 'an int', flag_values=self.flag_values
2668+
)
2669+
2670+
self.flag_values.mark_as_parsed()
2671+
2672+
with self.assertRaises(flags.IllegalFlagValueError):
2673+
flags.override_value(int_holder, '2') # pytype: disable=wrong-arg-types
2674+
2675+
def test_failure_on_parser_rejection(self):
2676+
int_holder = flags.DEFINE_integer(
2677+
'an_int', 1, 'an int', flag_values=self.flag_values, upper_bound=5
2678+
)
2679+
2680+
self.flag_values.mark_as_parsed()
2681+
2682+
with self.assertRaises(flags.IllegalFlagValueError):
2683+
flags.override_value(int_holder, 6)
2684+
2685+
self.assertEqual(int_holder.value, 1)
2686+
2687+
def test_failure_on_validator_rejection(self):
2688+
int_holder = flags.DEFINE_integer(
2689+
'an_int', 1, 'an int', flag_values=self.flag_values
2690+
)
2691+
flags.register_validator(
2692+
int_holder.name, lambda x: x < 5, flag_values=self.flag_values
2693+
)
2694+
2695+
self.flag_values.mark_as_parsed()
2696+
2697+
with self.assertRaises(flags.IllegalFlagValueError):
2698+
flags.override_value(int_holder, 6)
2699+
2700+
self.assertEqual(int_holder.value, 1)
2701+
2702+
25952703
class KeyFlagsTest(absltest.TestCase):
25962704

25972705
def setUp(self):

0 commit comments

Comments
 (0)