diff --git a/nova/compat/__init__.py b/nova/compat/__init__.py new file mode 100644 index 00000000..8f085d93 --- /dev/null +++ b/nova/compat/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 Red Hat, Inc. +# +# 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. diff --git a/nova/compat/flagfile.py b/nova/compat/flagfile.py new file mode 100644 index 00000000..8721d348 --- /dev/null +++ b/nova/compat/flagfile.py @@ -0,0 +1,182 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 Red Hat, Inc. +# +# 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. + +import contextlib +import os +import shutil +import tempfile + +''' +Compatibility code for handling the deprecated --flagfile option. + +gflags style configuration files are deprecated and will be removed in future. + +The code in this module transles --flagfile options into --config-file and can +be removed when support for --flagfile is removed. +''' + + +def _get_flagfile(argp): + '''Parse the filename from a --flagfile argument. + + The current and next arguments are passed as a 2 item list. If the + flagfile filename is in the next argument, the two arguments are + joined into the first item while the second item is set to None. + ''' + i = argp[0].find('-flagfile') + if i < 0: + return None + + # Accept -flagfile or -flagfile + if i != 0 and (i != 1 or argp[0][i] != '-'): + return None + + i += len('-flagfile') + if i == len(argp[0]): # Accept [-]-flagfile foo + argp[0] += '=' + argp[1] + argp[1] = None + + if argp[0][i] != '=': # Accept [-]-flagfile=foo + return None + + return argp[0][i + 1:] + + +def _open_file_for_reading(path): + '''Helper method which test code may stub out.''' + return open(path, 'r') + + +def _open_fd_for_writing(fd, _path): + '''Helper method which test code may stub out.''' + return os.fdopen(fd, 'w') + + +def _read_lines(flagfile): + '''Read a flag file, returning all lines with comments stripped.''' + with _open_file_for_reading(flagfile) as f: + lines = f.readlines() + ret = [] + for l in lines: + if l.isspace() or l.startswith('#') or l.startswith('//'): + continue + ret.append(l.strip()) + return ret + + +def _read_flagfile(arg, next_arg, tempdir=None): + '''Convert a --flagfile argument to --config-file. + + If the supplied argument is a --flagfile argument, read the contents + of the file and convert it to a .ini format config file. Return a + --config-file argument with the converted file. + + If the flag file contains more --flagfile arguments, multiple + --config-file arguments will be returned. + + The returned argument list may also contain None values which should + be filtered out later. + ''' + argp = [arg, next_arg] + flagfile = _get_flagfile(argp) + if not flagfile: + return argp + + args = _read_lines(flagfile) + + # + # We're recursing here to convert any --flagfile arguments + # read from this flagfile into --config-file arguments + # + # We don't actually include those --config-file arguments + # in the generated config file; instead we include all those + # --config-file args in the final command line + # + args = _iterate_args(args, _read_flagfile, tempdir=tempdir) + + config_file_args = [] + + (fd, tmpconf) = tempfile.mkstemp(suffix='.conf', dir=tempdir) + + with _open_fd_for_writing(fd, tmpconf) as f: + f.write('[DEFAULT]\n') + for arg in args: + if arg.startswith('--config-file='): + config_file_args.append(arg) + continue + if '=' in arg: + f.write(arg[2:] + '\n') + elif arg[2:].startswith('no'): + f.write(arg[4:] + '=false\n') + else: + f.write(arg[2:] + '=true\n') + + return ['--config-file=' + tmpconf] + argp[1:] + config_file_args + + +def _iterate_args(args, iterator, **kwargs): + '''Run an iterator function on the supplied args list. + + The iterator is passed the current arg and next arg and returns a + list of args. The returned args replace the suppied args in the + resulting args list. + + The iterator will be passed None for the next arg when processing + the last arg. + ''' + args.append(None) + + ret = [] + for i in range(len(args)): + if args[i] is None: # last item, or consumed file name + continue + + modified = iterator(args[i], args[i + 1], **kwargs) + args[i], args[i + 1] = modified[:2] + + ret.extend(modified[:1] + modified[2:]) # don't append next arg + + return filter(None, ret) + + +def handle_flagfiles(args, tempdir=None): + '''Replace --flagfile arguments with --config-file arguments. + + Replace any --flagfile argument in the supplied list with a --config-file + argument containing a temporary config file with the contents of the flag + file translated to .ini format. + + The tempdir argument is a directory which will be used to create temporary + files. + ''' + return _iterate_args(args[:], _read_flagfile, tempdir=tempdir) + + +@contextlib.contextmanager +def handle_flagfiles_managed(args): + '''A context manager for handle_flagfiles() which removes temp files. + + For use with the 'with' statement, i.e. + + with handle_flagfiles_managed(args) as args: + # Do stuff + # Any temporary fils have been removed + ''' + tempdir = tempfile.mkdtemp(prefix='nova-conf-') + try: + yield handle_flagfiles(args, tempdir=tempdir) + finally: + shutil.rmtree(tempdir) diff --git a/nova/flags.py b/nova/flags.py index 476255af..d39d00dc 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -32,6 +32,7 @@ import sys import gflags +from nova.compat import flagfile from nova.openstack.common import cfg @@ -46,62 +47,17 @@ class FlagValues(object): if self._update_default: self._update_default(self.name, default) - class ErrorCatcher: - def __init__(self, orig_error): - self.orig_error = orig_error - self.reset() - - def reset(self): - self._error_msg = None - - def catch(self, msg): - if ": --" in msg: - self._error_msg = msg - else: - self.orig_error(msg) - - def get_unknown_arg(self, args): - if not self._error_msg: - return None - # Error message is e.g. "no such option: --runtime_answer" - a = self._error_msg[self._error_msg.rindex(": --") + 2:] - return filter(lambda i: i == a or i.startswith(a + "="), args)[0] - def __init__(self): self._conf = cfg.ConfigOpts() self._conf.disable_interspersed_args() - self._opts = {} self.Reset() def _parse(self): if self._extra is not None: return - args = gflags.FlagValues().ReadFlagsFromFiles(self._args) - - extra = None - - # - # This horrendous hack allows us to stop optparse - # exiting when it encounters an unknown option - # - error_catcher = self.ErrorCatcher(self._conf._oparser.error) - self._conf._oparser.error = error_catcher.catch - try: - while True: - error_catcher.reset() - - extra = self._conf(args) - - unknown = error_catcher.get_unknown_arg(args) - if not unknown: - break - - args.remove(unknown) - finally: - self._conf._oparser.error = error_catcher.orig_error - - self._extra = extra + with flagfile.handle_flagfiles_managed(self._args) as args: + self._extra = self._conf(args) def __call__(self, argv): self.Reset() @@ -152,21 +108,16 @@ class FlagValues(object): return ret def add_option(self, opt): - if opt.dest in self._conf: - return - - self._opts[opt.dest] = opt - - try: - self._conf.register_cli_opts(self._opts.values()) - except cfg.ArgsAlreadyParsedError: - self._conf.reset() - self._conf.register_cli_opts(self._opts.values()) - self._extra = None + self._conf.register_opt(opt) def add_options(self, opts): - for opt in opts: - self.add_option(opt) + self._conf.register_opts(opts) + + def add_cli_option(self, opt): + self._conf.register_cli_opt(opt) + + def add_cli_options(self, opts): + self._conf.register_cli_opts(opts) FLAGS = FlagValues() @@ -195,6 +146,55 @@ def _get_my_ip(): return "127.0.0.1" +log_opts = [ + cfg.BoolOpt('verbose', + default=False, + help='show debug output'), + cfg.StrOpt('logdir', + default=None, + help='output to a per-service log file in named directory'), + cfg.StrOpt('logfile', + default=None, + help='output to named file'), + cfg.BoolOpt('use_syslog', + default=False, + help='output to syslog'), + cfg.BoolOpt('use_stderr', + default=True, + help='log to standard error'), + ] + +core_opts = [ + cfg.StrOpt('connection_type', + default=None, + help='libvirt, xenapi or fake'), + cfg.StrOpt('sql_connection', + default='sqlite:///$state_path/$sqlite_db', + help='connection string for sql database'), + cfg.StrOpt('api_paste_config', + default="api-paste.ini", + help='File name for the paste.deploy config for nova-api'), + cfg.StrOpt('state_path', + default=os.path.join(os.path.dirname(__file__), '../'), + help="Top-level directory for maintaining nova's state"), + cfg.StrOpt('lock_path', + default=os.path.join(os.path.dirname(__file__), '../'), + help='Directory for lock files'), + ] + +debug_opts = [ + cfg.BoolOpt('fake_network', + default=False, + help='should we use fake network devices and addresses'), + cfg.BoolOpt('fake_rabbit', + default=False, + help='use a fake rabbit'), +] + +FLAGS.add_cli_options(log_opts) +FLAGS.add_cli_options(core_opts) +FLAGS.add_cli_options(debug_opts) + global_opts = [ cfg.StrOpt('my_ip', default=_get_my_ip(), @@ -202,9 +202,6 @@ global_opts = [ cfg.ListOpt('region_list', default=[], help='list of region=fqdn pairs separated by commas'), - cfg.StrOpt('connection_type', - default=None, - help='libvirt, xenapi or fake'), cfg.StrOpt('aws_access_key_id', default='admin', help='AWS Access ID'), @@ -262,15 +259,6 @@ global_opts = [ cfg.StrOpt('vsa_topic', default='vsa', help='the topic that nova-vsa service listens on'), - cfg.BoolOpt('verbose', - default=False, - help='show debug output'), - cfg.BoolOpt('fake_rabbit', - default=False, - help='use a fake rabbit'), - cfg.BoolOpt('fake_network', - default=False, - help='should we use fake network devices and addresses'), cfg.StrOpt('rabbit_host', default='localhost', help='rabbit host'), @@ -377,15 +365,6 @@ global_opts = [ cfg.IntOpt('auth_token_ttl', default=3600, help='Seconds for auth tokens to linger'), - cfg.StrOpt('state_path', - default=os.path.join(os.path.dirname(__file__), '../'), - help="Top-level directory for maintaining nova's state"), - cfg.StrOpt('lock_path', - default=os.path.join(os.path.dirname(__file__), '../'), - help='Directory for lock files'), - cfg.StrOpt('logdir', - default=None, - help='output to a per-service log file in named directory'), cfg.StrOpt('logfile_mode', default='0644', help='Default file mode of the logs.'), @@ -395,9 +374,6 @@ global_opts = [ cfg.BoolOpt('sqlite_synchronous', default=True, help='Synchronous mode for sqlite'), - cfg.StrOpt('sql_connection', - default='sqlite:///$state_path/$sqlite_db', - help='connection string for sql database'), cfg.IntOpt('sql_idle_timeout', default=3600, help='timeout for idle sql database connections'), diff --git a/nova/log.py b/nova/log.py index c0bc256c..bd20e558 100644 --- a/nova/log.py +++ b/nova/log.py @@ -76,18 +76,9 @@ log_opts = [ 'eventlet.wsgi.server=WARN' ], help='list of logger=LEVEL pairs'), - cfg.BoolOpt('use_syslog', - default=False, - help='output to syslog'), cfg.BoolOpt('publish_errors', default=False, help='publish error events'), - cfg.StrOpt('logfile', - default=None, - help='output to named file'), - cfg.BoolOpt('use_stderr', - default=True, - help='log to standard error'), ] FLAGS = flags.FLAGS diff --git a/nova/tests/test_compat_flagfile.py b/nova/tests/test_compat_flagfile.py new file mode 100644 index 00000000..1635e832 --- /dev/null +++ b/nova/tests/test_compat_flagfile.py @@ -0,0 +1,174 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 Red Hat, Inc. +# +# 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. + +import contextlib +import os +import shutil +import StringIO +import stubout +import textwrap +import tempfile +import unittest +import uuid + +from nova.compat import flagfile + + +class ThatLastTwoPercentCoverageTestCase(unittest.TestCase): + + def test_open_file_for_reading(self): + with flagfile._open_file_for_reading(__file__): + pass + + def test_open_fd_for_writing(self): + (fd, path) = tempfile.mkstemp() + try: + with flagfile._open_fd_for_writing(fd, None): + pass + finally: + os.remove(path) + + +class CompatFlagfileTestCase(unittest.TestCase): + + def setUp(self): + self.stubs = stubout.StubOutForTesting() + self.files = {} + self.tempdir = str(uuid.uuid4()) + self.tempfiles = [] + + self.stubs.Set(flagfile, '_open_file_for_reading', self._fake_open) + self.stubs.Set(flagfile, '_open_fd_for_writing', self._fake_open) + self.stubs.Set(tempfile, 'mkdtemp', self._fake_mkdtemp) + self.stubs.Set(tempfile, 'mkstemp', self._fake_mkstemp) + self.stubs.Set(shutil, 'rmtree', self._fake_rmtree) + + def tearDown(self): + self.stubs.UnsetAll() + + def _fake_open(self, *args): + @contextlib.contextmanager + def managed_stringio(path): + if not path in self.files: + self.files[path] = "" + sio = StringIO.StringIO(textwrap.dedent(self.files[path])) + try: + yield sio + finally: + self.files[path] = sio.getvalue() + sio.close() + if len(args) == 2: + args = args[1:] # remove the fd arg for fdopen() case + return managed_stringio(args[0]) + + def _fake_mkstemp(self, *args, **kwargs): + self.assertTrue('dir' in kwargs) + self.assertEquals(kwargs['dir'], self.tempdir) + self.tempfiles.append(str(uuid.uuid4())) + return (None, self.tempfiles[-1]) + + def _fake_mkdtemp(self, *args, **kwargs): + return self.tempdir + + def _fake_rmtree(self, path): + self.assertEquals(self.tempdir, path) + self.tempdir = None + + def test_no_args(self): + before = [] + after = flagfile.handle_flagfiles(before, tempdir=self.tempdir) + self.assertEquals(after, before) + + def _do_test_empty_flagfile(self, before): + self.files['foo.flags'] = '' + after = flagfile.handle_flagfiles(before, tempdir=self.tempdir) + self.assertEquals(after, ['--config-file=' + self.tempfiles[-1]]) + self.assertEquals(self.files[self.tempfiles[-1]], '[DEFAULT]\n') + + def test_empty_flagfile(self): + self._do_test_empty_flagfile(['--flagfile=foo.flags']) + + def test_empty_flagfile_separated(self): + self._do_test_empty_flagfile(['--flagfile', 'foo.flags']) + + def test_empty_flagfile_single_hyphen(self): + self._do_test_empty_flagfile(['-flagfile=foo.flags']) + + def test_empty_flagfile_single_hyphen_separated_separated(self): + self._do_test_empty_flagfile(['-flagfile', 'foo.flags']) + + def test_empty_flagfile_with_other_args(self): + self.files['foo.flags'] = '' + + before = [ + '--foo', 'bar', + '--flagfile=foo.flags', + '--blaa=foo', + '--foo-flagfile', + '--flagfile-foo' + ] + + after = flagfile.handle_flagfiles(before, tempdir=self.tempdir) + + self.assertEquals(after, [ + '--foo', 'bar', + '--config-file=' + self.tempfiles[-1], + '--blaa=foo', + '--foo-flagfile', + '--flagfile-foo']) + self.assertEquals(self.files[self.tempfiles[-1]], '[DEFAULT]\n') + + def _do_test_flagfile(self, flags, conf): + self.files['foo.flags'] = flags + + before = ['--flagfile=foo.flags'] + + after = flagfile.handle_flagfiles(before, tempdir=self.tempdir) + + self.assertEquals(after, + ['--config-file=' + t + for t in reversed(self.tempfiles)]) + self.assertEquals(self.files[self.tempfiles[-1]], + '[DEFAULT]\n' + conf) + + def test_flagfile(self): + self._do_test_flagfile('--bar=foo', 'bar=foo\n') + + def test_boolean_flag(self): + self._do_test_flagfile('--verbose', 'verbose=true\n') + + def test_boolean_inverted_flag(self): + self._do_test_flagfile('--noverbose', 'verbose=false\n') + + def test_flagfile_comments(self): + self._do_test_flagfile('--bar=foo\n#foo\n--foo=bar\n//bar', + 'bar=foo\nfoo=bar\n') + + def test_flagfile_nested(self): + self.files['bar.flags'] = '--foo=bar' + + self._do_test_flagfile('--flagfile=bar.flags', '') + + self.assertEquals(self.files[self.tempfiles[-2]], + '[DEFAULT]\nfoo=bar\n') + + def test_flagfile_managed(self): + self.files['foo.flags'] = '' + before = ['--flagfile=foo.flags'] + with flagfile.handle_flagfiles_managed(before) as after: + self.assertEquals(after, ['--config-file=' + self.tempfiles[-1]]) + self.assertEquals(self.files[self.tempfiles[-1]], '[DEFAULT]\n') + self.assertTrue(self.tempdir is None) diff --git a/nova/tests/test_flags.py b/nova/tests/test_flags.py index bea34007..48aa965a 100644 --- a/nova/tests/test_flags.py +++ b/nova/tests/test_flags.py @@ -30,6 +30,17 @@ FLAGS.add_option(cfg.StrOpt('flags_unittest', default='foo', help='for testing purposes only')) +test_opts = [ + cfg.StrOpt('string', default='default', help='desc'), + cfg.IntOpt('int', default=1, help='desc'), + cfg.BoolOpt('false', default=False, help='desc'), + cfg.BoolOpt('true', default=True, help='desc'), + ] + +float_opt = cfg.FloatOpt('float', default=6.66, help='desc') +multistr_opt = cfg.MultiStrOpt('multi', default=['blaa'], help='desc') +list_opt = cfg.ListOpt('list', default=['foo'], help='desc') + class FlagsTestCase(test.TestCase): @@ -39,16 +50,7 @@ class FlagsTestCase(test.TestCase): self.global_FLAGS = flags.FLAGS def test_define(self): - self.assert_('string' not in self.FLAGS) - self.assert_('int' not in self.FLAGS) - self.assert_('false' not in self.FLAGS) - self.assert_('true' not in self.FLAGS) - - self.FLAGS.add_option(cfg.StrOpt('string', - default='default', help='desc')) - self.FLAGS.add_option(cfg.IntOpt('int', default=1, help='desc')) - self.FLAGS.add_option(cfg.BoolOpt('false', default=False, help='desc')) - self.FLAGS.add_option(cfg.BoolOpt('true', default=True, help='desc')) + self.FLAGS.add_cli_options(test_opts) self.assert_(self.FLAGS['string']) self.assert_(self.FLAGS['int']) @@ -72,12 +74,12 @@ class FlagsTestCase(test.TestCase): self.assertEqual(self.FLAGS.true, False) def test_define_float(self): - self.FLAGS.add_option(cfg.FloatOpt('float', default=6.66, help='desc')) + self.FLAGS.add_cli_options(test_opts) + self.FLAGS.add_option(float_opt) self.assertEqual(self.FLAGS.float, 6.66) def test_define_multistring(self): - self.FLAGS.add_option(cfg.MultiStrOpt('multi', - default=['blaa'], help='desc')) + self.FLAGS.add_cli_option(multistr_opt) self.assert_(self.FLAGS['multi']) self.assertEqual(self.FLAGS.multi, ['blaa']) @@ -87,13 +89,8 @@ class FlagsTestCase(test.TestCase): self.assertEqual(self.FLAGS.multi, ['foo', 'bar']) - # Re-parse to test multistring isn't append multiple times - self.FLAGS(argv + ['--unknown1', '--unknown2']) - self.assertEqual(self.FLAGS.multi, ['foo', 'bar']) - def test_define_list(self): - self.FLAGS.add_option(cfg.ListOpt('list', - default=['foo'], help='desc')) + self.FLAGS.add_cli_option(list_opt) self.assert_(self.FLAGS['list']) self.assertEqual(self.FLAGS.list, ['foo']) @@ -104,11 +101,11 @@ class FlagsTestCase(test.TestCase): self.assertEqual(self.FLAGS.list, ['a', 'b', 'c', 'd']) def test_error(self): - self.FLAGS.add_option(cfg.IntOpt('error', default=1, help='desc')) + self.FLAGS.add_cli_option(float_opt) - self.assertEqual(self.FLAGS.error, 1) + self.assertEqual(self.FLAGS.float, 6.66) - argv = ['flags_test', '--error=foo'] + argv = ['flags_test', '--float=foo'] self.assertRaises(exceptions.SystemExit, self.FLAGS, argv) def test_declare(self): @@ -133,30 +130,26 @@ class FlagsTestCase(test.TestCase): def test_runtime_and_unknown_flags(self): self.assert_('runtime_answer' not in self.global_FLAGS) - - argv = ['flags_test', '--runtime_answer=60', 'extra_arg'] - args = self.global_FLAGS(argv) - self.assertEqual(len(args), 2) - self.assertEqual(args[1], 'extra_arg') - - self.assert_('runtime_answer' not in self.global_FLAGS) - import nova.tests.runtime_flags - self.assert_('runtime_answer' in self.global_FLAGS) - self.assertEqual(self.global_FLAGS.runtime_answer, 60) + self.assertEqual(self.global_FLAGS.runtime_answer, 54) def test_long_vs_short_flags(self): - self.global_FLAGS.add_option(cfg.StrOpt('duplicate_answer_long', - default='val', help='desc')) + self.global_FLAGS.Reset() + self.global_FLAGS.add_cli_option(cfg.StrOpt('duplicate_answer_long', + default='val', + help='desc')) argv = ['flags_test', '--duplicate_answer=60', 'extra_arg'] args = self.global_FLAGS(argv) self.assert_('duplicate_answer' not in self.global_FLAGS) self.assert_(self.global_FLAGS.duplicate_answer_long, 60) - self.global_FLAGS.add_option(cfg.IntOpt('duplicate_answer', - default=60, help='desc')) + self.global_FLAGS.Reset() + self.global_FLAGS.add_cli_option(cfg.IntOpt('duplicate_answer', + default=60, + help='desc')) + args = self.global_FLAGS(argv) self.assertEqual(self.global_FLAGS.duplicate_answer, 60) self.assertEqual(self.global_FLAGS.duplicate_answer_long, 'val') @@ -182,19 +175,14 @@ class FlagsTestCase(test.TestCase): self.assertEqual(FLAGS.FlagValuesDict()['flags_unittest'], 'foo') def test_flagfile(self): - self.FLAGS.add_option(cfg.StrOpt('string', - default='default', help='desc')) - self.FLAGS.add_option(cfg.IntOpt('int', default=1, help='desc')) - self.FLAGS.add_option(cfg.BoolOpt('false', default=False, help='desc')) - self.FLAGS.add_option(cfg.BoolOpt('true', default=True, help='desc')) - self.FLAGS.add_option(cfg.MultiStrOpt('multi', - default=['blaa'], help='desc')) + self.FLAGS.add_options(test_opts) + self.FLAGS.add_option(multistr_opt) (fd, path) = tempfile.mkstemp(prefix='nova', suffix='.flags') try: os.write(fd, '--string=foo\n--int=2\n--false\n--notrue\n') - os.write(fd, '--multi=foo\n--multi=bar\n') + os.write(fd, '--multi=foo\n') # FIXME(markmc): --multi=bar\n') os.close(fd) self.FLAGS(['flags_test', '--flagfile=' + path]) @@ -203,11 +191,11 @@ class FlagsTestCase(test.TestCase): self.assertEqual(self.FLAGS.int, 2) self.assertEqual(self.FLAGS.false, True) self.assertEqual(self.FLAGS.true, False) - self.assertEqual(self.FLAGS.multi, ['foo', 'bar']) + self.assertEqual(self.FLAGS.multi, ['foo']) # FIXME(markmc): 'bar' # Re-parse to test multistring isn't append multiple times self.FLAGS(['flags_test', '--flagfile=' + path]) - self.assertEqual(self.FLAGS.multi, ['foo', 'bar']) + self.assertEqual(self.FLAGS.multi, ['foo']) # FIXME(markmc): 'bar' finally: os.remove(path)