mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-31 19:26:52 +00:00
have OptionManager take plugin versions directly
This commit is contained in:
parent
fed77cd60a
commit
f98d52a398
9 changed files with 73 additions and 255 deletions
|
|
@ -113,8 +113,6 @@ disallow_untyped_defs = false
|
|||
disallow_untyped_defs = false
|
||||
[mypy-flake8.main.application]
|
||||
disallow_untyped_defs = false
|
||||
[mypy-flake8.main.debug]
|
||||
disallow_untyped_defs = false
|
||||
[mypy-flake8.plugins.manager]
|
||||
disallow_untyped_defs = false
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ LOG = logging.getLogger(__name__)
|
|||
class Application:
|
||||
"""Abstract our application into a class."""
|
||||
|
||||
def __init__(self, program="flake8", version=flake8.__version__):
|
||||
def __init__(self) -> None:
|
||||
"""Initialize our application.
|
||||
|
||||
:param str program:
|
||||
|
|
@ -49,21 +49,12 @@ class Application:
|
|||
self.start_time = time.time()
|
||||
#: The timestamp when the Application finished reported errors.
|
||||
self.end_time: Optional[float] = None
|
||||
#: The name of the program being run
|
||||
self.program = program
|
||||
#: The version of the program being run
|
||||
self.version = version
|
||||
#: The prelimary argument parser for handling options required for
|
||||
#: obtaining and parsing the configuration file.
|
||||
self.prelim_arg_parser = options.stage1_arg_parser()
|
||||
#: The instance of :class:`flake8.options.manager.OptionManager` used
|
||||
#: to parse and handle the options and arguments passed by the user
|
||||
self.option_manager = manager.OptionManager(
|
||||
prog="flake8",
|
||||
version=flake8.__version__,
|
||||
parents=[self.prelim_arg_parser],
|
||||
)
|
||||
options.register_default_options(self.option_manager)
|
||||
self.option_manager: Optional[manager.OptionManager] = None
|
||||
|
||||
#: The instance of :class:`flake8.plugins.manager.Checkers`
|
||||
self.check_plugins: Optional[plugin_manager.Checkers] = None
|
||||
|
|
@ -166,9 +157,19 @@ class Application:
|
|||
def register_plugin_options(self) -> None:
|
||||
"""Register options provided by plugins to our option manager."""
|
||||
assert self.check_plugins is not None
|
||||
self.check_plugins.register_options(self.option_manager)
|
||||
self.check_plugins.register_plugin_versions(self.option_manager)
|
||||
assert self.formatting_plugins is not None
|
||||
|
||||
versions = sorted(set(self.check_plugins.manager.versions()))
|
||||
self.option_manager = manager.OptionManager(
|
||||
version=flake8.__version__,
|
||||
plugin_versions=", ".join(
|
||||
f"{name}: {version}" for name, version in versions
|
||||
),
|
||||
parents=[self.prelim_arg_parser],
|
||||
)
|
||||
options.register_default_options(self.option_manager)
|
||||
|
||||
self.check_plugins.register_options(self.option_manager)
|
||||
self.formatting_plugins.register_options(self.option_manager)
|
||||
|
||||
def parse_configuration_and_cli(
|
||||
|
|
@ -178,6 +179,7 @@ class Application:
|
|||
argv: List[str],
|
||||
) -> None:
|
||||
"""Parse configuration files and the CLI options."""
|
||||
assert self.option_manager is not None
|
||||
self.options = aggregator.aggregate_options(
|
||||
self.option_manager,
|
||||
cfg,
|
||||
|
|
@ -186,7 +188,8 @@ class Application:
|
|||
)
|
||||
|
||||
if self.options.bug_report:
|
||||
info = debug.information(self.option_manager)
|
||||
assert self.check_plugins is not None
|
||||
info = debug.information(flake8.__version__, self.check_plugins)
|
||||
print(json.dumps(info, indent=2, sort_keys=True))
|
||||
raise SystemExit(0)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
"""Module containing the logic for our debugging logic."""
|
||||
import platform
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
|
||||
from flake8.plugins.manager import PluginTypeManager
|
||||
|
||||
|
||||
def information(option_manager):
|
||||
def information(
|
||||
version: str,
|
||||
plugins: PluginTypeManager,
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate the information to be printed for the bug report."""
|
||||
return {
|
||||
"version": option_manager.version,
|
||||
"plugins": plugins_from(option_manager),
|
||||
"version": version,
|
||||
"plugins": plugins_from(plugins),
|
||||
"platform": {
|
||||
"python_implementation": platform.python_implementation(),
|
||||
"python_version": platform.python_version(),
|
||||
|
|
@ -15,13 +23,9 @@ def information(option_manager):
|
|||
}
|
||||
|
||||
|
||||
def plugins_from(option_manager):
|
||||
def plugins_from(plugins: PluginTypeManager) -> List[Dict[str, str]]:
|
||||
"""Generate the list of plugins installed."""
|
||||
return [
|
||||
{
|
||||
"plugin": plugin.name,
|
||||
"version": plugin.version,
|
||||
"is_local": plugin.local,
|
||||
}
|
||||
for plugin in sorted(option_manager.registered_plugins)
|
||||
{"plugin": name, "version": version}
|
||||
for name, version in sorted(set(plugins.manager.versions()))
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
"""Option handling and Option management logic."""
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
import enum
|
||||
import functools
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
|
|
@ -316,20 +314,15 @@ class Option:
|
|||
return self.option_args, self.filtered_option_kwargs
|
||||
|
||||
|
||||
PluginVersion = collections.namedtuple(
|
||||
"PluginVersion", ["name", "version", "local"]
|
||||
)
|
||||
|
||||
|
||||
class OptionManager:
|
||||
"""Manage Options and OptionParser while adding post-processing."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
prog: str,
|
||||
*,
|
||||
version: str,
|
||||
usage: str = "%(prog)s [options] file file ...",
|
||||
parents: Optional[List[argparse.ArgumentParser]] = None,
|
||||
plugin_versions: str,
|
||||
parents: List[argparse.ArgumentParser],
|
||||
) -> None:
|
||||
"""Initialize an instance of an OptionManager.
|
||||
|
||||
|
|
@ -343,28 +336,28 @@ class OptionManager:
|
|||
A list of ArgumentParser objects whose arguments should also be
|
||||
included.
|
||||
"""
|
||||
if parents is None:
|
||||
parents = []
|
||||
|
||||
self.parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
||||
prog=prog, usage=usage, parents=parents
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="flake8",
|
||||
usage="%(prog)s [options] file file ...",
|
||||
parents=parents,
|
||||
epilog=f"Installed plugins: {plugin_versions}",
|
||||
)
|
||||
self._current_group: Optional[argparse._ArgumentGroup] = None
|
||||
self.version_action = cast(
|
||||
"argparse._VersionAction",
|
||||
self.parser.add_argument(
|
||||
"--version", action="version", version=version
|
||||
self.parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=(
|
||||
f"{version} ({plugin_versions}) "
|
||||
f"{utils.get_python_version()}"
|
||||
),
|
||||
)
|
||||
self.parser.add_argument("filenames", nargs="*", metavar="filename")
|
||||
self.config_options_dict: Dict[str, Option] = {}
|
||||
self.options: List[Option] = []
|
||||
self.program_name = prog
|
||||
self.version = version
|
||||
self.registered_plugins: Set[PluginVersion] = set()
|
||||
self.extended_default_ignore: Set[str] = set()
|
||||
self.extended_default_select: Set[str] = set()
|
||||
|
||||
self._current_group: Optional[argparse._ArgumentGroup] = None
|
||||
|
||||
@contextlib.contextmanager
|
||||
def group(self, name: str) -> Generator[None, None, None]:
|
||||
"""Attach options to an argparse group during this context."""
|
||||
|
|
@ -395,7 +388,7 @@ class OptionManager:
|
|||
self.options.append(option)
|
||||
if option.parse_from_config:
|
||||
name = option.config_name
|
||||
assert name is not None # nosec (for mypy)
|
||||
assert name is not None
|
||||
self.config_options_dict[name] = option
|
||||
self.config_options_dict[name.replace("_", "-")] = option
|
||||
LOG.debug('Registered option "%s".', option)
|
||||
|
|
@ -438,63 +431,12 @@ class OptionManager:
|
|||
LOG.debug("Extending default select list with %r", error_codes)
|
||||
self.extended_default_select.update(error_codes)
|
||||
|
||||
def generate_versions(
|
||||
self, format_str: str = "%(name)s: %(version)s", join_on: str = ", "
|
||||
) -> str:
|
||||
"""Generate a comma-separated list of versions of plugins."""
|
||||
return join_on.join(
|
||||
format_str % plugin._asdict()
|
||||
for plugin in sorted(self.registered_plugins)
|
||||
)
|
||||
|
||||
def update_version_string(self) -> None:
|
||||
"""Update the flake8 version string."""
|
||||
self.version_action.version = "{} ({}) {}".format(
|
||||
self.version, self.generate_versions(), utils.get_python_version()
|
||||
)
|
||||
|
||||
def generate_epilog(self) -> None:
|
||||
"""Create an epilog with the version and name of each of plugin."""
|
||||
plugin_version_format = "%(name)s: %(version)s"
|
||||
self.parser.epilog = "Installed plugins: " + self.generate_versions(
|
||||
plugin_version_format
|
||||
)
|
||||
|
||||
def parse_args(
|
||||
self,
|
||||
args: Optional[Sequence[str]] = None,
|
||||
values: Optional[argparse.Namespace] = None,
|
||||
) -> argparse.Namespace:
|
||||
"""Proxy to calling the OptionParser's parse_args method."""
|
||||
self.generate_epilog()
|
||||
self.update_version_string()
|
||||
if values:
|
||||
self.parser.set_defaults(**vars(values))
|
||||
return self.parser.parse_args(args)
|
||||
|
||||
def parse_known_args(
|
||||
self, args: Optional[List[str]] = None
|
||||
) -> Tuple[argparse.Namespace, List[str]]:
|
||||
"""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()
|
||||
return self.parser.parse_known_args(args)
|
||||
|
||||
def register_plugin(
|
||||
self, name: str, version: str, local: bool = False
|
||||
) -> None:
|
||||
"""Register a plugin relying on the OptionManager.
|
||||
|
||||
:param str name:
|
||||
The name of the checker itself. This will be the ``name``
|
||||
attribute of the class or function loaded from the entry-point.
|
||||
:param str version:
|
||||
The version of the checker that we're using.
|
||||
:param bool local:
|
||||
Whether the plugin is local to the project/repository or not.
|
||||
"""
|
||||
self.registered_plugins.add(PluginVersion(name, version, local))
|
||||
|
|
|
|||
|
|
@ -427,12 +427,6 @@ class PluginTypeManager:
|
|||
# Do not set plugins_loaded if we run into an exception
|
||||
self.plugins_loaded = True
|
||||
|
||||
def register_plugin_versions(self, optmanager):
|
||||
"""Register the plugins and their versions with the OptionManager."""
|
||||
self.load_plugins()
|
||||
for (plugin_name, version) in self.manager.versions():
|
||||
optmanager.register_plugin(name=plugin_name, version=version)
|
||||
|
||||
def register_options(self, optmanager):
|
||||
"""Register all of the checkers' options to the OptionManager."""
|
||||
self.load_plugins()
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ from flake8.options import manager
|
|||
def optmanager():
|
||||
"""Create a new OptionManager."""
|
||||
option_manager = manager.OptionManager(
|
||||
prog="flake8",
|
||||
version="3.0.0",
|
||||
plugin_versions="",
|
||||
parents=[],
|
||||
)
|
||||
options.register_default_options(option_manager)
|
||||
return option_manager
|
||||
|
|
|
|||
|
|
@ -4,58 +4,24 @@ from unittest import mock
|
|||
import pytest
|
||||
|
||||
from flake8.main import debug
|
||||
from flake8.options import manager
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"plugins, expected",
|
||||
[
|
||||
("versions", "expected"),
|
||||
(
|
||||
([], []),
|
||||
(
|
||||
[manager.PluginVersion("pycodestyle", "2.0.0", False)],
|
||||
[("p1", "1"), ("p2", "2"), ("p1", "1")],
|
||||
[
|
||||
{
|
||||
"plugin": "pycodestyle",
|
||||
"version": "2.0.0",
|
||||
"is_local": False,
|
||||
}
|
||||
{"plugin": "p1", "version": "1"},
|
||||
{"plugin": "p2", "version": "2"},
|
||||
],
|
||||
),
|
||||
(
|
||||
[
|
||||
manager.PluginVersion("pycodestyle", "2.0.0", False),
|
||||
manager.PluginVersion("mccabe", "0.5.9", False),
|
||||
],
|
||||
[
|
||||
{"plugin": "mccabe", "version": "0.5.9", "is_local": False},
|
||||
{
|
||||
"plugin": "pycodestyle",
|
||||
"version": "2.0.0",
|
||||
"is_local": False,
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
[
|
||||
manager.PluginVersion("pycodestyle", "2.0.0", False),
|
||||
manager.PluginVersion("my-local", "0.0.1", True),
|
||||
manager.PluginVersion("mccabe", "0.5.9", False),
|
||||
],
|
||||
[
|
||||
{"plugin": "mccabe", "version": "0.5.9", "is_local": False},
|
||||
{"plugin": "my-local", "version": "0.0.1", "is_local": True},
|
||||
{
|
||||
"plugin": "pycodestyle",
|
||||
"version": "2.0.0",
|
||||
"is_local": False,
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
def test_plugins_from(plugins, expected):
|
||||
def test_plugins_from(versions, expected):
|
||||
"""Test that we format plugins appropriately."""
|
||||
option_manager = mock.Mock(registered_plugins=set(plugins))
|
||||
option_manager = mock.Mock(**{"manager.versions.return_value": versions})
|
||||
assert expected == debug.plugins_from(option_manager)
|
||||
|
||||
|
||||
|
|
@ -67,8 +33,8 @@ def test_information(system, pyversion, pyimpl):
|
|||
expected = {
|
||||
"version": "3.1.0",
|
||||
"plugins": [
|
||||
{"plugin": "mccabe", "version": "0.5.9", "is_local": False},
|
||||
{"plugin": "pycodestyle", "version": "2.0.0", "is_local": False},
|
||||
{"plugin": "mccabe", "version": "0.5.9"},
|
||||
{"plugin": "pycodestyle", "version": "2.0.0"},
|
||||
],
|
||||
"platform": {
|
||||
"python_implementation": "CPython",
|
||||
|
|
@ -76,14 +42,15 @@ def test_information(system, pyversion, pyimpl):
|
|||
"system": "Linux",
|
||||
},
|
||||
}
|
||||
option_manager = mock.Mock(
|
||||
registered_plugins={
|
||||
manager.PluginVersion("pycodestyle", "2.0.0", False),
|
||||
manager.PluginVersion("mccabe", "0.5.9", False),
|
||||
},
|
||||
version="3.1.0",
|
||||
plugins = mock.Mock(
|
||||
**{
|
||||
"manager.versions.return_value": [
|
||||
("pycodestyle", "2.0.0"),
|
||||
("mccabe", "0.5.9"),
|
||||
]
|
||||
}
|
||||
)
|
||||
assert expected == debug.information(option_manager)
|
||||
assert expected == debug.information("3.1.0", plugins)
|
||||
pyimpl.assert_called_once_with()
|
||||
pyversion.assert_called_once_with()
|
||||
system.assert_called_once_with()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from unittest import mock
|
|||
|
||||
import pytest
|
||||
|
||||
from flake8 import utils
|
||||
from flake8.main.options import JobsArgument
|
||||
from flake8.options import manager
|
||||
|
||||
|
|
@ -15,7 +14,9 @@ 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)
|
||||
return manager.OptionManager(
|
||||
version=TEST_VERSION, plugin_versions="", parents=[]
|
||||
)
|
||||
|
||||
|
||||
def test_option_manager_creates_option_parser(optmanager):
|
||||
|
|
@ -31,7 +32,7 @@ def test_option_manager_including_parent_options():
|
|||
|
||||
# WHEN
|
||||
optmanager = manager.OptionManager(
|
||||
prog="flake8", version=TEST_VERSION, parents=[parent_parser]
|
||||
version=TEST_VERSION, plugin_versions="", parents=[parent_parser]
|
||||
)
|
||||
options = optmanager.parse_args(["--parent", "foo"])
|
||||
|
||||
|
|
@ -153,90 +154,6 @@ def test_parse_args_normalize_paths(optmanager):
|
|||
]
|
||||
|
||||
|
||||
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()
|
||||
|
|
@ -245,14 +162,6 @@ def test_extend_default_ignore(optmanager):
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ def test_load_config_append_config(tmpdir):
|
|||
|
||||
@pytest.fixture
|
||||
def opt_manager():
|
||||
ret = OptionManager(prog="flake8", version="123")
|
||||
ret = OptionManager(version="123", plugin_versions="", parents=[])
|
||||
register_default_options(ret)
|
||||
return ret
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue