"""Unit tests for flake.options.manager.OptionManager.""" import argparse import os from unittest import mock import pytest from flake8 import utils from flake8.main.options import JobsArgument from flake8.options import manager TEST_VERSION = "3.0.0b1" @pytest.fixture def optmanager(): """Generate a simple OptionManager with default test arguments.""" return manager.OptionManager(prog="flake8", version=TEST_VERSION) def test_option_manager_creates_option_parser(optmanager): """Verify that a new manager creates a new parser.""" assert isinstance(optmanager.parser, argparse.ArgumentParser) def test_option_manager_including_parent_options(): """Verify parent options are included in the parsed options.""" # GIVEN parent_parser = argparse.ArgumentParser(add_help=False) parent_parser.add_argument("--parent") # WHEN optmanager = manager.OptionManager( prog="flake8", version=TEST_VERSION, parents=[parent_parser] ) option, _ = optmanager.parse_args(["--parent", "foo"]) # THEN assert option.parent == "foo" def test_parse_args_forwarding_default_values(optmanager): """Verify default provided values are present in the final result.""" namespace = argparse.Namespace(foo="bar") options, args = optmanager.parse_args([], namespace) assert options.foo == "bar" def test_parse_args_forwarding_type_coercion(optmanager): """Verify default provided values are type converted from add_option.""" optmanager.add_option("--foo", type=int) namespace = argparse.Namespace(foo="5") options, args = optmanager.parse_args([], namespace) assert options.foo == 5 def test_add_option_short_option_only(optmanager): """Verify the behaviour of adding a short-option only.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option("-s", help="Test short opt") assert optmanager.options[0].short_option_name == "-s" def test_add_option_long_option_only(optmanager): """Verify the behaviour of adding a long-option only.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option("--long", help="Test long opt") assert optmanager.options[0].short_option_name is manager._ARG.NO assert optmanager.options[0].long_option_name == "--long" def test_add_short_and_long_option_names(optmanager): """Verify the behaviour of using both short and long option names.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option("-b", "--both", help="Test both opts") assert optmanager.options[0].short_option_name == "-b" assert optmanager.options[0].long_option_name == "--both" def test_add_option_with_custom_args(optmanager): """Verify that add_option handles custom Flake8 parameters.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option("--parse", parse_from_config=True) optmanager.add_option("--commas", comma_separated_list=True) optmanager.add_option("--files", normalize_paths=True) attrs = ["parse_from_config", "comma_separated_list", "normalize_paths"] for option, attr in zip(optmanager.options, attrs): assert getattr(option, attr) is True def test_parse_args_normalize_path(optmanager): """Show that parse_args handles path normalization.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option("--config", normalize_paths=True) options, args = optmanager.parse_args(["--config", "../config.ini"]) assert options.config == os.path.abspath("../config.ini") def test_parse_args_handles_comma_separated_defaults(optmanager): """Show that parse_args handles defaults that are comma-separated.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option( "--exclude", default="E123,W234", comma_separated_list=True ) options, args = optmanager.parse_args([]) assert options.exclude == ["E123", "W234"] def test_parse_args_handles_comma_separated_lists(optmanager): """Show that parse_args handles user-specified comma-separated lists.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option( "--exclude", default="E123,W234", comma_separated_list=True ) options, args = optmanager.parse_args(["--exclude", "E201,W111,F280"]) assert options.exclude == ["E201", "W111", "F280"] def test_parse_args_normalize_paths(optmanager): """Verify parse_args normalizes a comma-separated list of paths.""" assert optmanager.options == [] assert optmanager.config_options_dict == {} optmanager.add_option( "--extra-config", normalize_paths=True, comma_separated_list=True ) options, args = optmanager.parse_args( ["--extra-config", "../config.ini,tox.ini,flake8/some-other.cfg"] ) assert options.extra_config == [ os.path.abspath("../config.ini"), "tox.ini", os.path.abspath("flake8/some-other.cfg"), ] def test_generate_versions(optmanager): """Verify a comma-separated string is generated of registered plugins.""" optmanager.registered_plugins = [ manager.PluginVersion("Testing 100", "0.0.0", False), manager.PluginVersion("Testing 101", "0.0.0", False), manager.PluginVersion("Testing 300", "0.0.0", True), ] assert ( optmanager.generate_versions() == "Testing 100: 0.0.0, Testing 101: 0.0.0, Testing 300: 0.0.0" ) def test_plugins_are_sorted_in_generate_versions(optmanager): """Verify we sort before joining strings in generate_versions.""" optmanager.registered_plugins = [ manager.PluginVersion("pyflakes", "1.5.0", False), manager.PluginVersion("mccabe", "0.7.0", False), manager.PluginVersion("pycodestyle", "2.2.0", False), manager.PluginVersion("flake8-docstrings", "0.6.1", False), manager.PluginVersion("flake8-bugbear", "2016.12.1", False), ] assert ( optmanager.generate_versions() == "flake8-bugbear: 2016.12.1, " "flake8-docstrings: 0.6.1, " "mccabe: 0.7.0, " "pycodestyle: 2.2.0, " "pyflakes: 1.5.0" ) def test_generate_versions_with_format_string(optmanager): """Verify a comma-separated string is generated of registered plugins.""" optmanager.registered_plugins.update( [ manager.PluginVersion("Testing", "0.0.0", False), manager.PluginVersion("Testing", "0.0.0", False), manager.PluginVersion("Testing", "0.0.0", False), ] ) assert optmanager.generate_versions() == "Testing: 0.0.0" def test_update_version_string(optmanager): """Verify we update the version string idempotently.""" assert optmanager.version == TEST_VERSION assert optmanager.version_action.version == TEST_VERSION optmanager.registered_plugins = [ manager.PluginVersion("Testing 100", "0.0.0", False), manager.PluginVersion("Testing 101", "0.0.0", False), manager.PluginVersion("Testing 300", "0.0.0", False), ] optmanager.update_version_string() assert optmanager.version == TEST_VERSION assert ( optmanager.version_action.version == TEST_VERSION + " (Testing 100: 0.0.0, Testing 101: 0.0.0, Testing 300: 0.0.0) " + utils.get_python_version() ) def test_generate_epilog(optmanager): """Verify how we generate the epilog for help text.""" assert optmanager.parser.epilog is None optmanager.registered_plugins = [ manager.PluginVersion("Testing 100", "0.0.0", False), manager.PluginVersion("Testing 101", "0.0.0", False), manager.PluginVersion("Testing 300", "0.0.0", False), ] expected_value = ( "Installed plugins: Testing 100: 0.0.0, Testing 101: 0.0.0, Testing" " 300: 0.0.0" ) optmanager.generate_epilog() assert optmanager.parser.epilog == expected_value def test_extend_default_ignore(optmanager): """Verify that we update the extended default ignore list.""" assert optmanager.extended_default_ignore == set() optmanager.extend_default_ignore(["T100", "T101", "T102"]) assert optmanager.extended_default_ignore == {"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 def test_optparse_normalize_callback_option_legacy(optmanager): """Test the optparse shim for `callback=`.""" callback_foo = mock.Mock() optmanager.add_option( "--foo", action="callback", callback=callback_foo, callback_args=(1, 2), callback_kwargs={"a": "b"}, ) callback_bar = mock.Mock() optmanager.add_option( "--bar", action="callback", type="string", callback=callback_bar, ) callback_baz = mock.Mock() optmanager.add_option( "--baz", action="callback", type="string", nargs=2, callback=callback_baz, ) optmanager.parse_args(["--foo", "--bar", "bararg", "--baz", "1", "2"]) callback_foo.assert_called_once_with( mock.ANY, # the option / action instance "--foo", None, mock.ANY, # the OptionParser / ArgumentParser 1, 2, a="b", ) callback_bar.assert_called_once_with( mock.ANY, # the option / action instance "--bar", "bararg", mock.ANY, # the OptionParser / ArgumentParser ) callback_baz.assert_called_once_with( mock.ANY, # the option / action instance "--baz", ("1", "2"), mock.ANY, # the OptionParser / ArgumentParser ) @pytest.mark.parametrize( ("type_s", "input_val", "expected"), ( ("int", "5", 5), ("long", "6", 6), ("string", "foo", "foo"), ("float", "1.5", 1.5), ("complex", "1+5j", 1 + 5j), # optparse allows this but does not document it ("str", "foo", "foo"), ), ) def test_optparse_normalize_types(optmanager, type_s, input_val, expected): """Test the optparse shim for type="typename".""" optmanager.add_option("--foo", type=type_s) opts, args = optmanager.parse_args(["--foo", input_val]) assert opts.foo == expected def test_optparse_normalize_choice_type(optmanager): """Test the optparse shim for type="choice".""" optmanager.add_option("--foo", type="choice", choices=("1", "2", "3")) opts, args = optmanager.parse_args(["--foo", "1"]) assert opts.foo == "1" # fails to parse with pytest.raises(SystemExit): optmanager.parse_args(["--foo", "4"]) def test_optparse_normalize_help(optmanager, capsys): """Test the optparse shim for %default in help text.""" optmanager.add_option("--foo", default="bar", help="default: %default") with pytest.raises(SystemExit): optmanager.parse_args(["--help"]) out, err = capsys.readouterr() output = out + err assert "default: bar" in output def test_optmanager_group(optmanager, capsys): """Test that group(...) causes options to be assigned to a group.""" with optmanager.group("groupname"): optmanager.add_option("--foo") with pytest.raises(SystemExit): optmanager.parse_args(["--help"]) out, err = capsys.readouterr() output = out + err assert "\ngroupname:\n" in output @pytest.mark.parametrize( ("s", "is_auto", "n_jobs"), ( ("auto", True, -1), ("4", False, 4), ), ) def test_parse_valid_jobs_argument(s, is_auto, n_jobs): """Test that --jobs properly parses valid arguments.""" jobs_opt = JobsArgument(s) assert is_auto == jobs_opt.is_auto assert n_jobs == jobs_opt.n_jobs def test_parse_invalid_jobs_argument(optmanager, capsys): """Test that --jobs properly rejects invalid arguments.""" namespace = argparse.Namespace() optmanager.add_option("--jobs", type=JobsArgument) with pytest.raises(SystemExit): optmanager.parse_args(["--jobs=foo"], namespace) out, err = capsys.readouterr() output = out + err expected = ( "\nflake8: error: argument --jobs: " "'foo' must be 'auto' or an integer.\n" ) assert expected in output def test_jobs_argument_str(): """Test that JobsArgument has a correct __str__.""" assert str(JobsArgument("auto")) == "auto" assert str(JobsArgument("123")) == "123"