From 068784afab28631b2a8fcd5dbc55a777508f63b6 Mon Sep 17 00:00:00 2001 From: Alexis Lee Date: Tue, 2 Feb 2016 16:19:20 +0000 Subject: [PATCH] Fix unrecognized subparser args Someone thought a transient attr would be a clever way to smuggle data from a _SubParsersAction up to the main ArgumentsParser. Unfortunately they use a really weird way to set the attr: vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) This bypasses __setattr__ so when they try to delattr later, __delattr__ can't find it and bombs. Even though the delattr is protected by a hasattr, because hasattr uses getattr which checks the private object dict first before calling __getattr__. Handle this by falling back to super in __getattr__ and __delattr__. Change-Id: Id89a0c00fd32b6580d891ccf69d58fa17f11ec49 Closes-Bug: #1540959 --- oslo_config/cfg.py | 6 ++---- oslo_config/tests/test_cfg.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/oslo_config/cfg.py b/oslo_config/cfg.py index 39615cae..e5edaa8e 100644 --- a/oslo_config/cfg.py +++ b/oslo_config/cfg.py @@ -1776,15 +1776,13 @@ class _Namespace(argparse.Namespace): try: return self._cli[name] except KeyError: - raise AttributeError( - "'_Namespace' object has no attribute '%s'" % name) + return super(_Namespace, self).__getattr__(name) def __delattr__(self, name): try: del self._cli[name] except KeyError: - raise AttributeError( - "'_Namespace' object has no attribute '%s'" % name) + return super(_Namespace, self).__delattr__(name) def _parse_cli_opts_from_config_file(self, sections, normalized): """Parse CLI options from a config file. diff --git a/oslo_config/tests/test_cfg.py b/oslo_config/tests/test_cfg.py index 037d6227..92637ed5 100644 --- a/oslo_config/tests/test_cfg.py +++ b/oslo_config/tests/test_cfg.py @@ -3457,6 +3457,18 @@ class NamespaceTestCase(BaseTestCase): self.assertRaises(AttributeError, self.ns.__getattr__, 'boo') self.assertRaises(AttributeError, self.ns.__delattr__, 'boo') + def test_attrs_subparser(self): + CONF = cfg.ConfigOpts() + CONF.register_cli_opt(cfg.SubCommandOpt( + 'foo', handler=lambda sub: sub.add_parser('foo'))) + CONF(['foo']) + + def test_attrs_subparser_failure(self): + CONF = cfg.ConfigOpts() + CONF.register_cli_opt(cfg.SubCommandOpt( + 'foo', handler=lambda sub: sub.add_parser('foo'))) + self.assertRaises(SystemExit, CONF, ['foo', 'bar']) + class TildeExpansionTestCase(BaseTestCase):