From 73be9b0e90b187dd7e9533cdd0c287a3d40cb1ec Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sat, 16 Jul 2016 10:07:19 -0500 Subject: [PATCH] Add OptionManager#parse_known_args If a user specified `--max-complexity` on the command-line, they would be told that it did not exist. The same would be true of any option provided by a plugin. This is because we parse the command-line arguments twice in Flake8 -- the first time to specify the verbosity and destination for logging, the second time to actually execute Flake8. Since plugin options are not registered to start with the first time, they are not valid options. So when we first parse the options, we should only attempt to parse the ones which we know about. Closes #168 --- src/flake8/main/application.py | 2 +- src/flake8/options/manager.py | 40 ++++++++++++++++++++++++++++--- tests/unit/test_option_manager.py | 9 +++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index c7a9741..febe6c7 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -68,7 +68,7 @@ class Application(object): except ValueError: pass - preliminary_opts, _ = self.option_manager.parse_args(args) + preliminary_opts, _ = self.option_manager.parse_known_args(args) # Set the verbosity of the program flake8.configure_logging(preliminary_opts.verbose, preliminary_opts.output_file) diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py index f9d9e26..58d8b2d 100644 --- a/src/flake8/options/manager.py +++ b/src/flake8/options/manager.py @@ -261,15 +261,49 @@ class OptionManager(object): plugin_version_format ) + def _normalize(self, options): + for option in self.options: + old_value = getattr(options, option.dest) + setattr(options, option.dest, option.normalize(old_value)) + def parse_args(self, args=None, values=None): """Simple proxy to calling the OptionParser's parse_args method.""" self.generate_epilog() self.update_version_string() options, xargs = self.parser.parse_args(args, values) - for option in self.options: - old_value = getattr(options, option.dest) - setattr(options, option.dest, option.normalize(old_value)) + self._normalize(options) + return options, xargs + def parse_known_args(self, args=None, values=None): + """Parse only the known arguments from the argument values. + + Replicate a little argparse behaviour while we're still on + optparse. + """ + self.generate_epilog() + self.update_version_string() + # Taken from optparse.OptionParser.parse_args + rargs = self.parser._get_args(args) + if values is None: + values = self.parser.get_default_values() + + self.parser.rargs = rargs + self.parser.largs = largs = [] + self.parser.values = values + + while rargs: + # NOTE(sigmavirus24): If we only care about *known* options, then + # we should just shift the bad option over to the largs list and + # carry on. + # Unfortunately, we need to rely on a private method here. + try: + self.parser._process_args(largs, rargs, values) + except (optparse.BadOptionError, optparse.OptionValueError) as err: + self.parser.largs.append(err.opt_str) + + args = largs + rargs + options, xargs = self.parser.check_values(values, args) + self._normalize(options) return options, xargs def register_plugin(self, name, version): diff --git a/tests/unit/test_option_manager.py b/tests/unit/test_option_manager.py index 6f6cb17..661b445 100644 --- a/tests/unit/test_option_manager.py +++ b/tests/unit/test_option_manager.py @@ -2,6 +2,7 @@ import optparse import os +import mock import pytest from flake8 import utils @@ -194,3 +195,11 @@ def test_extend_default_ignore(optmanager): assert optmanager.extended_default_ignore == set(['T100', 'T101', 'T102']) + + +def test_parse_known_args(optmanager): + """Verify we ignore unknown options.""" + with mock.patch('sys.exit') as sysexit: + optmanager.parse_known_args(['--max-complexity', '5']) + + assert sysexit.called is False