3022 lines
110 KiB
Python
3022 lines
110 KiB
Python
# Copyright 2017 The Abseil Authors.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Tests for absl.flags used as a package."""
|
|
|
|
import contextlib
|
|
import enum
|
|
import io
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from absl import flags
|
|
from absl.flags import _exceptions
|
|
from absl.flags import _helpers
|
|
from absl.flags.tests import module_bar
|
|
from absl.flags.tests import module_baz
|
|
from absl.flags.tests import module_foo
|
|
from absl.testing import absltest
|
|
|
|
FLAGS = flags.FLAGS
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def _use_gnu_getopt(flag_values, use_gnu_get_opt):
|
|
old_use_gnu_get_opt = flag_values.is_gnu_getopt()
|
|
flag_values.set_gnu_getopt(use_gnu_get_opt)
|
|
yield
|
|
flag_values.set_gnu_getopt(old_use_gnu_get_opt)
|
|
|
|
|
|
class FlagDictToArgsTest(absltest.TestCase):
|
|
|
|
def test_flatten_google_flag_map(self):
|
|
arg_dict = {
|
|
'week-end': None,
|
|
'estudia': False,
|
|
'trabaja': False,
|
|
'party': True,
|
|
'monday': 'party',
|
|
'score': 42,
|
|
'loadthatstuff': [42, 'hello', 'goodbye'],
|
|
}
|
|
self.assertSameElements(
|
|
('--week-end', '--noestudia', '--notrabaja', '--party',
|
|
'--monday=party', '--score=42', '--loadthatstuff=42,hello,goodbye'),
|
|
flags.flag_dict_to_args(arg_dict))
|
|
|
|
def test_flatten_google_flag_map_with_multi_flag(self):
|
|
arg_dict = {
|
|
'some_list': ['value1', 'value2'],
|
|
'some_multi_string': ['value3', 'value4'],
|
|
}
|
|
self.assertSameElements(
|
|
('--some_list=value1,value2', '--some_multi_string=value3',
|
|
'--some_multi_string=value4'),
|
|
flags.flag_dict_to_args(arg_dict, multi_flags={'some_multi_string'}))
|
|
|
|
|
|
class Fruit(enum.Enum):
|
|
APPLE = object()
|
|
ORANGE = object()
|
|
|
|
|
|
class CaseSensitiveFruit(enum.Enum):
|
|
apple = 1
|
|
orange = 2
|
|
APPLE = 3
|
|
|
|
|
|
class EmptyEnum(enum.Enum):
|
|
pass
|
|
|
|
|
|
class AliasFlagsTest(absltest.TestCase):
|
|
|
|
def setUp(self):
|
|
super(AliasFlagsTest, self).setUp()
|
|
self.flags = flags.FlagValues()
|
|
|
|
@property
|
|
def alias(self):
|
|
return self.flags['alias']
|
|
|
|
@property
|
|
def aliased(self):
|
|
return self.flags['aliased']
|
|
|
|
def define_alias(self, *args, **kwargs):
|
|
flags.DEFINE_alias(*args, flag_values=self.flags, **kwargs)
|
|
|
|
def define_integer(self, *args, **kwargs):
|
|
flags.DEFINE_integer(*args, flag_values=self.flags, **kwargs)
|
|
|
|
def define_multi_integer(self, *args, **kwargs):
|
|
flags.DEFINE_multi_integer(*args, flag_values=self.flags, **kwargs)
|
|
|
|
def define_string(self, *args, **kwargs):
|
|
flags.DEFINE_string(*args, flag_values=self.flags, **kwargs)
|
|
|
|
def assert_alias_mirrors_aliased(self, alias, aliased, ignore_due_to_bug=()):
|
|
# A few sanity checks to avoid false success
|
|
self.assertIn('FlagAlias', alias.__class__.__qualname__)
|
|
self.assertIsNot(alias, aliased)
|
|
self.assertNotEqual(aliased.name, alias.name)
|
|
|
|
alias_state = {}
|
|
aliased_state = {}
|
|
attrs = {
|
|
'allow_hide_cpp',
|
|
'allow_override',
|
|
'allow_override_cpp',
|
|
'allow_overwrite',
|
|
'allow_using_method_names',
|
|
'boolean',
|
|
'default',
|
|
'default_as_str',
|
|
'default_unparsed',
|
|
# TODO(rlevasseur): This should match, but a bug prevents it from being
|
|
# in sync.
|
|
# 'using_default_value',
|
|
'value',
|
|
}
|
|
attrs.difference_update(ignore_due_to_bug)
|
|
|
|
for attr in attrs:
|
|
alias_state[attr] = getattr(alias, attr)
|
|
aliased_state[attr] = getattr(aliased, attr)
|
|
|
|
self.assertEqual(aliased_state, alias_state, 'LHS is aliased; RHS is alias')
|
|
|
|
def test_serialize_multi(self):
|
|
self.define_multi_integer('aliased', [0, 1], '')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
actual = self.alias.serialize()
|
|
# TODO(rlevasseur): This should check for --alias=0\n--alias=1, but
|
|
# a bug causes it to serialize incorrectly.
|
|
self.assertEqual('--alias=[0, 1]', actual)
|
|
|
|
def test_allow_overwrite_false(self):
|
|
self.define_integer('aliased', None, 'help', allow_overwrite=False)
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
with self.assertRaisesRegex(flags.IllegalFlagValueError, 'already defined'):
|
|
self.flags(['./program', '--alias=1', '--aliased=2'])
|
|
|
|
self.assertEqual(1, self.alias.value)
|
|
self.assertEqual(1, self.aliased.value)
|
|
|
|
def test_aliasing_multi_no_default(self):
|
|
|
|
def define_flags():
|
|
self.flags = flags.FlagValues()
|
|
self.define_multi_integer('aliased', None, 'help')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
with self.subTest('after defining'):
|
|
define_flags()
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
self.assertIsNone(self.alias.value)
|
|
|
|
with self.subTest('set alias'):
|
|
define_flags()
|
|
self.flags(['./program', '--alias=1', '--alias=2'])
|
|
self.assertEqual([1, 2], self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
with self.subTest('set aliased'):
|
|
define_flags()
|
|
self.flags(['./program', '--aliased=1', '--aliased=2'])
|
|
self.assertEqual([1, 2], self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
with self.subTest('not setting anything'):
|
|
define_flags()
|
|
self.flags(['./program'])
|
|
self.assertEqual(None, self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
def test_aliasing_multi_with_default(self):
|
|
|
|
def define_flags():
|
|
self.flags = flags.FlagValues()
|
|
self.define_multi_integer('aliased', [0], 'help')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
with self.subTest('after defining'):
|
|
define_flags()
|
|
self.assertEqual([0], self.alias.default)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
with self.subTest('set alias'):
|
|
define_flags()
|
|
self.flags(['./program', '--alias=1', '--alias=2'])
|
|
self.assertEqual([1, 2], self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
self.assertEqual(2, self.alias.present)
|
|
# TODO(rlevasseur): This should assert 0, but a bug with aliases and
|
|
# MultiFlag causes the alias to increment aliased's present counter.
|
|
self.assertEqual(2, self.aliased.present)
|
|
|
|
with self.subTest('set aliased'):
|
|
define_flags()
|
|
self.flags(['./program', '--aliased=1', '--aliased=2'])
|
|
self.assertEqual([1, 2], self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
self.assertEqual(0, self.alias.present)
|
|
|
|
# TODO(rlevasseur): This should assert 0, but a bug with aliases and
|
|
# MultiFlag causes the alias to increment aliased present counter.
|
|
self.assertEqual(2, self.aliased.present)
|
|
|
|
with self.subTest('not setting anything'):
|
|
define_flags()
|
|
self.flags(['./program'])
|
|
self.assertEqual([0], self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
self.assertEqual(0, self.alias.present)
|
|
self.assertEqual(0, self.aliased.present)
|
|
|
|
def test_aliasing_regular(self):
|
|
|
|
def define_flags():
|
|
self.flags = flags.FlagValues()
|
|
self.define_string('aliased', '', 'help')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
define_flags()
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
|
|
self.flags(['./program', '--alias=1'])
|
|
self.assertEqual('1', self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
self.assertEqual(1, self.alias.present)
|
|
self.assertEqual('--alias=1', self.alias.serialize())
|
|
self.assertEqual(1, self.aliased.present)
|
|
|
|
define_flags()
|
|
self.flags(['./program', '--aliased=2'])
|
|
self.assertEqual('2', self.alias.value)
|
|
self.assert_alias_mirrors_aliased(self.alias, self.aliased)
|
|
self.assertEqual(0, self.alias.present)
|
|
self.assertEqual('--alias=2', self.alias.serialize())
|
|
self.assertEqual(1, self.aliased.present)
|
|
|
|
def test_defining_alias_doesnt_affect_aliased_state_regular(self):
|
|
self.define_string('aliased', 'default', 'help')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
self.assertEqual(0, self.aliased.present)
|
|
self.assertEqual(0, self.alias.present)
|
|
|
|
def test_defining_alias_doesnt_affect_aliased_state_multi(self):
|
|
self.define_multi_integer('aliased', [0], 'help')
|
|
self.define_alias('alias', 'aliased')
|
|
|
|
self.assertEqual([0], self.aliased.value)
|
|
self.assertEqual([0], self.aliased.default)
|
|
self.assertEqual(0, self.aliased.present)
|
|
|
|
self.assertEqual([0], self.aliased.value)
|
|
self.assertEqual([0], self.aliased.default)
|
|
self.assertEqual(0, self.alias.present)
|
|
|
|
|
|
class FlagsUnitTest(absltest.TestCase):
|
|
"""Flags Unit Test."""
|
|
|
|
maxDiff = None
|
|
|
|
def test_flags(self):
|
|
"""Test normal usage with no (expected) errors."""
|
|
# Define flags
|
|
number_test_framework_flags = len(FLAGS)
|
|
repeat_help = 'how many times to repeat (0-5)'
|
|
flags.DEFINE_integer(
|
|
'repeat', 4, repeat_help, lower_bound=0, short_name='r')
|
|
flags.DEFINE_string('name', 'Bob', 'namehelp')
|
|
flags.DEFINE_boolean('debug', 0, 'debughelp')
|
|
flags.DEFINE_boolean('q', 1, 'quiet mode')
|
|
flags.DEFINE_boolean('quack', 0, "superstring of 'q'")
|
|
flags.DEFINE_boolean('noexec', 1, 'boolean flag with no as prefix')
|
|
flags.DEFINE_float('float', 3.14, 'using floats')
|
|
flags.DEFINE_integer('octal', '0o666', 'using octals')
|
|
flags.DEFINE_integer('decimal', '666', 'using decimals')
|
|
flags.DEFINE_integer('hexadecimal', '0x666', 'using hexadecimals')
|
|
flags.DEFINE_integer('x', 3, 'how eXtreme to be')
|
|
flags.DEFINE_integer('l', 0x7fffffff00000000, 'how long to be')
|
|
flags.DEFINE_list('args', 'v=1,"vmodule=a=0,b=2"', 'a list of arguments')
|
|
flags.DEFINE_list('letters', 'a,b,c', 'a list of letters')
|
|
flags.DEFINE_list('numbers', [1, 2, 3], 'a list of numbers')
|
|
flags.DEFINE_enum('kwery', None, ['who', 'what', 'Why', 'where', 'when'],
|
|
'?')
|
|
flags.DEFINE_enum(
|
|
'sense', None, ['Case', 'case', 'CASE'], '?', case_sensitive=True)
|
|
flags.DEFINE_enum(
|
|
'cases',
|
|
None, ['UPPER', 'lower', 'Initial', 'Ot_HeR'],
|
|
'?',
|
|
case_sensitive=False)
|
|
flags.DEFINE_enum(
|
|
'funny',
|
|
None, ['Joke', 'ha', 'ha', 'ha', 'ha'],
|
|
'?',
|
|
case_sensitive=True)
|
|
flags.DEFINE_enum(
|
|
'blah',
|
|
None, ['bla', 'Blah', 'BLAH', 'blah'],
|
|
'?',
|
|
case_sensitive=False)
|
|
flags.DEFINE_string(
|
|
'only_once', None, 'test only sets this once', allow_overwrite=False)
|
|
flags.DEFINE_string(
|
|
'universe',
|
|
None,
|
|
'test tries to set this three times',
|
|
allow_overwrite=False)
|
|
|
|
# Specify number of flags defined above. The short_name defined
|
|
# for 'repeat' counts as an extra flag.
|
|
number_defined_flags = 22 + 1
|
|
self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags)
|
|
|
|
self.assertEqual(FLAGS.repeat, 4)
|
|
self.assertEqual(FLAGS.name, 'Bob')
|
|
self.assertEqual(FLAGS.debug, 0)
|
|
self.assertEqual(FLAGS.q, 1)
|
|
self.assertEqual(FLAGS.octal, 0o666)
|
|
self.assertEqual(FLAGS.decimal, 666)
|
|
self.assertEqual(FLAGS.hexadecimal, 0x666)
|
|
self.assertEqual(FLAGS.x, 3)
|
|
self.assertEqual(FLAGS.l, 0x7fffffff00000000)
|
|
self.assertEqual(FLAGS.args, ['v=1', 'vmodule=a=0,b=2'])
|
|
self.assertEqual(FLAGS.letters, ['a', 'b', 'c'])
|
|
self.assertEqual(FLAGS.numbers, [1, 2, 3])
|
|
self.assertIsNone(FLAGS.kwery)
|
|
self.assertIsNone(FLAGS.sense)
|
|
self.assertIsNone(FLAGS.cases)
|
|
self.assertIsNone(FLAGS.funny)
|
|
self.assertIsNone(FLAGS.blah)
|
|
|
|
flag_values = FLAGS.flag_values_dict()
|
|
self.assertEqual(flag_values['repeat'], 4)
|
|
self.assertEqual(flag_values['name'], 'Bob')
|
|
self.assertEqual(flag_values['debug'], 0)
|
|
self.assertEqual(flag_values['r'], 4) # Short for repeat.
|
|
self.assertEqual(flag_values['q'], 1)
|
|
self.assertEqual(flag_values['quack'], 0)
|
|
self.assertEqual(flag_values['x'], 3)
|
|
self.assertEqual(flag_values['l'], 0x7fffffff00000000)
|
|
self.assertEqual(flag_values['args'], ['v=1', 'vmodule=a=0,b=2'])
|
|
self.assertEqual(flag_values['letters'], ['a', 'b', 'c'])
|
|
self.assertEqual(flag_values['numbers'], [1, 2, 3])
|
|
self.assertIsNone(flag_values['kwery'])
|
|
self.assertIsNone(flag_values['sense'])
|
|
self.assertIsNone(flag_values['cases'])
|
|
self.assertIsNone(flag_values['funny'])
|
|
self.assertIsNone(flag_values['blah'])
|
|
|
|
# Verify string form of defaults
|
|
self.assertEqual(FLAGS['repeat'].default_as_str, "'4'")
|
|
self.assertEqual(FLAGS['name'].default_as_str, "'Bob'")
|
|
self.assertEqual(FLAGS['debug'].default_as_str, "'false'")
|
|
self.assertEqual(FLAGS['q'].default_as_str, "'true'")
|
|
self.assertEqual(FLAGS['quack'].default_as_str, "'false'")
|
|
self.assertEqual(FLAGS['noexec'].default_as_str, "'true'")
|
|
self.assertEqual(FLAGS['x'].default_as_str, "'3'")
|
|
self.assertEqual(FLAGS['l'].default_as_str, "'9223372032559808512'")
|
|
self.assertEqual(FLAGS['args'].default_as_str, '\'v=1,"vmodule=a=0,b=2"\'')
|
|
self.assertEqual(FLAGS['letters'].default_as_str, "'a,b,c'")
|
|
self.assertEqual(FLAGS['numbers'].default_as_str, "'1,2,3'")
|
|
|
|
# Verify that the iterator for flags yields all the keys
|
|
keys = list(FLAGS)
|
|
keys.sort()
|
|
reg_flags = list(FLAGS._flags())
|
|
reg_flags.sort()
|
|
self.assertEqual(keys, reg_flags)
|
|
|
|
# Parse flags
|
|
# .. empty command line
|
|
argv = ('./program',)
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
|
|
# .. non-empty command line
|
|
argv = ('./program', '--debug', '--name=Bob', '-q', '--x=8')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['debug'].present, 1)
|
|
FLAGS['debug'].present = 0 # Reset
|
|
self.assertEqual(FLAGS['name'].present, 1)
|
|
FLAGS['name'].present = 0 # Reset
|
|
self.assertEqual(FLAGS['q'].present, 1)
|
|
FLAGS['q'].present = 0 # Reset
|
|
self.assertEqual(FLAGS['x'].present, 1)
|
|
FLAGS['x'].present = 0 # Reset
|
|
|
|
# Flags list.
|
|
self.assertLen(FLAGS, number_defined_flags + number_test_framework_flags)
|
|
self.assertIn('name', FLAGS)
|
|
self.assertIn('debug', FLAGS)
|
|
self.assertIn('repeat', FLAGS)
|
|
self.assertIn('r', FLAGS)
|
|
self.assertIn('q', FLAGS)
|
|
self.assertIn('quack', FLAGS)
|
|
self.assertIn('x', FLAGS)
|
|
self.assertIn('l', FLAGS)
|
|
self.assertIn('args', FLAGS)
|
|
self.assertIn('letters', FLAGS)
|
|
self.assertIn('numbers', FLAGS)
|
|
|
|
# __contains__
|
|
self.assertIn('name', FLAGS)
|
|
self.assertNotIn('name2', FLAGS)
|
|
|
|
# try deleting a flag
|
|
del FLAGS.r
|
|
self.assertLen(FLAGS,
|
|
number_defined_flags - 1 + number_test_framework_flags)
|
|
self.assertNotIn('r', FLAGS)
|
|
|
|
# .. command line with extra stuff
|
|
argv = ('./program', '--debug', '--name=Bob', 'extra')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
self.assertEqual(FLAGS['debug'].present, 1)
|
|
FLAGS['debug'].present = 0 # Reset
|
|
self.assertEqual(FLAGS['name'].present, 1)
|
|
FLAGS['name'].present = 0 # Reset
|
|
|
|
# Test reset
|
|
argv = ('./program', '--debug')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['debug'].present, 1)
|
|
self.assertTrue(FLAGS['debug'].value)
|
|
FLAGS.unparse_flags()
|
|
self.assertEqual(FLAGS['debug'].present, 0)
|
|
self.assertFalse(FLAGS['debug'].value)
|
|
|
|
# Test that reset restores default value when default value is None.
|
|
argv = ('./program', '--kwery=who')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['kwery'].present, 1)
|
|
self.assertEqual(FLAGS['kwery'].value, 'who')
|
|
FLAGS.unparse_flags()
|
|
argv = ('./program', '--kwery=Why')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['kwery'].present, 1)
|
|
self.assertEqual(FLAGS['kwery'].value, 'Why')
|
|
FLAGS.unparse_flags()
|
|
self.assertEqual(FLAGS['kwery'].present, 0)
|
|
self.assertIsNone(FLAGS['kwery'].value)
|
|
|
|
# Test case sensitive enum.
|
|
argv = ('./program', '--sense=CASE')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['sense'].present, 1)
|
|
self.assertEqual(FLAGS['sense'].value, 'CASE')
|
|
FLAGS.unparse_flags()
|
|
argv = ('./program', '--sense=Case')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['sense'].present, 1)
|
|
self.assertEqual(FLAGS['sense'].value, 'Case')
|
|
FLAGS.unparse_flags()
|
|
|
|
# Test case insensitive enum.
|
|
argv = ('./program', '--cases=upper')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['cases'].present, 1)
|
|
self.assertEqual(FLAGS['cases'].value, 'UPPER')
|
|
FLAGS.unparse_flags()
|
|
|
|
# Test case sensitive enum with duplicates.
|
|
argv = ('./program', '--funny=ha')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['funny'].present, 1)
|
|
self.assertEqual(FLAGS['funny'].value, 'ha')
|
|
FLAGS.unparse_flags()
|
|
|
|
# Test case insensitive enum with duplicates.
|
|
argv = ('./program', '--blah=bLah')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['blah'].present, 1)
|
|
self.assertEqual(FLAGS['blah'].value, 'Blah')
|
|
FLAGS.unparse_flags()
|
|
argv = ('./program', '--blah=BLAH')
|
|
argv = FLAGS(argv)
|
|
self.assertLen(argv, 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(FLAGS['blah'].present, 1)
|
|
self.assertEqual(FLAGS['blah'].value, 'Blah')
|
|
FLAGS.unparse_flags()
|
|
|
|
# Test integer argument passing
|
|
argv = ('./program', '--x', '0x12345')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.x, 0x12345)
|
|
self.assertEqual(type(FLAGS.x), int)
|
|
|
|
argv = ('./program', '--x', '0x1234567890ABCDEF1234567890ABCDEF')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.x, 0x1234567890ABCDEF1234567890ABCDEF)
|
|
self.assertIsInstance(FLAGS.x, int)
|
|
|
|
argv = ('./program', '--x', '0o12345')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.x, 0o12345)
|
|
self.assertEqual(type(FLAGS.x), int)
|
|
|
|
# Treat 0-prefixed parameters as base-10, not base-8
|
|
argv = ('./program', '--x', '012345')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.x, 12345)
|
|
self.assertEqual(type(FLAGS.x), int)
|
|
|
|
argv = ('./program', '--x', '0123459')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.x, 123459)
|
|
self.assertEqual(type(FLAGS.x), int)
|
|
|
|
argv = ('./program', '--x', '0x123efg')
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = FLAGS(argv)
|
|
|
|
# Test boolean argument parsing
|
|
flags.DEFINE_boolean('test0', None, 'test boolean parsing')
|
|
argv = ('./program', '--notest0')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test0, 0)
|
|
|
|
flags.DEFINE_boolean('test1', None, 'test boolean parsing')
|
|
argv = ('./program', '--test1')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test1, 1)
|
|
|
|
FLAGS.test0 = None
|
|
argv = ('./program', '--test0=false')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test0, 0)
|
|
|
|
FLAGS.test1 = None
|
|
argv = ('./program', '--test1=true')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test1, 1)
|
|
|
|
FLAGS.test0 = None
|
|
argv = ('./program', '--test0=0')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test0, 0)
|
|
|
|
FLAGS.test1 = None
|
|
argv = ('./program', '--test1=1')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.test1, 1)
|
|
|
|
# Test booleans that already have 'no' as a prefix
|
|
FLAGS.noexec = None
|
|
argv = ('./program', '--nonoexec', '--name', 'Bob')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.noexec, 0)
|
|
|
|
FLAGS.noexec = None
|
|
argv = ('./program', '--name', 'Bob', '--noexec')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.noexec, 1)
|
|
|
|
# Test unassigned booleans
|
|
flags.DEFINE_boolean('testnone', None, 'test boolean parsing')
|
|
argv = ('./program',)
|
|
argv = FLAGS(argv)
|
|
self.assertIsNone(FLAGS.testnone)
|
|
|
|
# Test get with default
|
|
flags.DEFINE_boolean('testget1', None, 'test parsing with defaults')
|
|
flags.DEFINE_boolean('testget2', None, 'test parsing with defaults')
|
|
flags.DEFINE_boolean('testget3', None, 'test parsing with defaults')
|
|
flags.DEFINE_integer('testget4', None, 'test parsing with defaults')
|
|
argv = ('./program', '--testget1', '--notestget2')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('testget1', 'foo'), 1)
|
|
self.assertEqual(FLAGS.get_flag_value('testget2', 'foo'), 0)
|
|
self.assertEqual(FLAGS.get_flag_value('testget3', 'foo'), 'foo')
|
|
self.assertEqual(FLAGS.get_flag_value('testget4', 'foo'), 'foo')
|
|
|
|
# test list code
|
|
lists = [['hello', 'moo', 'boo', '1'], []]
|
|
|
|
flags.DEFINE_list('testcomma_list', '', 'test comma list parsing')
|
|
flags.DEFINE_spaceseplist('testspace_list', '', 'tests space list parsing')
|
|
flags.DEFINE_spaceseplist(
|
|
'testspace_or_comma_list',
|
|
'',
|
|
'tests space list parsing with comma compatibility',
|
|
comma_compat=True)
|
|
|
|
for name, sep in (('testcomma_list', ','), ('testspace_list',
|
|
' '), ('testspace_list', '\n'),
|
|
('testspace_or_comma_list',
|
|
' '), ('testspace_or_comma_list',
|
|
'\n'), ('testspace_or_comma_list', ',')):
|
|
for lst in lists:
|
|
argv = ('./program', '--%s=%s' % (name, sep.join(lst)))
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(getattr(FLAGS, name), lst)
|
|
|
|
# Test help text
|
|
flags_help = str(FLAGS)
|
|
self.assertNotEqual(
|
|
flags_help.find('repeat'), -1, 'cannot find flag in help')
|
|
self.assertNotEqual(
|
|
flags_help.find(repeat_help), -1, 'cannot find help string in help')
|
|
|
|
# Test flag specified twice
|
|
argv = ('./program', '--repeat=4', '--repeat=2', '--debug', '--nodebug')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('repeat', None), 2)
|
|
self.assertEqual(FLAGS.get_flag_value('debug', None), 0)
|
|
|
|
# Test MultiFlag with single default value
|
|
flags.DEFINE_multi_string(
|
|
's_str',
|
|
'sing1',
|
|
'string option that can occur multiple times',
|
|
short_name='s')
|
|
self.assertEqual(FLAGS.get_flag_value('s_str', None), ['sing1'])
|
|
|
|
# Test MultiFlag with list of default values
|
|
multi_string_defs = ['def1', 'def2']
|
|
flags.DEFINE_multi_string(
|
|
'm_str',
|
|
multi_string_defs,
|
|
'string option that can occur multiple times',
|
|
short_name='m')
|
|
self.assertEqual(FLAGS.get_flag_value('m_str', None), multi_string_defs)
|
|
|
|
# Test flag specified multiple times with a MultiFlag
|
|
argv = ('./program', '--m_str=str1', '-m', 'str2')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('m_str', None), ['str1', 'str2'])
|
|
|
|
# A flag with allow_overwrite set to False should behave normally when it
|
|
# is only specified once
|
|
argv = ('./program', '--only_once=singlevalue')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('only_once', None), 'singlevalue')
|
|
|
|
# A flag with allow_overwrite set to False should complain when it is
|
|
# specified more than once
|
|
argv = ('./program', '--universe=ptolemaic', '--universe=copernicean',
|
|
'--universe=euclidean')
|
|
self.assertRaisesWithLiteralMatch(
|
|
flags.IllegalFlagValueError,
|
|
'flag --universe=copernicean: already defined as ptolemaic', FLAGS,
|
|
argv)
|
|
|
|
# Test single-letter flags; should support both single and double dash
|
|
argv = ('./program', '-q')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('q', None), 1)
|
|
|
|
argv = ('./program', '--q', '--x', '9', '--noquack')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('q', None), 1)
|
|
self.assertEqual(FLAGS.get_flag_value('x', None), 9)
|
|
self.assertEqual(FLAGS.get_flag_value('quack', None), 0)
|
|
|
|
argv = ('./program', '--noq', '--x=10', '--quack')
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('q', None), 0)
|
|
self.assertEqual(FLAGS.get_flag_value('x', None), 10)
|
|
self.assertEqual(FLAGS.get_flag_value('quack', None), 1)
|
|
|
|
####################################
|
|
# Test flag serialization code:
|
|
|
|
old_testcomma_list = FLAGS.testcomma_list
|
|
old_testspace_list = FLAGS.testspace_list
|
|
old_testspace_or_comma_list = FLAGS.testspace_or_comma_list
|
|
|
|
argv = ('./program', FLAGS['test0'].serialize(), FLAGS['test1'].serialize(),
|
|
FLAGS['s_str'].serialize())
|
|
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS['test0'].serialize(), '--notest0')
|
|
self.assertEqual(FLAGS['test1'].serialize(), '--test1')
|
|
self.assertEqual(FLAGS['s_str'].serialize(), '--s_str=sing1')
|
|
|
|
self.assertEqual(FLAGS['testnone'].serialize(), '')
|
|
|
|
testcomma_list1 = ['aa', 'bb']
|
|
testspace_list1 = ['aa', 'bb', 'cc']
|
|
testspace_or_comma_list1 = ['aa', 'bb', 'cc', 'dd']
|
|
FLAGS.testcomma_list = list(testcomma_list1)
|
|
FLAGS.testspace_list = list(testspace_list1)
|
|
FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1)
|
|
argv = ('./program', FLAGS['testcomma_list'].serialize(),
|
|
FLAGS['testspace_list'].serialize(),
|
|
FLAGS['testspace_or_comma_list'].serialize())
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.testcomma_list, testcomma_list1)
|
|
self.assertEqual(FLAGS.testspace_list, testspace_list1)
|
|
self.assertEqual(FLAGS.testspace_or_comma_list, testspace_or_comma_list1)
|
|
|
|
testcomma_list1 = ['aa some spaces', 'bb']
|
|
testspace_list1 = ['aa', 'bb,some,commas,', 'cc']
|
|
testspace_or_comma_list1 = ['aa', 'bb,some,commas,', 'cc']
|
|
FLAGS.testcomma_list = list(testcomma_list1)
|
|
FLAGS.testspace_list = list(testspace_list1)
|
|
FLAGS.testspace_or_comma_list = list(testspace_or_comma_list1)
|
|
argv = ('./program', FLAGS['testcomma_list'].serialize(),
|
|
FLAGS['testspace_list'].serialize(),
|
|
FLAGS['testspace_or_comma_list'].serialize())
|
|
argv = FLAGS(argv)
|
|
self.assertEqual(FLAGS.testcomma_list, testcomma_list1)
|
|
self.assertEqual(FLAGS.testspace_list, testspace_list1)
|
|
# We don't expect idempotency when commas are placed in an item value and
|
|
# comma_compat is enabled.
|
|
self.assertEqual(FLAGS.testspace_or_comma_list,
|
|
['aa', 'bb', 'some', 'commas', 'cc'])
|
|
|
|
FLAGS.testcomma_list = old_testcomma_list
|
|
FLAGS.testspace_list = old_testspace_list
|
|
FLAGS.testspace_or_comma_list = old_testspace_or_comma_list
|
|
|
|
####################################
|
|
# Test flag-update:
|
|
|
|
def args_list():
|
|
# Exclude flags that have different default values based on the
|
|
# environment.
|
|
flags_to_exclude = {'log_dir', 'test_srcdir', 'test_tmpdir'}
|
|
flagnames = set(FLAGS) - flags_to_exclude
|
|
|
|
nonbool_flags = []
|
|
truebool_flags = []
|
|
falsebool_flags = []
|
|
for name in flagnames:
|
|
flag_value = FLAGS.get_flag_value(name, None)
|
|
if not isinstance(FLAGS[name], flags.BooleanFlag):
|
|
nonbool_flags.append('--%s %s' % (name, flag_value))
|
|
elif flag_value:
|
|
truebool_flags.append('--%s' % name)
|
|
else:
|
|
falsebool_flags.append('--no%s' % name)
|
|
all_flags = nonbool_flags + truebool_flags + falsebool_flags
|
|
all_flags.sort()
|
|
return all_flags
|
|
|
|
argv = ('./program', '--repeat=3', '--name=giants', '--nodebug')
|
|
|
|
FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('repeat', None), 3)
|
|
self.assertEqual(FLAGS.get_flag_value('name', None), 'giants')
|
|
self.assertEqual(FLAGS.get_flag_value('debug', None), 0)
|
|
self.assertListEqual([
|
|
'--alsologtostderr',
|
|
"--args ['v=1', 'vmodule=a=0,b=2']",
|
|
'--blah None',
|
|
'--cases None',
|
|
'--decimal 666',
|
|
'--float 3.14',
|
|
'--funny None',
|
|
'--hexadecimal 1638',
|
|
'--kwery None',
|
|
'--l 9223372032559808512',
|
|
"--letters ['a', 'b', 'c']",
|
|
'--logger_levels {}',
|
|
"--m ['str1', 'str2']",
|
|
"--m_str ['str1', 'str2']",
|
|
'--name giants',
|
|
'--no?',
|
|
'--nodebug',
|
|
'--noexec',
|
|
'--nohelp',
|
|
'--nohelpfull',
|
|
'--nohelpshort',
|
|
'--nohelpxml',
|
|
'--nologtostderr',
|
|
'--noonly_check_args',
|
|
'--nopdb_post_mortem',
|
|
'--noq',
|
|
'--norun_with_pdb',
|
|
'--norun_with_profiling',
|
|
'--notest0',
|
|
'--notestget2',
|
|
'--notestget3',
|
|
'--notestnone',
|
|
'--numbers [1, 2, 3]',
|
|
'--octal 438',
|
|
'--only_once singlevalue',
|
|
'--pdb False',
|
|
'--profile_file None',
|
|
'--quack',
|
|
'--repeat 3',
|
|
"--s ['sing1']",
|
|
"--s_str ['sing1']",
|
|
'--sense None',
|
|
'--showprefixforinfo',
|
|
'--stderrthreshold fatal',
|
|
'--test1',
|
|
'--test_random_seed 301',
|
|
'--test_randomize_ordering_seed ',
|
|
'--testcomma_list []',
|
|
'--testget1',
|
|
'--testget4 None',
|
|
'--testspace_list []',
|
|
'--testspace_or_comma_list []',
|
|
'--tmod_baz_x',
|
|
'--universe ptolemaic',
|
|
'--use_cprofile_for_profiling',
|
|
'--v -1',
|
|
'--verbosity -1',
|
|
'--x 10',
|
|
'--xml_output_file ',
|
|
], args_list())
|
|
|
|
argv = ('./program', '--debug', '--m_str=upd1', '-s', 'upd2')
|
|
FLAGS(argv)
|
|
self.assertEqual(FLAGS.get_flag_value('repeat', None), 3)
|
|
self.assertEqual(FLAGS.get_flag_value('name', None), 'giants')
|
|
self.assertEqual(FLAGS.get_flag_value('debug', None), 1)
|
|
|
|
# items appended to existing non-default value lists for --m/--m_str
|
|
# new value overwrites default value (not appended to it) for --s/--s_str
|
|
self.assertListEqual([
|
|
'--alsologtostderr',
|
|
"--args ['v=1', 'vmodule=a=0,b=2']",
|
|
'--blah None',
|
|
'--cases None',
|
|
'--debug',
|
|
'--decimal 666',
|
|
'--float 3.14',
|
|
'--funny None',
|
|
'--hexadecimal 1638',
|
|
'--kwery None',
|
|
'--l 9223372032559808512',
|
|
"--letters ['a', 'b', 'c']",
|
|
'--logger_levels {}',
|
|
"--m ['str1', 'str2', 'upd1']",
|
|
"--m_str ['str1', 'str2', 'upd1']",
|
|
'--name giants',
|
|
'--no?',
|
|
'--noexec',
|
|
'--nohelp',
|
|
'--nohelpfull',
|
|
'--nohelpshort',
|
|
'--nohelpxml',
|
|
'--nologtostderr',
|
|
'--noonly_check_args',
|
|
'--nopdb_post_mortem',
|
|
'--noq',
|
|
'--norun_with_pdb',
|
|
'--norun_with_profiling',
|
|
'--notest0',
|
|
'--notestget2',
|
|
'--notestget3',
|
|
'--notestnone',
|
|
'--numbers [1, 2, 3]',
|
|
'--octal 438',
|
|
'--only_once singlevalue',
|
|
'--pdb False',
|
|
'--profile_file None',
|
|
'--quack',
|
|
'--repeat 3',
|
|
"--s ['sing1', 'upd2']",
|
|
"--s_str ['sing1', 'upd2']",
|
|
'--sense None',
|
|
'--showprefixforinfo',
|
|
'--stderrthreshold fatal',
|
|
'--test1',
|
|
'--test_random_seed 301',
|
|
'--test_randomize_ordering_seed ',
|
|
'--testcomma_list []',
|
|
'--testget1',
|
|
'--testget4 None',
|
|
'--testspace_list []',
|
|
'--testspace_or_comma_list []',
|
|
'--tmod_baz_x',
|
|
'--universe ptolemaic',
|
|
'--use_cprofile_for_profiling',
|
|
'--v -1',
|
|
'--verbosity -1',
|
|
'--x 10',
|
|
'--xml_output_file ',
|
|
], args_list())
|
|
|
|
####################################
|
|
# Test all kind of error conditions.
|
|
|
|
# Argument not in enum exception
|
|
argv = ('./program', '--kwery=WHEN')
|
|
self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv)
|
|
argv = ('./program', '--kwery=why')
|
|
self.assertRaises(flags.IllegalFlagValueError, FLAGS, argv)
|
|
|
|
# Duplicate flag detection
|
|
with self.assertRaises(flags.DuplicateFlagError):
|
|
flags.DEFINE_boolean('run', 0, 'runhelp', short_name='q')
|
|
|
|
# Duplicate short flag detection
|
|
with self.assertRaisesRegex(
|
|
flags.DuplicateFlagError,
|
|
r"The flag 'z' is defined twice\. .*First from.*, Second from"):
|
|
flags.DEFINE_boolean('zoom1', 0, 'runhelp z1', short_name='z')
|
|
flags.DEFINE_boolean('zoom2', 0, 'runhelp z2', short_name='z')
|
|
raise AssertionError('duplicate short flag detection failed')
|
|
|
|
# Duplicate mixed flag detection
|
|
with self.assertRaisesRegex(
|
|
flags.DuplicateFlagError,
|
|
r"The flag 's' is defined twice\. .*First from.*, Second from"):
|
|
flags.DEFINE_boolean('short1', 0, 'runhelp s1', short_name='s')
|
|
flags.DEFINE_boolean('s', 0, 'runhelp s2')
|
|
|
|
# Check that duplicate flag detection detects definition sites
|
|
# correctly.
|
|
flagnames = ['repeated']
|
|
original_flags = flags.FlagValues()
|
|
flags.DEFINE_boolean(
|
|
flagnames[0],
|
|
False,
|
|
'Flag about to be repeated.',
|
|
flag_values=original_flags)
|
|
duplicate_flags = module_foo.duplicate_flags(flagnames)
|
|
with self.assertRaisesRegex(flags.DuplicateFlagError,
|
|
'flags_test.*module_foo'):
|
|
original_flags.append_flag_values(duplicate_flags)
|
|
|
|
# Make sure allow_override works
|
|
try:
|
|
flags.DEFINE_boolean(
|
|
'dup1', 0, 'runhelp d11', short_name='u', allow_override=0)
|
|
flag = FLAGS._flags()['dup1']
|
|
self.assertEqual(flag.default, 0)
|
|
|
|
flags.DEFINE_boolean(
|
|
'dup1', 1, 'runhelp d12', short_name='u', allow_override=1)
|
|
flag = FLAGS._flags()['dup1']
|
|
self.assertEqual(flag.default, 1)
|
|
except flags.DuplicateFlagError:
|
|
raise AssertionError('allow_override did not permit a flag duplication')
|
|
|
|
# Make sure allow_override works
|
|
try:
|
|
flags.DEFINE_boolean(
|
|
'dup2', 0, 'runhelp d21', short_name='u', allow_override=1)
|
|
flag = FLAGS._flags()['dup2']
|
|
self.assertEqual(flag.default, 0)
|
|
|
|
flags.DEFINE_boolean(
|
|
'dup2', 1, 'runhelp d22', short_name='u', allow_override=0)
|
|
flag = FLAGS._flags()['dup2']
|
|
self.assertEqual(flag.default, 1)
|
|
except flags.DuplicateFlagError:
|
|
raise AssertionError('allow_override did not permit a flag duplication')
|
|
|
|
# Make sure that re-importing a module does not cause a DuplicateFlagError
|
|
# to be raised.
|
|
try:
|
|
sys.modules.pop('absl.flags.tests.module_baz')
|
|
import absl.flags.tests.module_baz
|
|
del absl
|
|
except flags.DuplicateFlagError:
|
|
raise AssertionError('Module reimport caused flag duplication error')
|
|
|
|
# Make sure that when we override, the help string gets updated correctly
|
|
flags.DEFINE_boolean(
|
|
'dup3', 0, 'runhelp d31', short_name='u', allow_override=1)
|
|
flags.DEFINE_boolean(
|
|
'dup3', 1, 'runhelp d32', short_name='u', allow_override=1)
|
|
self.assertEqual(str(FLAGS).find('runhelp d31'), -1)
|
|
self.assertNotEqual(str(FLAGS).find('runhelp d32'), -1)
|
|
|
|
# Make sure append_flag_values works
|
|
new_flags = flags.FlagValues()
|
|
flags.DEFINE_boolean('new1', 0, 'runhelp n1', flag_values=new_flags)
|
|
flags.DEFINE_boolean('new2', 0, 'runhelp n2', flag_values=new_flags)
|
|
self.assertEqual(len(new_flags._flags()), 2)
|
|
old_len = len(FLAGS._flags())
|
|
FLAGS.append_flag_values(new_flags)
|
|
self.assertEqual(len(FLAGS._flags()) - old_len, 2)
|
|
self.assertEqual('new1' in FLAGS._flags(), True)
|
|
self.assertEqual('new2' in FLAGS._flags(), True)
|
|
|
|
# Then test that removing those flags works
|
|
FLAGS.remove_flag_values(new_flags)
|
|
self.assertEqual(len(FLAGS._flags()), old_len)
|
|
self.assertFalse('new1' in FLAGS._flags())
|
|
self.assertFalse('new2' in FLAGS._flags())
|
|
|
|
# Make sure append_flag_values works with flags with shortnames.
|
|
new_flags = flags.FlagValues()
|
|
flags.DEFINE_boolean('new3', 0, 'runhelp n3', flag_values=new_flags)
|
|
flags.DEFINE_boolean(
|
|
'new4', 0, 'runhelp n4', flag_values=new_flags, short_name='n4')
|
|
self.assertEqual(len(new_flags._flags()), 3)
|
|
old_len = len(FLAGS._flags())
|
|
FLAGS.append_flag_values(new_flags)
|
|
self.assertEqual(len(FLAGS._flags()) - old_len, 3)
|
|
self.assertIn('new3', FLAGS._flags())
|
|
self.assertIn('new4', FLAGS._flags())
|
|
self.assertIn('n4', FLAGS._flags())
|
|
self.assertEqual(FLAGS._flags()['n4'], FLAGS._flags()['new4'])
|
|
|
|
# Then test removing them
|
|
FLAGS.remove_flag_values(new_flags)
|
|
self.assertEqual(len(FLAGS._flags()), old_len)
|
|
self.assertFalse('new3' in FLAGS._flags())
|
|
self.assertFalse('new4' in FLAGS._flags())
|
|
self.assertFalse('n4' in FLAGS._flags())
|
|
|
|
# Make sure append_flag_values fails on duplicates
|
|
flags.DEFINE_boolean('dup4', 0, 'runhelp d41')
|
|
new_flags = flags.FlagValues()
|
|
flags.DEFINE_boolean('dup4', 0, 'runhelp d42', flag_values=new_flags)
|
|
with self.assertRaises(flags.DuplicateFlagError):
|
|
FLAGS.append_flag_values(new_flags)
|
|
|
|
# Integer out of bounds
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = ('./program', '--repeat=-4')
|
|
FLAGS(argv)
|
|
|
|
# Non-integer
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = ('./program', '--repeat=2.5')
|
|
FLAGS(argv)
|
|
|
|
# Missing required argument
|
|
with self.assertRaises(flags.Error):
|
|
argv = ('./program', '--name')
|
|
FLAGS(argv)
|
|
|
|
# Non-boolean arguments for boolean
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = ('./program', '--debug=goofup')
|
|
FLAGS(argv)
|
|
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = ('./program', '--debug=42')
|
|
FLAGS(argv)
|
|
|
|
# Non-numeric argument for integer flag --repeat
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
argv = ('./program', '--repeat', 'Bob', 'extra')
|
|
FLAGS(argv)
|
|
|
|
# Aliases of existing flags
|
|
with self.assertRaises(flags.UnrecognizedFlagError):
|
|
flags.DEFINE_alias('alias_not_a_flag', 'not_a_flag')
|
|
|
|
# Programmtically modify alias and aliased flag
|
|
flags.DEFINE_alias('alias_octal', 'octal')
|
|
FLAGS.octal = 0o2222
|
|
self.assertEqual(0o2222, FLAGS.octal)
|
|
self.assertEqual(0o2222, FLAGS.alias_octal)
|
|
FLAGS.alias_octal = 0o4444
|
|
self.assertEqual(0o4444, FLAGS.octal)
|
|
self.assertEqual(0o4444, FLAGS.alias_octal)
|
|
|
|
# Setting alias preserves the default of the original
|
|
flags.DEFINE_alias('alias_name', 'name')
|
|
flags.DEFINE_alias('alias_debug', 'debug')
|
|
flags.DEFINE_alias('alias_decimal', 'decimal')
|
|
flags.DEFINE_alias('alias_float', 'float')
|
|
flags.DEFINE_alias('alias_letters', 'letters')
|
|
self.assertEqual(FLAGS['name'].default, FLAGS.alias_name)
|
|
self.assertEqual(FLAGS['debug'].default, FLAGS.alias_debug)
|
|
self.assertEqual(int(FLAGS['decimal'].default), FLAGS.alias_decimal)
|
|
self.assertEqual(float(FLAGS['float'].default), FLAGS.alias_float)
|
|
self.assertSameElements(FLAGS['letters'].default, FLAGS.alias_letters)
|
|
|
|
# Original flags set on command line
|
|
argv = ('./program', '--name=Martin', '--debug=True', '--decimal=777',
|
|
'--letters=x,y,z')
|
|
FLAGS(argv)
|
|
self.assertEqual('Martin', FLAGS.name)
|
|
self.assertEqual('Martin', FLAGS.alias_name)
|
|
self.assertTrue(FLAGS.debug)
|
|
self.assertTrue(FLAGS.alias_debug)
|
|
self.assertEqual(777, FLAGS.decimal)
|
|
self.assertEqual(777, FLAGS.alias_decimal)
|
|
self.assertSameElements(['x', 'y', 'z'], FLAGS.letters)
|
|
self.assertSameElements(['x', 'y', 'z'], FLAGS.alias_letters)
|
|
|
|
# Alias flags set on command line
|
|
argv = ('./program', '--alias_name=Auston', '--alias_debug=False',
|
|
'--alias_decimal=888', '--alias_letters=l,m,n')
|
|
FLAGS(argv)
|
|
self.assertEqual('Auston', FLAGS.name)
|
|
self.assertEqual('Auston', FLAGS.alias_name)
|
|
self.assertFalse(FLAGS.debug)
|
|
self.assertFalse(FLAGS.alias_debug)
|
|
self.assertEqual(888, FLAGS.decimal)
|
|
self.assertEqual(888, FLAGS.alias_decimal)
|
|
self.assertSameElements(['l', 'm', 'n'], FLAGS.letters)
|
|
self.assertSameElements(['l', 'm', 'n'], FLAGS.alias_letters)
|
|
|
|
# Make sure importing a module does not change flag value parsed
|
|
# from commandline.
|
|
flags.DEFINE_integer(
|
|
'dup5', 1, 'runhelp d51', short_name='u5', allow_override=0)
|
|
self.assertEqual(FLAGS.dup5, 1)
|
|
self.assertEqual(FLAGS.dup5, 1)
|
|
argv = ('./program', '--dup5=3')
|
|
FLAGS(argv)
|
|
self.assertEqual(FLAGS.dup5, 3)
|
|
flags.DEFINE_integer(
|
|
'dup5', 2, 'runhelp d52', short_name='u5', allow_override=1)
|
|
self.assertEqual(FLAGS.dup5, 3)
|
|
|
|
# Make sure importing a module does not change user defined flag value.
|
|
flags.DEFINE_integer(
|
|
'dup6', 1, 'runhelp d61', short_name='u6', allow_override=0)
|
|
self.assertEqual(FLAGS.dup6, 1)
|
|
FLAGS.dup6 = 3
|
|
self.assertEqual(FLAGS.dup6, 3)
|
|
flags.DEFINE_integer(
|
|
'dup6', 2, 'runhelp d62', short_name='u6', allow_override=1)
|
|
self.assertEqual(FLAGS.dup6, 3)
|
|
|
|
# Make sure importing a module does not change user defined flag value
|
|
# even if it is the 'default' value.
|
|
flags.DEFINE_integer(
|
|
'dup7', 1, 'runhelp d71', short_name='u7', allow_override=0)
|
|
self.assertEqual(FLAGS.dup7, 1)
|
|
FLAGS.dup7 = 1
|
|
self.assertEqual(FLAGS.dup7, 1)
|
|
flags.DEFINE_integer(
|
|
'dup7', 2, 'runhelp d72', short_name='u7', allow_override=1)
|
|
self.assertEqual(FLAGS.dup7, 1)
|
|
|
|
# Test module_help().
|
|
helpstr = FLAGS.module_help(module_baz)
|
|
|
|
expected_help = '\n' + module_baz.__name__ + ':' + """
|
|
--[no]tmod_baz_x: Boolean flag.
|
|
(default: 'true')"""
|
|
|
|
self.assertMultiLineEqual(expected_help, helpstr)
|
|
|
|
# Test main_module_help(). This must be part of test_flags because
|
|
# it depends on dup1/2/3/etc being introduced first.
|
|
helpstr = FLAGS.main_module_help()
|
|
|
|
expected_help = '\n' + sys.argv[0] + ':' + """
|
|
--[no]alias_debug: Alias for --debug.
|
|
(default: 'false')
|
|
--alias_decimal: Alias for --decimal.
|
|
(default: '666')
|
|
(an integer)
|
|
--alias_float: Alias for --float.
|
|
(default: '3.14')
|
|
(a number)
|
|
--alias_letters: Alias for --letters.
|
|
(default: 'a,b,c')
|
|
(a comma separated list)
|
|
--alias_name: Alias for --name.
|
|
(default: 'Bob')
|
|
--alias_octal: Alias for --octal.
|
|
(default: '438')
|
|
(an integer)
|
|
--args: a list of arguments
|
|
(default: 'v=1,"vmodule=a=0,b=2"')
|
|
(a comma separated list)
|
|
--blah: <bla|Blah|BLAH|blah>: ?
|
|
--cases: <UPPER|lower|Initial|Ot_HeR>: ?
|
|
--[no]debug: debughelp
|
|
(default: 'false')
|
|
--decimal: using decimals
|
|
(default: '666')
|
|
(an integer)
|
|
-u,--[no]dup1: runhelp d12
|
|
(default: 'true')
|
|
-u,--[no]dup2: runhelp d22
|
|
(default: 'true')
|
|
-u,--[no]dup3: runhelp d32
|
|
(default: 'true')
|
|
--[no]dup4: runhelp d41
|
|
(default: 'false')
|
|
-u5,--dup5: runhelp d51
|
|
(default: '1')
|
|
(an integer)
|
|
-u6,--dup6: runhelp d61
|
|
(default: '1')
|
|
(an integer)
|
|
-u7,--dup7: runhelp d71
|
|
(default: '1')
|
|
(an integer)
|
|
--float: using floats
|
|
(default: '3.14')
|
|
(a number)
|
|
--funny: <Joke|ha|ha|ha|ha>: ?
|
|
--hexadecimal: using hexadecimals
|
|
(default: '1638')
|
|
(an integer)
|
|
--kwery: <who|what|Why|where|when>: ?
|
|
--l: how long to be
|
|
(default: '9223372032559808512')
|
|
(an integer)
|
|
--letters: a list of letters
|
|
(default: 'a,b,c')
|
|
(a comma separated list)
|
|
-m,--m_str: string option that can occur multiple times;
|
|
repeat this option to specify a list of values
|
|
(default: "['def1', 'def2']")
|
|
--name: namehelp
|
|
(default: 'Bob')
|
|
--[no]noexec: boolean flag with no as prefix
|
|
(default: 'true')
|
|
--numbers: a list of numbers
|
|
(default: '1,2,3')
|
|
(a comma separated list)
|
|
--octal: using octals
|
|
(default: '438')
|
|
(an integer)
|
|
--only_once: test only sets this once
|
|
--[no]q: quiet mode
|
|
(default: 'true')
|
|
--[no]quack: superstring of 'q'
|
|
(default: 'false')
|
|
-r,--repeat: how many times to repeat (0-5)
|
|
(default: '4')
|
|
(a non-negative integer)
|
|
-s,--s_str: string option that can occur multiple times;
|
|
repeat this option to specify a list of values
|
|
(default: "['sing1']")
|
|
--sense: <Case|case|CASE>: ?
|
|
--[no]test0: test boolean parsing
|
|
--[no]test1: test boolean parsing
|
|
--testcomma_list: test comma list parsing
|
|
(default: '')
|
|
(a comma separated list)
|
|
--[no]testget1: test parsing with defaults
|
|
--[no]testget2: test parsing with defaults
|
|
--[no]testget3: test parsing with defaults
|
|
--testget4: test parsing with defaults
|
|
(an integer)
|
|
--[no]testnone: test boolean parsing
|
|
--testspace_list: tests space list parsing
|
|
(default: '')
|
|
(a whitespace separated list)
|
|
--testspace_or_comma_list: tests space list parsing with comma compatibility
|
|
(default: '')
|
|
(a whitespace or comma separated list)
|
|
--universe: test tries to set this three times
|
|
--x: how eXtreme to be
|
|
(default: '3')
|
|
(an integer)
|
|
-z,--[no]zoom1: runhelp z1
|
|
(default: 'false')"""
|
|
|
|
self.assertMultiLineEqual(expected_help, helpstr)
|
|
|
|
def test_string_flag_with_wrong_type(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_string('name', False, 'help', flag_values=fv)
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_string('name2', 0, 'help', flag_values=fv)
|
|
|
|
def test_integer_flag_with_wrong_type(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_integer('name', 1e2, 'help', flag_values=fv)
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_integer('name', [], 'help', flag_values=fv)
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_integer('name', False, 'help', flag_values=fv)
|
|
|
|
def test_float_flag_with_wrong_type(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.DEFINE_float('name', False, 'help', flag_values=fv)
|
|
|
|
def test_enum_flag_with_empty_values(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(ValueError):
|
|
flags.DEFINE_enum('fruit', None, [], 'help', flag_values=fv)
|
|
|
|
def test_define_enum_class_flag(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertIsNone(fv.fruit)
|
|
|
|
def test_parse_enum_class_flag(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
|
|
|
|
argv = ('./program', '--fruit=orange')
|
|
argv = fv(argv)
|
|
self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(fv['fruit'].present, 1)
|
|
self.assertEqual(fv['fruit'].value, Fruit.ORANGE)
|
|
fv.unparse_flags()
|
|
argv = ('./program', '--fruit=APPLE')
|
|
argv = fv(argv)
|
|
self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(fv['fruit'].present, 1)
|
|
self.assertEqual(fv['fruit'].value, Fruit.APPLE)
|
|
fv.unparse_flags()
|
|
|
|
def test_enum_class_flag_help_message(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_enum_class('fruit', None, Fruit, '?', flag_values=fv)
|
|
|
|
helpstr = fv.main_module_help()
|
|
expected_help = '\n%s:\n --fruit: <apple|orange>: ?' % sys.argv[0]
|
|
|
|
self.assertEqual(helpstr, expected_help)
|
|
|
|
def test_enum_class_flag_with_wrong_default_value_type(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(_exceptions.IllegalFlagValueError):
|
|
flags.DEFINE_enum_class('fruit', 1, Fruit, 'help', flag_values=fv)
|
|
|
|
def test_enum_class_flag_requires_enum_class(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(TypeError):
|
|
flags.DEFINE_enum_class(
|
|
'fruit', None, ['apple', 'orange'], 'help', flag_values=fv)
|
|
|
|
def test_enum_class_flag_requires_non_empty_enum_class(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(ValueError):
|
|
flags.DEFINE_enum_class('empty', None, EmptyEnum, 'help', flag_values=fv)
|
|
|
|
def test_required_flag(self):
|
|
fv = flags.FlagValues()
|
|
fl = flags.DEFINE_integer(
|
|
name='int_flag',
|
|
default=None,
|
|
help='help',
|
|
required=True,
|
|
flag_values=fv)
|
|
# Since the flag is required, the FlagHolder should ensure value returned
|
|
# is not None.
|
|
self.assertTrue(fl._ensure_non_none_value)
|
|
|
|
def test_illegal_required_flag(self):
|
|
fv = flags.FlagValues()
|
|
with self.assertRaises(ValueError):
|
|
flags.DEFINE_integer(
|
|
name='int_flag',
|
|
default=3,
|
|
help='help',
|
|
required=True,
|
|
flag_values=fv)
|
|
|
|
|
|
class MultiNumericalFlagsTest(absltest.TestCase):
|
|
|
|
def test_multi_numerical_flags(self):
|
|
"""Test multi_int and multi_float flags."""
|
|
fv = flags.FlagValues()
|
|
int_defaults = [77, 88]
|
|
flags.DEFINE_multi_integer(
|
|
'm_int',
|
|
int_defaults,
|
|
'integer option that can occur multiple times',
|
|
short_name='mi',
|
|
flag_values=fv)
|
|
self.assertListEqual(fv['m_int'].default, int_defaults)
|
|
argv = ('./program', '--m_int=-99', '--mi=101')
|
|
fv(argv)
|
|
self.assertListEqual(fv.get_flag_value('m_int', None), [-99, 101])
|
|
|
|
float_defaults = [2.2, 3]
|
|
flags.DEFINE_multi_float(
|
|
'm_float',
|
|
float_defaults,
|
|
'float option that can occur multiple times',
|
|
short_name='mf',
|
|
flag_values=fv)
|
|
for (expected, actual) in zip(float_defaults,
|
|
fv.get_flag_value('m_float', None)):
|
|
self.assertAlmostEqual(expected, actual)
|
|
argv = ('./program', '--m_float=-17', '--mf=2.78e9')
|
|
fv(argv)
|
|
expected_floats = [-17.0, 2.78e9]
|
|
for (expected, actual) in zip(expected_floats,
|
|
fv.get_flag_value('m_float', None)):
|
|
self.assertAlmostEqual(expected, actual)
|
|
|
|
def test_multi_numerical_with_tuples(self):
|
|
"""Verify multi_int/float accept tuples as default values."""
|
|
flags.DEFINE_multi_integer(
|
|
'm_int_tuple', (77, 88),
|
|
'integer option that can occur multiple times',
|
|
short_name='mi_tuple')
|
|
self.assertListEqual(FLAGS.get_flag_value('m_int_tuple', None), [77, 88])
|
|
|
|
dict_with_float_keys = {2.2: 'hello', 3: 'happy'}
|
|
float_defaults = dict_with_float_keys.keys()
|
|
flags.DEFINE_multi_float(
|
|
'm_float_tuple',
|
|
float_defaults,
|
|
'float option that can occur multiple times',
|
|
short_name='mf_tuple')
|
|
for (expected, actual) in zip(float_defaults,
|
|
FLAGS.get_flag_value('m_float_tuple', None)):
|
|
self.assertAlmostEqual(expected, actual)
|
|
|
|
def test_single_value_default(self):
|
|
"""Test multi_int and multi_float flags with a single default value."""
|
|
int_default = 77
|
|
flags.DEFINE_multi_integer('m_int1', int_default,
|
|
'integer option that can occur multiple times')
|
|
self.assertListEqual(FLAGS.get_flag_value('m_int1', None), [int_default])
|
|
|
|
float_default = 2.2
|
|
flags.DEFINE_multi_float('m_float1', float_default,
|
|
'float option that can occur multiple times')
|
|
actual = FLAGS.get_flag_value('m_float1', None)
|
|
self.assertEqual(1, len(actual))
|
|
self.assertAlmostEqual(actual[0], float_default)
|
|
|
|
def test_bad_multi_numerical_flags(self):
|
|
"""Test multi_int and multi_float flags with non-parseable values."""
|
|
|
|
# Test non-parseable defaults.
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r"flag --m_int2=abc: invalid literal for int\(\) with base 10: 'abc'",
|
|
flags.DEFINE_multi_integer, 'm_int2', ['abc'], 'desc')
|
|
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError, r'flag --m_float2=abc: '
|
|
r'(invalid literal for float\(\)|could not convert string to float): '
|
|
r"'?abc'?", flags.DEFINE_multi_float, 'm_float2', ['abc'], 'desc')
|
|
|
|
# Test non-parseable command line values.
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_multi_integer(
|
|
'm_int2',
|
|
'77',
|
|
'integer option that can occur multiple times',
|
|
flag_values=fv)
|
|
argv = ('./program', '--m_int2=def')
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r"flag --m_int2=def: invalid literal for int\(\) with base 10: 'def'",
|
|
fv, argv)
|
|
|
|
flags.DEFINE_multi_float(
|
|
'm_float2',
|
|
2.2,
|
|
'float option that can occur multiple times',
|
|
flag_values=fv)
|
|
argv = ('./program', '--m_float2=def')
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError, r'flag --m_float2=def: '
|
|
r'(invalid literal for float\(\)|could not convert string to float): '
|
|
r"'?def'?", fv, argv)
|
|
|
|
|
|
class MultiEnumFlagsTest(absltest.TestCase):
|
|
|
|
def test_multi_enum_flags(self):
|
|
"""Test multi_enum flags."""
|
|
fv = flags.FlagValues()
|
|
|
|
enum_defaults = ['FOO', 'BAZ']
|
|
flags.DEFINE_multi_enum(
|
|
'm_enum',
|
|
enum_defaults, ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
|
|
'Enum option that can occur multiple times',
|
|
short_name='me',
|
|
flag_values=fv)
|
|
self.assertListEqual(fv['m_enum'].default, enum_defaults)
|
|
argv = ('./program', '--m_enum=WHOOSH', '--me=FOO')
|
|
fv(argv)
|
|
self.assertListEqual(fv.get_flag_value('m_enum', None), ['WHOOSH', 'FOO'])
|
|
|
|
def test_help_text(self):
|
|
"""Test multi_enum flag's help text."""
|
|
fv = flags.FlagValues()
|
|
|
|
flags.DEFINE_multi_enum(
|
|
'm_enum',
|
|
None, ['FOO', 'BAR'],
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
self.assertRegex(
|
|
fv['m_enum'].help,
|
|
r'<FOO\|BAR>: Enum option that can occur multiple times;\s+'
|
|
'repeat this option to specify a list of values')
|
|
|
|
def test_single_value_default(self):
|
|
"""Test multi_enum flags with a single default value."""
|
|
fv = flags.FlagValues()
|
|
enum_default = 'FOO'
|
|
flags.DEFINE_multi_enum(
|
|
'm_enum1',
|
|
enum_default, ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
|
|
'enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
self.assertListEqual(fv['m_enum1'].default, [enum_default])
|
|
|
|
def test_case_sensitivity(self):
|
|
"""Test case sensitivity of multi_enum flag."""
|
|
fv = flags.FlagValues()
|
|
# Test case insensitive enum.
|
|
flags.DEFINE_multi_enum(
|
|
'm_enum2', ['whoosh'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
|
|
'Enum option that can occur multiple times',
|
|
short_name='me2',
|
|
case_sensitive=False,
|
|
flag_values=fv)
|
|
argv = ('./program', '--m_enum2=bar', '--me2=fOo')
|
|
fv(argv)
|
|
self.assertListEqual(fv.get_flag_value('m_enum2', None), ['BAR', 'FOO'])
|
|
|
|
# Test case sensitive enum.
|
|
flags.DEFINE_multi_enum(
|
|
'm_enum3', ['BAR'], ['FOO', 'BAR', 'BAZ', 'WHOOSH'],
|
|
'Enum option that can occur multiple times',
|
|
short_name='me3',
|
|
case_sensitive=True,
|
|
flag_values=fv)
|
|
argv = ('./program', '--m_enum3=bar', '--me3=fOo')
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r'flag --m_enum3=invalid: value should be one of <FOO|BAR|BAZ|WHOOSH>',
|
|
fv, argv)
|
|
|
|
def test_bad_multi_enum_flags(self):
|
|
"""Test multi_enum with invalid values."""
|
|
|
|
# Test defaults that are not in the permitted list of enums.
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r'flag --m_enum=INVALID: value should be one of <FOO|BAR|BAZ>',
|
|
flags.DEFINE_multi_enum, 'm_enum', ['INVALID'], ['FOO', 'BAR', 'BAZ'],
|
|
'desc')
|
|
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r'flag --m_enum=1234: value should be one of <FOO|BAR|BAZ>',
|
|
flags.DEFINE_multi_enum, 'm_enum2', [1234], ['FOO', 'BAR', 'BAZ'],
|
|
'desc')
|
|
|
|
# Test command-line values that are not in the permitted list of enums.
|
|
flags.DEFINE_multi_enum('m_enum4', 'FOO', ['FOO', 'BAR', 'BAZ'],
|
|
'enum option that can occur multiple times')
|
|
argv = ('./program', '--m_enum4=INVALID')
|
|
self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
r'flag --m_enum4=invalid: value should be one of <FOO|BAR|BAZ>', FLAGS,
|
|
argv)
|
|
|
|
|
|
class MultiEnumClassFlagsTest(absltest.TestCase):
|
|
|
|
def test_define_results_in_registered_flag_with_none(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = None
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertIsNone(fv.fruit)
|
|
|
|
def test_help_text(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = None
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
|
|
self.assertRegex(
|
|
fv['fruit'].help,
|
|
r'<apple\|orange>: Enum option that can occur multiple times;\s+'
|
|
'repeat this option to specify a list of values')
|
|
|
|
def test_define_results_in_registered_flag_with_string(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = 'apple'
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertListEqual(fv.fruit, [Fruit.APPLE])
|
|
|
|
def test_define_results_in_registered_flag_with_enum(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = Fruit.APPLE
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertListEqual(fv.fruit, [Fruit.APPLE])
|
|
|
|
def test_define_results_in_registered_flag_with_string_list(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = ['apple', 'APPLE']
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
CaseSensitiveFruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv,
|
|
case_sensitive=True)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertListEqual(fv.fruit,
|
|
[CaseSensitiveFruit.apple, CaseSensitiveFruit.APPLE])
|
|
|
|
def test_define_results_in_registered_flag_with_enum_list(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = [Fruit.APPLE, Fruit.ORANGE]
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
fv.mark_as_parsed()
|
|
|
|
self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE])
|
|
|
|
def test_from_command_line_returns_multiple(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = [Fruit.APPLE]
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit',
|
|
enum_defaults,
|
|
Fruit,
|
|
'Enum option that can occur multiple times',
|
|
flag_values=fv)
|
|
argv = ('./program', '--fruit=Apple', '--fruit=orange')
|
|
fv(argv)
|
|
self.assertListEqual(fv.fruit, [Fruit.APPLE, Fruit.ORANGE])
|
|
|
|
def test_bad_multi_enum_class_flags_from_definition(self):
|
|
with self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'):
|
|
flags.DEFINE_multi_enum_class('fruit', ['INVALID'], Fruit, 'desc')
|
|
|
|
def test_bad_multi_enum_class_flags_from_commandline(self):
|
|
fv = flags.FlagValues()
|
|
enum_defaults = [Fruit.APPLE]
|
|
flags.DEFINE_multi_enum_class(
|
|
'fruit', enum_defaults, Fruit, 'desc', flag_values=fv)
|
|
argv = ('./program', '--fruit=INVALID')
|
|
with self.assertRaisesRegex(
|
|
flags.IllegalFlagValueError,
|
|
'flag --fruit=INVALID: value should be one of <apple|orange|APPLE>'):
|
|
fv(argv)
|
|
|
|
|
|
class UnicodeFlagsTest(absltest.TestCase):
|
|
"""Testing proper unicode support for flags."""
|
|
|
|
def test_unicode_default_and_helpstring(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_string(
|
|
'unicode_str',
|
|
b'\xC3\x80\xC3\xBD'.decode('utf-8'),
|
|
b'help:\xC3\xAA'.decode('utf-8'),
|
|
flag_values=fv)
|
|
argv = ('./program',)
|
|
fv(argv) # should not raise any exceptions
|
|
|
|
argv = ('./program', '--unicode_str=foo')
|
|
fv(argv) # should not raise any exceptions
|
|
|
|
def test_unicode_in_list(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_list(
|
|
'unicode_list',
|
|
['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')],
|
|
b'help:\xC3\xAB'.decode('utf-8'),
|
|
flag_values=fv)
|
|
argv = ('./program',)
|
|
fv(argv) # should not raise any exceptions
|
|
|
|
argv = ('./program', '--unicode_list=hello,there')
|
|
fv(argv) # should not raise any exceptions
|
|
|
|
def test_xmloutput(self):
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_string(
|
|
'unicode1',
|
|
b'\xC3\x80\xC3\xBD'.decode('utf-8'),
|
|
b'help:\xC3\xAC'.decode('utf-8'),
|
|
flag_values=fv)
|
|
flags.DEFINE_list(
|
|
'unicode2',
|
|
['abc', b'\xC3\x80'.decode('utf-8'), b'\xC3\xBD'.decode('utf-8')],
|
|
b'help:\xC3\xAD'.decode('utf-8'),
|
|
flag_values=fv)
|
|
flags.DEFINE_list(
|
|
'non_unicode', ['abc', 'def', 'ghi'],
|
|
b'help:\xC3\xAD'.decode('utf-8'),
|
|
flag_values=fv)
|
|
|
|
outfile = io.StringIO()
|
|
fv.write_help_in_xml_format(outfile)
|
|
actual_output = outfile.getvalue()
|
|
|
|
# The xml output is large, so we just check parts of it.
|
|
self.assertIn(
|
|
b'<name>unicode1</name>\n'
|
|
b' <meaning>help:\xc3\xac</meaning>\n'
|
|
b' <default>\xc3\x80\xc3\xbd</default>\n'
|
|
b' <current>\xc3\x80\xc3\xbd</current>'.decode('utf-8'),
|
|
actual_output)
|
|
self.assertIn(
|
|
b'<name>unicode2</name>\n'
|
|
b' <meaning>help:\xc3\xad</meaning>\n'
|
|
b' <default>abc,\xc3\x80,\xc3\xbd</default>\n'
|
|
b" <current>['abc', '\xc3\x80', '\xc3\xbd']"
|
|
b'</current>'.decode('utf-8'), actual_output)
|
|
self.assertIn(
|
|
b'<name>non_unicode</name>\n'
|
|
b' <meaning>help:\xc3\xad</meaning>\n'
|
|
b' <default>abc,def,ghi</default>\n'
|
|
b" <current>['abc', 'def', 'ghi']"
|
|
b'</current>'.decode('utf-8'), actual_output)
|
|
|
|
|
|
class LoadFromFlagFileTest(absltest.TestCase):
|
|
"""Testing loading flags from a file and parsing them."""
|
|
|
|
def setUp(self):
|
|
self.flag_values = flags.FlagValues()
|
|
flags.DEFINE_string(
|
|
'unittest_message1',
|
|
'Foo!',
|
|
'You Add Here.',
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_string(
|
|
'unittest_message2',
|
|
'Bar!',
|
|
'Hello, Sailor!',
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_boolean(
|
|
'unittest_boolflag',
|
|
0,
|
|
'Some Boolean thing',
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'unittest_number',
|
|
12345,
|
|
'Some integer',
|
|
lower_bound=0,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_list(
|
|
'UnitTestList', '1,2,3', 'Some list', flag_values=self.flag_values)
|
|
self.tmp_path = None
|
|
self.flag_values.mark_as_parsed()
|
|
|
|
def tearDown(self):
|
|
self._remove_test_files()
|
|
|
|
def _setup_test_files(self):
|
|
"""Creates and sets up some dummy flagfile files with bogus flags."""
|
|
|
|
# Figure out where to create temporary files
|
|
self.assertFalse(self.tmp_path)
|
|
self.tmp_path = tempfile.mkdtemp()
|
|
|
|
tmp_flag_file_1 = open(self.tmp_path + '/UnitTestFile1.tst', 'w')
|
|
tmp_flag_file_2 = open(self.tmp_path + '/UnitTestFile2.tst', 'w')
|
|
tmp_flag_file_3 = open(self.tmp_path + '/UnitTestFile3.tst', 'w')
|
|
tmp_flag_file_4 = open(self.tmp_path + '/UnitTestFile4.tst', 'w')
|
|
|
|
# put some dummy flags in our test files
|
|
tmp_flag_file_1.write('#A Fake Comment\n')
|
|
tmp_flag_file_1.write('--unittest_message1=tempFile1!\n')
|
|
tmp_flag_file_1.write('\n')
|
|
tmp_flag_file_1.write('--unittest_number=54321\n')
|
|
tmp_flag_file_1.write('--nounittest_boolflag\n')
|
|
file_list = [tmp_flag_file_1.name]
|
|
# this one includes test file 1
|
|
tmp_flag_file_2.write('//A Different Fake Comment\n')
|
|
tmp_flag_file_2.write('--flagfile=%s\n' % tmp_flag_file_1.name)
|
|
tmp_flag_file_2.write('--unittest_message2=setFromTempFile2\n')
|
|
tmp_flag_file_2.write('\t\t\n')
|
|
tmp_flag_file_2.write('--unittest_number=6789a\n')
|
|
file_list.append(tmp_flag_file_2.name)
|
|
# this file points to itself
|
|
tmp_flag_file_3.write('--flagfile=%s\n' % tmp_flag_file_3.name)
|
|
tmp_flag_file_3.write('--unittest_message1=setFromTempFile3\n')
|
|
tmp_flag_file_3.write('#YAFC\n')
|
|
tmp_flag_file_3.write('--unittest_boolflag\n')
|
|
file_list.append(tmp_flag_file_3.name)
|
|
# this file is unreadable
|
|
tmp_flag_file_4.write('--flagfile=%s\n' % tmp_flag_file_3.name)
|
|
tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n')
|
|
tmp_flag_file_4.write('--unittest_message1=setFromTempFile4\n')
|
|
os.chmod(self.tmp_path + '/UnitTestFile4.tst', 0)
|
|
file_list.append(tmp_flag_file_4.name)
|
|
|
|
tmp_flag_file_1.close()
|
|
tmp_flag_file_2.close()
|
|
tmp_flag_file_3.close()
|
|
tmp_flag_file_4.close()
|
|
|
|
return file_list # these are just the file names
|
|
|
|
def _remove_test_files(self):
|
|
"""Removes the files we just created."""
|
|
if self.tmp_path:
|
|
shutil.rmtree(self.tmp_path, ignore_errors=True)
|
|
self.tmp_path = None
|
|
|
|
def _read_flags_from_files(self, argv, force_gnu):
|
|
return argv[:1] + self.flag_values.read_flags_from_files(
|
|
argv[1:], force_gnu=force_gnu)
|
|
|
|
#### Flagfile Unit Tests ####
|
|
def test_method_flagfiles_1(self):
|
|
"""Test trivial case with no flagfile based options."""
|
|
fake_cmd_line = 'fooScript --unittest_boolflag'
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
self.flag_values(fake_argv)
|
|
self.assertEqual(self.flag_values.unittest_boolflag, 1)
|
|
self.assertListEqual(fake_argv,
|
|
self._read_flags_from_files(fake_argv, False))
|
|
|
|
def test_method_flagfiles_2(self):
|
|
"""Tests parsing one file + arguments off simulated argv."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = 'fooScript --q --flagfile=%s' % tmp_files[0]
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
|
|
# We should see the original cmd line with the file's contents spliced in.
|
|
# Flags from the file will appear in the order order they are specified
|
|
# in the file, in the same position as the flagfile argument.
|
|
expected_results = [
|
|
'fooScript', '--q', '--unittest_message1=tempFile1!',
|
|
'--unittest_number=54321', '--nounittest_boolflag'
|
|
]
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
# end testTwo def
|
|
|
|
def test_method_flagfiles_3(self):
|
|
"""Tests parsing nested files + arguments of simulated argv."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --unittest_number=77 --flagfile=%s' %
|
|
tmp_files[1])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
|
|
expected_results = [
|
|
'fooScript', '--unittest_number=77', '--unittest_message1=tempFile1!',
|
|
'--unittest_number=54321', '--nounittest_boolflag',
|
|
'--unittest_message2=setFromTempFile2', '--unittest_number=6789a'
|
|
]
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
# end testThree def
|
|
|
|
def test_method_flagfiles_3_spaces(self):
|
|
"""Tests parsing nested files + arguments of simulated argv.
|
|
|
|
The arguments include a pair that is actually an arg with a value, so it
|
|
doesn't stop processing.
|
|
"""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --unittest_number 77 --flagfile=%s' %
|
|
tmp_files[1])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
|
|
expected_results = [
|
|
'fooScript', '--unittest_number', '77',
|
|
'--unittest_message1=tempFile1!', '--unittest_number=54321',
|
|
'--nounittest_boolflag', '--unittest_message2=setFromTempFile2',
|
|
'--unittest_number=6789a'
|
|
]
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_3_spaces_boolean(self):
|
|
"""Tests parsing nested files + arguments of simulated argv.
|
|
|
|
The arguments include a pair that looks like a --x y arg with value, but
|
|
since the flag is a boolean it's actually not.
|
|
"""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --unittest_boolflag 77 --flagfile=%s' %
|
|
tmp_files[1])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
|
|
expected_results = [
|
|
'fooScript', '--unittest_boolflag', '77',
|
|
'--flagfile=%s' % tmp_files[1]
|
|
]
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_4(self):
|
|
"""Tests parsing self-referential files + arguments of simulated argv.
|
|
|
|
This test should print a warning to stderr of some sort.
|
|
"""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --flagfile=%s --nounittest_boolflag' %
|
|
tmp_files[2])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--unittest_message1=setFromTempFile3',
|
|
'--unittest_boolflag', '--nounittest_boolflag'
|
|
]
|
|
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_5(self):
|
|
"""Test that --flagfile parsing respects the '--' end-of-options marker."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = 'fooScript --some_flag -- --flagfile=%s' % tmp_files[0]
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--some_flag', '--',
|
|
'--flagfile=%s' % tmp_files[0]
|
|
]
|
|
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_6(self):
|
|
"""Test that --flagfile parsing stops at non-options (non-GNU behavior)."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
|
|
tmp_files[0])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--some_flag', 'some_arg',
|
|
'--flagfile=%s' % tmp_files[0]
|
|
]
|
|
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_7(self):
|
|
"""Test that --flagfile parsing skips over a non-option (GNU behavior)."""
|
|
self.flag_values.set_gnu_getopt()
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
|
|
tmp_files[0])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--some_flag', 'some_arg',
|
|
'--unittest_message1=tempFile1!', '--unittest_number=54321',
|
|
'--nounittest_boolflag'
|
|
]
|
|
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_8(self):
|
|
"""Test that --flagfile parsing respects force_gnu=True."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
|
|
tmp_files[0])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--some_flag', 'some_arg',
|
|
'--unittest_message1=tempFile1!', '--unittest_number=54321',
|
|
'--nounittest_boolflag'
|
|
]
|
|
|
|
test_results = self._read_flags_from_files(fake_argv, True)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
def test_method_flagfiles_repeated_non_circular(self):
|
|
"""Tests that parsing repeated non-circular flagfiles works."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp files on the fake cmd line
|
|
fake_cmd_line = ('fooScript --flagfile=%s --flagfile=%s' %
|
|
(tmp_files[1], tmp_files[0]))
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
expected_results = [
|
|
'fooScript', '--unittest_message1=tempFile1!',
|
|
'--unittest_number=54321', '--nounittest_boolflag',
|
|
'--unittest_message2=setFromTempFile2', '--unittest_number=6789a',
|
|
'--unittest_message1=tempFile1!', '--unittest_number=54321',
|
|
'--nounittest_boolflag'
|
|
]
|
|
|
|
test_results = self._read_flags_from_files(fake_argv, False)
|
|
self.assertListEqual(expected_results, test_results)
|
|
|
|
@unittest.skipIf(
|
|
os.name == 'nt',
|
|
'There is no good way to create an unreadable file on Windows.')
|
|
def test_method_flagfiles_no_permissions(self):
|
|
"""Test that --flagfile raises except on file that is unreadable."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%s' %
|
|
tmp_files[3])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files,
|
|
fake_argv, True)
|
|
|
|
def test_method_flagfiles_not_found(self):
|
|
"""Test that --flagfile raises except on file that does not exist."""
|
|
tmp_files = self._setup_test_files()
|
|
# specify our temp file on the fake cmd line
|
|
fake_cmd_line = ('fooScript --some_flag some_arg --flagfile=%sNOTEXIST' %
|
|
tmp_files[3])
|
|
fake_argv = fake_cmd_line.split(' ')
|
|
self.assertRaises(flags.CantOpenFlagFileError, self._read_flags_from_files,
|
|
fake_argv, True)
|
|
|
|
def test_flagfiles_user_path_expansion(self):
|
|
"""Test that user directory referenced paths are correctly expanded.
|
|
|
|
Test paths like ~/foo. This test depends on whatever account's running
|
|
the unit test to have read/write access to their own home directory,
|
|
otherwise it'll FAIL.
|
|
"""
|
|
fake_flagfile_item_style_1 = '--flagfile=~/foo.file'
|
|
fake_flagfile_item_style_2 = '-flagfile=~/foo.file'
|
|
|
|
expected_results = os.path.expanduser('~/foo.file')
|
|
|
|
test_results = self.flag_values._extract_filename(
|
|
fake_flagfile_item_style_1)
|
|
self.assertEqual(expected_results, test_results)
|
|
|
|
test_results = self.flag_values._extract_filename(
|
|
fake_flagfile_item_style_2)
|
|
self.assertEqual(expected_results, test_results)
|
|
|
|
def test_no_touchy_non_flags(self):
|
|
"""Test that the flags parser does not mutilate arguments.
|
|
|
|
The arguments are not supposed to be flags
|
|
"""
|
|
fake_argv = [
|
|
'fooScript', '--unittest_boolflag', 'command', '--command_arg1',
|
|
'--UnitTestBoom', '--UnitTestB'
|
|
]
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
argv = self.flag_values(fake_argv)
|
|
self.assertListEqual(argv, fake_argv[:1] + fake_argv[2:])
|
|
|
|
def test_parse_flags_after_args_if_using_gnugetopt(self):
|
|
"""Test that flags given after arguments are parsed if using gnu_getopt."""
|
|
self.flag_values.set_gnu_getopt()
|
|
fake_argv = [
|
|
'fooScript', '--unittest_boolflag', 'command', '--unittest_number=54321'
|
|
]
|
|
argv = self.flag_values(fake_argv)
|
|
self.assertListEqual(argv, ['fooScript', 'command'])
|
|
|
|
def test_set_default(self):
|
|
"""Test changing flag defaults."""
|
|
# Test that set_default changes both the default and the value,
|
|
# and that the value is changed when one is given as an option.
|
|
self.flag_values.set_default('unittest_message1', 'New value')
|
|
self.assertEqual(self.flag_values.unittest_message1, 'New value')
|
|
self.assertEqual(self.flag_values['unittest_message1'].default_as_str,
|
|
"'New value'")
|
|
self.flag_values(['dummyscript', '--unittest_message1=Newer value'])
|
|
self.assertEqual(self.flag_values.unittest_message1, 'Newer value')
|
|
|
|
# Test that setting the default to None works correctly.
|
|
self.flag_values.set_default('unittest_number', None)
|
|
self.assertEqual(self.flag_values.unittest_number, None)
|
|
self.assertEqual(self.flag_values['unittest_number'].default_as_str, None)
|
|
self.flag_values(['dummyscript', '--unittest_number=56'])
|
|
self.assertEqual(self.flag_values.unittest_number, 56)
|
|
|
|
# Test that setting the default to zero works correctly.
|
|
self.flag_values.set_default('unittest_number', 0)
|
|
self.assertEqual(self.flag_values['unittest_number'].default, 0)
|
|
self.assertEqual(self.flag_values.unittest_number, 56)
|
|
self.assertEqual(self.flag_values['unittest_number'].default_as_str, "'0'")
|
|
self.flag_values(['dummyscript', '--unittest_number=56'])
|
|
self.assertEqual(self.flag_values.unittest_number, 56)
|
|
|
|
# Test that setting the default to '' works correctly.
|
|
self.flag_values.set_default('unittest_message1', '')
|
|
self.assertEqual(self.flag_values['unittest_message1'].default, '')
|
|
self.assertEqual(self.flag_values.unittest_message1, 'Newer value')
|
|
self.assertEqual(self.flag_values['unittest_message1'].default_as_str, "''")
|
|
self.flag_values(['dummyscript', '--unittest_message1=fifty-six'])
|
|
self.assertEqual(self.flag_values.unittest_message1, 'fifty-six')
|
|
|
|
# Test that setting the default to false works correctly.
|
|
self.flag_values.set_default('unittest_boolflag', False)
|
|
self.assertEqual(self.flag_values.unittest_boolflag, False)
|
|
self.assertEqual(self.flag_values['unittest_boolflag'].default_as_str,
|
|
"'false'")
|
|
self.flag_values(['dummyscript', '--unittest_boolflag=true'])
|
|
self.assertEqual(self.flag_values.unittest_boolflag, True)
|
|
|
|
# Test that setting a list default works correctly.
|
|
self.flag_values.set_default('UnitTestList', '4,5,6')
|
|
self.assertListEqual(self.flag_values.UnitTestList, ['4', '5', '6'])
|
|
self.assertEqual(self.flag_values['UnitTestList'].default_as_str, "'4,5,6'")
|
|
self.flag_values(['dummyscript', '--UnitTestList=7,8,9'])
|
|
self.assertListEqual(self.flag_values.UnitTestList, ['7', '8', '9'])
|
|
|
|
# Test that setting invalid defaults raises exceptions
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
self.flag_values.set_default('unittest_number', 'oops')
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
self.flag_values.set_default('unittest_number', -1)
|
|
|
|
|
|
class FlagsParsingTest(absltest.TestCase):
|
|
"""Testing different aspects of parsing: '-f' vs '--flag', etc."""
|
|
|
|
def setUp(self):
|
|
self.flag_values = flags.FlagValues()
|
|
|
|
def test_two_dash_arg_first(self):
|
|
flags.DEFINE_string(
|
|
'twodash_name', 'Bob', 'namehelp', flag_values=self.flag_values)
|
|
flags.DEFINE_string(
|
|
'twodash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
|
|
argv = ('./program', '--', '--twodash_name=Harry')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual('Bob', self.flag_values.twodash_name)
|
|
self.assertEqual(argv[1], '--twodash_name=Harry')
|
|
|
|
def test_two_dash_arg_middle(self):
|
|
flags.DEFINE_string(
|
|
'twodash2_name', 'Bob', 'namehelp', flag_values=self.flag_values)
|
|
flags.DEFINE_string(
|
|
'twodash2_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
|
|
argv = ('./program', '--twodash2_blame=Larry', '--',
|
|
'--twodash2_name=Harry')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual('Bob', self.flag_values.twodash2_name)
|
|
self.assertEqual('Larry', self.flag_values.twodash2_blame)
|
|
self.assertEqual(argv[1], '--twodash2_name=Harry')
|
|
|
|
def test_one_dash_arg_first(self):
|
|
flags.DEFINE_string(
|
|
'onedash_name', 'Bob', 'namehelp', flag_values=self.flag_values)
|
|
flags.DEFINE_string(
|
|
'onedash_blame', 'Rob', 'blamehelp', flag_values=self.flag_values)
|
|
argv = ('./program', '-', '--onedash_name=Harry')
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 3)
|
|
self.assertEqual(argv[1], '-')
|
|
self.assertEqual(argv[2], '--onedash_name=Harry')
|
|
|
|
def test_required_flag_not_specified(self):
|
|
flags.DEFINE_string(
|
|
'str_flag',
|
|
default=None,
|
|
help='help',
|
|
required=True,
|
|
flag_values=self.flag_values)
|
|
argv = ('./program',)
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
self.flag_values(argv)
|
|
|
|
def test_required_arg_works_with_other_validators(self):
|
|
flags.DEFINE_integer(
|
|
'int_flag',
|
|
default=None,
|
|
help='help',
|
|
required=True,
|
|
lower_bound=4,
|
|
flag_values=self.flag_values)
|
|
argv = ('./program', '--int_flag=2')
|
|
with _use_gnu_getopt(self.flag_values, False):
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
self.flag_values(argv)
|
|
|
|
def test_unrecognized_flags(self):
|
|
flags.DEFINE_string('name', 'Bob', 'namehelp', flag_values=self.flag_values)
|
|
# Unknown flag --nosuchflag
|
|
try:
|
|
argv = ('./program', '--nosuchflag', '--name=Bob', 'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflag')
|
|
self.assertEqual(e.flagvalue, '--nosuchflag')
|
|
|
|
# Unknown flag -w (short option)
|
|
try:
|
|
argv = ('./program', '-w', '--name=Bob', 'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'w')
|
|
self.assertEqual(e.flagvalue, '-w')
|
|
|
|
# Unknown flag --nosuchflagwithparam=foo
|
|
try:
|
|
argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflagwithparam')
|
|
self.assertEqual(e.flagvalue, '--nosuchflagwithparam=foo')
|
|
|
|
# Allow unknown flag --nosuchflag if specified with undefok
|
|
argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchflag',
|
|
'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# Allow unknown flag --noboolflag if undefok=boolflag is specified
|
|
argv = ('./program', '--noboolflag', '--name=Bob', '--undefok=boolflag',
|
|
'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# But not if the flagname is misspelled:
|
|
try:
|
|
argv = ('./program', '--nosuchflag', '--name=Bob', '--undefok=nosuchfla',
|
|
'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflag')
|
|
|
|
try:
|
|
argv = ('./program', '--nosuchflag', '--name=Bob',
|
|
'--undefok=nosuchflagg', 'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflag')
|
|
|
|
# Allow unknown short flag -w if specified with undefok
|
|
argv = ('./program', '-w', '--name=Bob', '--undefok=w', 'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# Allow unknown flag --nosuchflagwithparam=foo if specified
|
|
# with undefok
|
|
argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob',
|
|
'--undefok=nosuchflagwithparam', 'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# Even if undefok specifies multiple flags
|
|
argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
|
|
'--name=Bob', '--undefok=nosuchflag,w,nosuchflagwithparam', 'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# However, not if undefok doesn't specify the flag
|
|
try:
|
|
argv = ('./program', '--nosuchflag', '--name=Bob',
|
|
'--undefok=another_such', 'extra')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflag')
|
|
|
|
# Make sure --undefok doesn't mask other option errors.
|
|
try:
|
|
# Provide an option requiring a parameter but not giving it one.
|
|
argv = ('./program', '--undefok=name', '--name')
|
|
self.flag_values(argv)
|
|
raise AssertionError('Missing option parameter exception not raised')
|
|
except flags.UnrecognizedFlagError:
|
|
raise AssertionError('Wrong kind of error exception raised')
|
|
except flags.Error:
|
|
pass
|
|
|
|
# Test --undefok <list>
|
|
argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
|
|
'--name=Bob', '--undefok', 'nosuchflag,w,nosuchflagwithparam',
|
|
'extra')
|
|
argv = self.flag_values(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
|
|
# Test incorrect --undefok with no value.
|
|
argv = ('./program', '--name=Bob', '--undefok')
|
|
with self.assertRaises(flags.Error):
|
|
self.flag_values(argv)
|
|
|
|
|
|
class NonGlobalFlagsTest(absltest.TestCase):
|
|
|
|
def test_nonglobal_flags(self):
|
|
"""Test use of non-global FlagValues."""
|
|
nonglobal_flags = flags.FlagValues()
|
|
flags.DEFINE_string('nonglobal_flag', 'Bob', 'flaghelp', nonglobal_flags)
|
|
argv = ('./program', '--nonglobal_flag=Mary', 'extra')
|
|
argv = nonglobal_flags(argv)
|
|
self.assertEqual(len(argv), 2, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
self.assertEqual(argv[1], 'extra', 'extra argument not preserved')
|
|
self.assertEqual(nonglobal_flags['nonglobal_flag'].value, 'Mary')
|
|
|
|
def test_unrecognized_nonglobal_flags(self):
|
|
"""Test unrecognized non-global flags."""
|
|
nonglobal_flags = flags.FlagValues()
|
|
argv = ('./program', '--nosuchflag')
|
|
try:
|
|
argv = nonglobal_flags(argv)
|
|
raise AssertionError('Unknown flag exception not raised')
|
|
except flags.UnrecognizedFlagError as e:
|
|
self.assertEqual(e.flagname, 'nosuchflag')
|
|
|
|
argv = ('./program', '--nosuchflag', '--undefok=nosuchflag')
|
|
|
|
argv = nonglobal_flags(argv)
|
|
self.assertEqual(len(argv), 1, 'wrong number of arguments pulled')
|
|
self.assertEqual(argv[0], './program', 'program name not preserved')
|
|
|
|
def test_create_flag_errors(self):
|
|
# Since the exception classes are exposed, nothing stops users
|
|
# from creating their own instances. This test makes sure that
|
|
# people modifying the flags module understand that the external
|
|
# mechanisms for creating the exceptions should continue to work.
|
|
_ = flags.Error()
|
|
_ = flags.Error('message')
|
|
_ = flags.DuplicateFlagError()
|
|
_ = flags.DuplicateFlagError('message')
|
|
_ = flags.IllegalFlagValueError()
|
|
_ = flags.IllegalFlagValueError('message')
|
|
|
|
def test_flag_values_del_attr(self):
|
|
"""Checks that del self.flag_values.flag_id works."""
|
|
default_value = 'default value for test_flag_values_del_attr'
|
|
# 1. Declare and delete a flag with no short name.
|
|
flag_values = flags.FlagValues()
|
|
flags.DEFINE_string(
|
|
'delattr_foo', default_value, 'A simple flag.', flag_values=flag_values)
|
|
|
|
flag_values.mark_as_parsed()
|
|
self.assertEqual(flag_values.delattr_foo, default_value)
|
|
flag_obj = flag_values['delattr_foo']
|
|
# We also check that _FlagIsRegistered works as expected :)
|
|
self.assertTrue(flag_values._flag_is_registered(flag_obj))
|
|
del flag_values.delattr_foo
|
|
self.assertFalse('delattr_foo' in flag_values._flags())
|
|
self.assertFalse(flag_values._flag_is_registered(flag_obj))
|
|
# If the previous del FLAGS.delattr_foo did not work properly, the
|
|
# next definition will trigger a redefinition error.
|
|
flags.DEFINE_integer(
|
|
'delattr_foo', 3, 'A simple flag.', flag_values=flag_values)
|
|
del flag_values.delattr_foo
|
|
|
|
self.assertFalse('delattr_foo' in flag_values)
|
|
|
|
# 2. Declare and delete a flag with a short name.
|
|
flags.DEFINE_string(
|
|
'delattr_bar',
|
|
default_value,
|
|
'flag with short name',
|
|
short_name='x5',
|
|
flag_values=flag_values)
|
|
flag_obj = flag_values['delattr_bar']
|
|
self.assertTrue(flag_values._flag_is_registered(flag_obj))
|
|
del flag_values.x5
|
|
self.assertTrue(flag_values._flag_is_registered(flag_obj))
|
|
del flag_values.delattr_bar
|
|
self.assertFalse(flag_values._flag_is_registered(flag_obj))
|
|
|
|
# 3. Just like 2, but del flag_values.name last
|
|
flags.DEFINE_string(
|
|
'delattr_bar',
|
|
default_value,
|
|
'flag with short name',
|
|
short_name='x5',
|
|
flag_values=flag_values)
|
|
flag_obj = flag_values['delattr_bar']
|
|
self.assertTrue(flag_values._flag_is_registered(flag_obj))
|
|
del flag_values.delattr_bar
|
|
self.assertTrue(flag_values._flag_is_registered(flag_obj))
|
|
del flag_values.x5
|
|
self.assertFalse(flag_values._flag_is_registered(flag_obj))
|
|
|
|
self.assertFalse('delattr_bar' in flag_values)
|
|
self.assertFalse('x5' in flag_values)
|
|
|
|
def test_list_flag_format(self):
|
|
"""Tests for correctly-formatted list flags."""
|
|
fv = flags.FlagValues()
|
|
flags.DEFINE_list('listflag', '', 'A list of arguments', flag_values=fv)
|
|
|
|
def _check_parsing(listval):
|
|
"""Parse a particular value for our test flag, --listflag."""
|
|
argv = fv(['./program', '--listflag=' + listval, 'plain-arg'])
|
|
self.assertEqual(['./program', 'plain-arg'], argv)
|
|
return fv.listflag
|
|
|
|
# Basic success case
|
|
self.assertEqual(_check_parsing('foo,bar'), ['foo', 'bar'])
|
|
# Success case: newline in argument is quoted.
|
|
self.assertEqual(_check_parsing('"foo","bar\nbar"'), ['foo', 'bar\nbar'])
|
|
# Failure case: newline in argument is unquoted.
|
|
self.assertRaises(flags.IllegalFlagValueError, _check_parsing,
|
|
'"foo",bar\nbar')
|
|
# Failure case: unmatched ".
|
|
self.assertRaises(flags.IllegalFlagValueError, _check_parsing,
|
|
'"foo,barbar')
|
|
|
|
def test_flag_definition_via_setitem(self):
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flag_values = flags.FlagValues()
|
|
flag_values['flag_name'] = 'flag_value'
|
|
|
|
|
|
class SetDefaultTest(absltest.TestCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.flag_values = flags.FlagValues()
|
|
|
|
def test_success(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', 1, 'an int', flag_values=self.flag_values)
|
|
|
|
flags.set_default(int_holder, 2)
|
|
self.flag_values.mark_as_parsed()
|
|
|
|
self.assertEqual(int_holder.value, 2)
|
|
|
|
def test_update_after_parse(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', 1, 'an int', flag_values=self.flag_values)
|
|
|
|
self.flag_values.mark_as_parsed()
|
|
flags.set_default(int_holder, 2)
|
|
|
|
self.assertEqual(int_holder.value, 2)
|
|
|
|
def test_overridden_by_explicit_assignment(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', 1, 'an int', flag_values=self.flag_values)
|
|
|
|
self.flag_values.mark_as_parsed()
|
|
self.flag_values.an_int = 3
|
|
flags.set_default(int_holder, 2)
|
|
|
|
self.assertEqual(int_holder.value, 3)
|
|
|
|
def test_restores_back_to_none(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', None, 'an int', flag_values=self.flag_values)
|
|
|
|
self.flag_values.mark_as_parsed()
|
|
flags.set_default(int_holder, 3)
|
|
flags.set_default(int_holder, None)
|
|
|
|
self.assertIsNone(int_holder.value)
|
|
|
|
def test_failure_on_invalid_type(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', 1, 'an int', flag_values=self.flag_values)
|
|
|
|
self.flag_values.mark_as_parsed()
|
|
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
flags.set_default(int_holder, 'a')
|
|
|
|
def test_failure_on_type_protected_none_default(self):
|
|
int_holder = flags.DEFINE_integer(
|
|
'an_int', 1, 'an int', flag_values=self.flag_values)
|
|
|
|
self.flag_values.mark_as_parsed()
|
|
|
|
flags.set_default(int_holder, None) # NOTE: should be a type failure
|
|
|
|
with self.assertRaises(flags.IllegalFlagValueError):
|
|
_ = int_holder.value # Will also fail on later access.
|
|
|
|
|
|
class KeyFlagsTest(absltest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.flag_values = flags.FlagValues()
|
|
|
|
def _get_names_of_defined_flags(self, module, flag_values):
|
|
"""Returns the list of names of flags defined by a module.
|
|
|
|
Auxiliary for the test_key_flags* methods.
|
|
|
|
Args:
|
|
module: A module object or a string module name.
|
|
flag_values: A FlagValues object.
|
|
|
|
Returns:
|
|
A list of strings.
|
|
"""
|
|
return [f.name for f in flag_values.get_flags_for_module(module)]
|
|
|
|
def _get_names_of_key_flags(self, module, flag_values):
|
|
"""Returns the list of names of key flags for a module.
|
|
|
|
Auxiliary for the test_key_flags* methods.
|
|
|
|
Args:
|
|
module: A module object or a string module name.
|
|
flag_values: A FlagValues object.
|
|
|
|
Returns:
|
|
A list of strings.
|
|
"""
|
|
return [f.name for f in flag_values.get_key_flags_for_module(module)]
|
|
|
|
def _assert_lists_have_same_elements(self, list_1, list_2):
|
|
# Checks that two lists have the same elements with the same
|
|
# multiplicity, in possibly different order.
|
|
list_1 = list(list_1)
|
|
list_1.sort()
|
|
list_2 = list(list_2)
|
|
list_2.sort()
|
|
self.assertListEqual(list_1, list_2)
|
|
|
|
def test_key_flags(self):
|
|
flag_values = flags.FlagValues()
|
|
# Before starting any testing, make sure no flags are already
|
|
# defined for module_foo and module_bar.
|
|
self.assertListEqual(
|
|
self._get_names_of_key_flags(module_foo, flag_values), [])
|
|
self.assertListEqual(
|
|
self._get_names_of_key_flags(module_bar, flag_values), [])
|
|
self.assertListEqual(
|
|
self._get_names_of_defined_flags(module_foo, flag_values), [])
|
|
self.assertListEqual(
|
|
self._get_names_of_defined_flags(module_bar, flag_values), [])
|
|
|
|
# Defines a few flags in module_foo and module_bar.
|
|
module_foo.define_flags(flag_values=flag_values)
|
|
|
|
try:
|
|
# Part 1. Check that all flags defined by module_foo are key for
|
|
# that module, and similarly for module_bar.
|
|
for module in [module_foo, module_bar]:
|
|
self._assert_lists_have_same_elements(
|
|
flag_values.get_flags_for_module(module),
|
|
flag_values.get_key_flags_for_module(module))
|
|
# Also check that each module defined the expected flags.
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_defined_flags(module, flag_values),
|
|
module.names_of_defined_flags())
|
|
|
|
# Part 2. Check that flags.declare_key_flag works fine.
|
|
# Declare that some flags from module_bar are key for
|
|
# module_foo.
|
|
module_foo.declare_key_flags(flag_values=flag_values)
|
|
|
|
# Check that module_foo has the expected list of defined flags.
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_defined_flags(module_foo, flag_values),
|
|
module_foo.names_of_defined_flags())
|
|
|
|
# Check that module_foo has the expected list of key flags.
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(module_foo, flag_values),
|
|
module_foo.names_of_declared_key_flags())
|
|
|
|
# Part 3. Check that flags.adopt_module_key_flags works fine.
|
|
# Trigger a call to flags.adopt_module_key_flags(module_bar)
|
|
# inside module_foo. This should declare a few more key
|
|
# flags in module_foo.
|
|
module_foo.declare_extra_key_flags(flag_values=flag_values)
|
|
|
|
# Check that module_foo has the expected list of key flags.
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(module_foo, flag_values),
|
|
module_foo.names_of_declared_key_flags() +
|
|
module_foo.names_of_declared_extra_key_flags())
|
|
finally:
|
|
module_foo.remove_flags(flag_values=flag_values)
|
|
|
|
def test_key_flags_with_non_default_flag_values_object(self):
|
|
# Check that key flags work even when we use a FlagValues object
|
|
# that is not the default flags.self.flag_values object. Otherwise, this
|
|
# test is similar to test_key_flags, but it uses only module_bar.
|
|
# The other test module (module_foo) uses only the default values
|
|
# for the flag_values keyword arguments. This way, test_key_flags
|
|
# and this method test both the default FlagValues, the explicitly
|
|
# specified one, and a mixed usage of the two.
|
|
|
|
# A brand-new FlagValues object, to use instead of flags.self.flag_values.
|
|
fv = flags.FlagValues()
|
|
|
|
# Before starting any testing, make sure no flags are already
|
|
# defined for module_foo and module_bar.
|
|
self.assertListEqual(self._get_names_of_key_flags(module_bar, fv), [])
|
|
self.assertListEqual(self._get_names_of_defined_flags(module_bar, fv), [])
|
|
|
|
module_bar.define_flags(flag_values=fv)
|
|
|
|
# Check that all flags defined by module_bar are key for that
|
|
# module, and that module_bar defined the expected flags.
|
|
self._assert_lists_have_same_elements(
|
|
fv.get_flags_for_module(module_bar),
|
|
fv.get_key_flags_for_module(module_bar))
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_defined_flags(module_bar, fv),
|
|
module_bar.names_of_defined_flags())
|
|
|
|
# Pick two flags from module_bar, declare them as key for the
|
|
# current (i.e., main) module (via flags.declare_key_flag), and
|
|
# check that we get the expected effect. The important thing is
|
|
# that we always use flags_values=fv (instead of the default
|
|
# self.flag_values).
|
|
main_module = sys.argv[0]
|
|
names_of_flags_defined_by_bar = module_bar.names_of_defined_flags()
|
|
flag_name_0 = names_of_flags_defined_by_bar[0]
|
|
flag_name_2 = names_of_flags_defined_by_bar[2]
|
|
|
|
flags.declare_key_flag(flag_name_0, flag_values=fv)
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(main_module, fv), [flag_name_0])
|
|
|
|
flags.declare_key_flag(flag_name_2, flag_values=fv)
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(main_module, fv),
|
|
[flag_name_0, flag_name_2])
|
|
|
|
# Try with a special (not user-defined) flag too:
|
|
flags.declare_key_flag('undefok', flag_values=fv)
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(main_module, fv),
|
|
[flag_name_0, flag_name_2, 'undefok'])
|
|
|
|
flags.adopt_module_key_flags(module_bar, fv)
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(main_module, fv),
|
|
names_of_flags_defined_by_bar + ['undefok'])
|
|
|
|
# Adopt key flags from the flags module itself.
|
|
flags.adopt_module_key_flags(flags, flag_values=fv)
|
|
self._assert_lists_have_same_elements(
|
|
self._get_names_of_key_flags(main_module, fv),
|
|
names_of_flags_defined_by_bar + ['flagfile', 'undefok'])
|
|
|
|
def test_key_flags_with_flagholders(self):
|
|
main_module = sys.argv[0]
|
|
|
|
self.assertListEqual(
|
|
self._get_names_of_key_flags(main_module, self.flag_values), [])
|
|
self.assertListEqual(
|
|
self._get_names_of_defined_flags(main_module, self.flag_values), [])
|
|
|
|
int_holder = flags.DEFINE_integer(
|
|
'main_module_int_fg',
|
|
1,
|
|
'Integer flag in the main module.',
|
|
flag_values=self.flag_values)
|
|
|
|
flags.declare_key_flag(int_holder, self.flag_values)
|
|
|
|
self.assertCountEqual(
|
|
self.flag_values.get_flags_for_module(main_module),
|
|
self.flag_values.get_key_flags_for_module(main_module))
|
|
|
|
bool_holder = flags.DEFINE_boolean(
|
|
'main_module_bool_fg',
|
|
False,
|
|
'Boolean flag in the main module.',
|
|
flag_values=self.flag_values)
|
|
|
|
flags.declare_key_flag(bool_holder) # omitted flag_values
|
|
|
|
self.assertCountEqual(
|
|
self.flag_values.get_flags_for_module(main_module),
|
|
self.flag_values.get_key_flags_for_module(main_module))
|
|
|
|
self.assertLen(self.flag_values.get_flags_for_module(main_module), 2)
|
|
|
|
def test_main_module_help_with_key_flags(self):
|
|
# Similar to test_main_module_help, but this time we make sure to
|
|
# declare some key flags.
|
|
|
|
# Safety check that the main module does not declare any flags
|
|
# at the beginning of this test.
|
|
expected_help = ''
|
|
self.assertMultiLineEqual(expected_help,
|
|
self.flag_values.main_module_help())
|
|
|
|
# Define one flag in this main module and some flags in modules
|
|
# a and b. Also declare one flag from module a and one flag
|
|
# from module b as key flags for the main module.
|
|
flags.DEFINE_integer(
|
|
'main_module_int_fg',
|
|
1,
|
|
'Integer flag in the main module.',
|
|
flag_values=self.flag_values)
|
|
|
|
try:
|
|
main_module_int_fg_help = (
|
|
' --main_module_int_fg: Integer flag in the main module.\n'
|
|
" (default: '1')\n"
|
|
' (an integer)')
|
|
|
|
expected_help += '\n%s:\n%s' % (sys.argv[0], main_module_int_fg_help)
|
|
self.assertMultiLineEqual(expected_help,
|
|
self.flag_values.main_module_help())
|
|
|
|
# The following call should be a no-op: any flag declared by a
|
|
# module is automatically key for that module.
|
|
flags.declare_key_flag('main_module_int_fg', flag_values=self.flag_values)
|
|
self.assertMultiLineEqual(expected_help,
|
|
self.flag_values.main_module_help())
|
|
|
|
# The definition of a few flags in an imported module should not
|
|
# change the main module help.
|
|
module_foo.define_flags(flag_values=self.flag_values)
|
|
self.assertMultiLineEqual(expected_help,
|
|
self.flag_values.main_module_help())
|
|
|
|
flags.declare_key_flag('tmod_foo_bool', flag_values=self.flag_values)
|
|
tmod_foo_bool_help = (
|
|
' --[no]tmod_foo_bool: Boolean flag from module foo.\n'
|
|
" (default: 'true')")
|
|
expected_help += '\n' + tmod_foo_bool_help
|
|
self.assertMultiLineEqual(expected_help,
|
|
self.flag_values.main_module_help())
|
|
|
|
flags.declare_key_flag('tmod_bar_z', flag_values=self.flag_values)
|
|
tmod_bar_z_help = (
|
|
' --[no]tmod_bar_z: Another boolean flag from module bar.\n'
|
|
" (default: 'false')")
|
|
# Unfortunately, there is some flag sorting inside
|
|
# main_module_help, so we can't keep incrementally extending
|
|
# the expected_help string ...
|
|
expected_help = ('\n%s:\n%s\n%s\n%s' %
|
|
(sys.argv[0], main_module_int_fg_help, tmod_bar_z_help,
|
|
tmod_foo_bool_help))
|
|
self.assertMultiLineEqual(self.flag_values.main_module_help(),
|
|
expected_help)
|
|
|
|
finally:
|
|
# At the end, delete all the flag information we created.
|
|
self.flag_values.__delattr__('main_module_int_fg')
|
|
module_foo.remove_flags(flag_values=self.flag_values)
|
|
|
|
def test_adoptmodule_key_flags(self):
|
|
# Check that adopt_module_key_flags raises an exception when
|
|
# called with a module name (as opposed to a module object).
|
|
self.assertRaises(flags.Error, flags.adopt_module_key_flags, 'pyglib.app')
|
|
|
|
def test_disclaimkey_flags(self):
|
|
original_disclaim_module_ids = _helpers.disclaim_module_ids
|
|
_helpers.disclaim_module_ids = set(_helpers.disclaim_module_ids)
|
|
try:
|
|
module_bar.disclaim_key_flags()
|
|
module_foo.define_bar_flags(flag_values=self.flag_values)
|
|
module_name = self.flag_values.find_module_defining_flag('tmod_bar_x')
|
|
self.assertEqual(module_foo.__name__, module_name)
|
|
finally:
|
|
_helpers.disclaim_module_ids = original_disclaim_module_ids
|
|
|
|
|
|
class FindModuleTest(absltest.TestCase):
|
|
"""Testing methods that find a module that defines a given flag."""
|
|
|
|
def test_find_module_defining_flag(self):
|
|
self.assertEqual(
|
|
'default',
|
|
FLAGS.find_module_defining_flag('__NON_EXISTENT_FLAG__', 'default'))
|
|
self.assertEqual(module_baz.__name__,
|
|
FLAGS.find_module_defining_flag('tmod_baz_x'))
|
|
|
|
def test_find_module_id_defining_flag(self):
|
|
self.assertEqual(
|
|
'default',
|
|
FLAGS.find_module_id_defining_flag('__NON_EXISTENT_FLAG__', 'default'))
|
|
self.assertEqual(
|
|
id(module_baz), FLAGS.find_module_id_defining_flag('tmod_baz_x'))
|
|
|
|
def test_find_module_defining_flag_passing_module_name(self):
|
|
my_flags = flags.FlagValues()
|
|
module_name = sys.__name__ # Must use an existing module.
|
|
flags.DEFINE_boolean(
|
|
'flag_name',
|
|
True,
|
|
'Flag with a different module name.',
|
|
flag_values=my_flags,
|
|
module_name=module_name)
|
|
self.assertEqual(module_name,
|
|
my_flags.find_module_defining_flag('flag_name'))
|
|
|
|
def test_find_module_id_defining_flag_passing_module_name(self):
|
|
my_flags = flags.FlagValues()
|
|
module_name = sys.__name__ # Must use an existing module.
|
|
flags.DEFINE_boolean(
|
|
'flag_name',
|
|
True,
|
|
'Flag with a different module name.',
|
|
flag_values=my_flags,
|
|
module_name=module_name)
|
|
self.assertEqual(
|
|
id(sys), my_flags.find_module_id_defining_flag('flag_name'))
|
|
|
|
|
|
class FlagsErrorMessagesTest(absltest.TestCase):
|
|
"""Testing special cases for integer and float flags error messages."""
|
|
|
|
def setUp(self):
|
|
self.flag_values = flags.FlagValues()
|
|
|
|
def test_integer_error_text(self):
|
|
# Make sure we get proper error text
|
|
flags.DEFINE_integer(
|
|
'positive',
|
|
4,
|
|
'non-negative flag',
|
|
lower_bound=1,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'non_negative',
|
|
4,
|
|
'positive flag',
|
|
lower_bound=0,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'negative',
|
|
-4,
|
|
'negative flag',
|
|
upper_bound=-1,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'non_positive',
|
|
-4,
|
|
'non-positive flag',
|
|
upper_bound=0,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'greater',
|
|
19,
|
|
'greater-than flag',
|
|
lower_bound=4,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'smaller',
|
|
-19,
|
|
'smaller-than flag',
|
|
upper_bound=4,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'usual',
|
|
4,
|
|
'usual flag',
|
|
lower_bound=0,
|
|
upper_bound=10000,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_integer(
|
|
'another_usual',
|
|
0,
|
|
'usual flag',
|
|
lower_bound=-1,
|
|
upper_bound=1,
|
|
flag_values=self.flag_values)
|
|
|
|
self._check_error_message('positive', -4, 'a positive integer')
|
|
self._check_error_message('non_negative', -4, 'a non-negative integer')
|
|
self._check_error_message('negative', 0, 'a negative integer')
|
|
self._check_error_message('non_positive', 4, 'a non-positive integer')
|
|
self._check_error_message('usual', -4, 'an integer in the range [0, 10000]')
|
|
self._check_error_message('another_usual', 4,
|
|
'an integer in the range [-1, 1]')
|
|
self._check_error_message('greater', -5, 'integer >= 4')
|
|
self._check_error_message('smaller', 5, 'integer <= 4')
|
|
|
|
def test_float_error_text(self):
|
|
flags.DEFINE_float(
|
|
'positive',
|
|
4,
|
|
'non-negative flag',
|
|
lower_bound=1,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'non_negative',
|
|
4,
|
|
'positive flag',
|
|
lower_bound=0,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'negative',
|
|
-4,
|
|
'negative flag',
|
|
upper_bound=-1,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'non_positive',
|
|
-4,
|
|
'non-positive flag',
|
|
upper_bound=0,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'greater',
|
|
19,
|
|
'greater-than flag',
|
|
lower_bound=4,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'smaller',
|
|
-19,
|
|
'smaller-than flag',
|
|
upper_bound=4,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'usual',
|
|
4,
|
|
'usual flag',
|
|
lower_bound=0,
|
|
upper_bound=10000,
|
|
flag_values=self.flag_values)
|
|
flags.DEFINE_float(
|
|
'another_usual',
|
|
0,
|
|
'usual flag',
|
|
lower_bound=-1,
|
|
upper_bound=1,
|
|
flag_values=self.flag_values)
|
|
|
|
self._check_error_message('positive', 0.5, 'number >= 1')
|
|
self._check_error_message('non_negative', -4.0, 'a non-negative number')
|
|
self._check_error_message('negative', 0.5, 'number <= -1')
|
|
self._check_error_message('non_positive', 4.0, 'a non-positive number')
|
|
self._check_error_message('usual', -4.0, 'a number in the range [0, 10000]')
|
|
self._check_error_message('another_usual', 4.0,
|
|
'a number in the range [-1, 1]')
|
|
self._check_error_message('smaller', 5.0, 'number <= 4')
|
|
|
|
def _check_error_message(self, flag_name, flag_value,
|
|
expected_message_suffix):
|
|
"""Set a flag to a given value and make sure we get expected message."""
|
|
|
|
try:
|
|
self.flag_values.__setattr__(flag_name, flag_value)
|
|
raise AssertionError('Bounds exception not raised!')
|
|
except flags.IllegalFlagValueError as e:
|
|
expected = ('flag --%(name)s=%(value)s: %(value)s is not %(suffix)s' % {
|
|
'name': flag_name,
|
|
'value': flag_value,
|
|
'suffix': expected_message_suffix
|
|
})
|
|
self.assertEqual(str(e), expected)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
absltest.main()
|