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
This commit is contained in:
Alexis Lee 2016-02-02 16:19:20 +00:00
parent ea53c55864
commit 068784afab
2 changed files with 14 additions and 4 deletions

View File

@ -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.

View File

@ -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):