mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-31 11:16:54 +00:00
Merge branch 'importlib_metadata' into 'master'
Switch from entrypoints to importlib_metadata Closes #569 See merge request pycqa/flake8!388
This commit is contained in:
commit
5d7eeaa9b3
12 changed files with 71 additions and 76 deletions
|
|
@ -41,7 +41,6 @@ install_requires=
|
|||
# http://flake8.pycqa.org/en/latest/faq.html#why-does-flake8-use-ranges-for-its-dependencies
|
||||
# And in which releases we will update those ranges here:
|
||||
# http://flake8.pycqa.org/en/latest/internal/releases.html#releasing-flake8
|
||||
entrypoints >= 0.3.0, < 0.4.0
|
||||
pyflakes >= 2.1.0, < 2.2.0
|
||||
pycodestyle >= 2.5.0, < 2.6.0
|
||||
mccabe >= 0.6.0, < 0.7.0
|
||||
|
|
@ -49,6 +48,7 @@ install_requires=
|
|||
typing; python_version<"3.5"
|
||||
configparser; python_version<"3.2"
|
||||
functools32; python_version<"3.2"
|
||||
importlib-metadata; python_version<"3.8"
|
||||
|
||||
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||||
|
||||
|
|
|
|||
14
src/flake8/_compat.py
Normal file
14
src/flake8/_compat.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
"""Expose backports in a single place."""
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3,): # pragma: no cover (PY3+)
|
||||
from functools import lru_cache
|
||||
else: # pragma: no cover (<PY3)
|
||||
from functools32 import lru_cache
|
||||
|
||||
if sys.version_info >= (3, 8): # pragma: no cover (PY38+)
|
||||
import importlib.metadata as importlib_metadata
|
||||
else: # pragma: no cover (<PY38)
|
||||
import importlib_metadata
|
||||
|
||||
__all__ = ("lru_cache", "importlib_metadata")
|
||||
|
|
@ -1,10 +1,6 @@
|
|||
"""Exception classes for all of Flake8."""
|
||||
|
||||
from typing import Dict
|
||||
|
||||
if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
|
||||
from flake8.plugins.manager import Plugin
|
||||
|
||||
|
||||
class Flake8Exception(Exception):
|
||||
"""Plain Flake8 exception."""
|
||||
|
|
@ -23,18 +19,17 @@ class FailedToLoadPlugin(Flake8Exception):
|
|||
|
||||
FORMAT = 'Flake8 failed to load plugin "%(name)s" due to %(exc)s.'
|
||||
|
||||
def __init__(self, plugin, exception):
|
||||
# type: (Plugin, Exception) -> None
|
||||
def __init__(self, plugin_name, exception):
|
||||
# type: (str, Exception) -> None
|
||||
"""Initialize our FailedToLoadPlugin exception."""
|
||||
self.plugin = plugin
|
||||
self.ep_name = self.plugin.name
|
||||
self.plugin_name = plugin_name
|
||||
self.original_exception = exception
|
||||
super(FailedToLoadPlugin, self).__init__(plugin, exception)
|
||||
super(FailedToLoadPlugin, self).__init__(plugin_name, exception)
|
||||
|
||||
def __str__(self): # type: () -> str
|
||||
"""Format our exception message."""
|
||||
return self.FORMAT % {
|
||||
"name": self.ep_name,
|
||||
"name": self.plugin_name,
|
||||
"exc": self.original_exception,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -168,9 +168,7 @@ class Application(object):
|
|||
|
||||
sys.path.extend(local_plugins.paths)
|
||||
|
||||
self.check_plugins = plugin_manager.Checkers(
|
||||
local_plugins.extension
|
||||
)
|
||||
self.check_plugins = plugin_manager.Checkers(local_plugins.extension)
|
||||
|
||||
self.formatting_plugins = plugin_manager.ReportFormatters(
|
||||
local_plugins.report
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ from __future__ import print_function
|
|||
import argparse
|
||||
import json
|
||||
import platform
|
||||
|
||||
import entrypoints
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class DebugAction(argparse.Action):
|
||||
|
|
@ -61,6 +60,6 @@ def plugins_from(option_manager):
|
|||
]
|
||||
|
||||
|
||||
def dependencies():
|
||||
def dependencies(): # type: () -> List[Dict[str, str]]
|
||||
"""Generate the list of dependencies we care about."""
|
||||
return [{"dependency": "entrypoints", "version": entrypoints.__version__}]
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@
|
|||
import logging
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
import entrypoints
|
||||
|
||||
from flake8 import exceptions
|
||||
from flake8 import utils
|
||||
from flake8._compat import importlib_metadata
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -159,7 +158,7 @@ class Plugin(object):
|
|||
except Exception as load_exception:
|
||||
LOG.exception(load_exception)
|
||||
failed_to_load = exceptions.FailedToLoadPlugin(
|
||||
plugin=self, exception=load_exception
|
||||
plugin_name=self.name, exception=load_exception
|
||||
)
|
||||
LOG.critical(str(failed_to_load))
|
||||
raise failed_to_load
|
||||
|
|
@ -224,6 +223,7 @@ class PluginManager(object): # pylint: disable=too-few-public-methods
|
|||
"""Find and manage plugins consistently."""
|
||||
|
||||
def __init__(self, namespace, local_plugins=None):
|
||||
# type: (str, Optional[List[str]]) -> None
|
||||
"""Initialize the manager.
|
||||
|
||||
:param str namespace:
|
||||
|
|
@ -246,12 +246,16 @@ class PluginManager(object): # pylint: disable=too-few-public-methods
|
|||
for plugin_str in local_plugins:
|
||||
name, _, entry_str = plugin_str.partition("=")
|
||||
name, entry_str = name.strip(), entry_str.strip()
|
||||
entry_point = entrypoints.EntryPoint.from_string(entry_str, name)
|
||||
entry_point = importlib_metadata.EntryPoint(name, entry_str, None)
|
||||
self._load_plugin_from_entrypoint(entry_point, local=True)
|
||||
|
||||
def _load_entrypoint_plugins(self):
|
||||
LOG.info('Loading entry-points for "%s".', self.namespace)
|
||||
for entry_point in entrypoints.get_group_all(self.namespace):
|
||||
eps = importlib_metadata.entry_points().get(self.namespace, ())
|
||||
# python2.7 occasionally gives duplicate results due to redundant
|
||||
# `local/lib` -> `../lib` symlink on linux in virtualenvs so we
|
||||
# eliminate duplicates here
|
||||
for entry_point in sorted(frozenset(eps)):
|
||||
if entry_point.name == "per-file-ignores":
|
||||
LOG.warning(
|
||||
"flake8-per-file-ignores plugin is incompatible with "
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import enum
|
|||
import itertools
|
||||
import linecache
|
||||
import logging
|
||||
import sys
|
||||
from typing import Dict, Generator, List, Match, Optional, Sequence, Set
|
||||
from typing import Tuple, Union
|
||||
|
||||
from flake8 import defaults
|
||||
from flake8 import statistics
|
||||
from flake8 import utils
|
||||
from flake8._compat import lru_cache
|
||||
from flake8.formatting import base as base_formatter
|
||||
|
||||
__all__ = ("StyleGuide",)
|
||||
|
|
@ -21,12 +21,6 @@ __all__ = ("StyleGuide",)
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if sys.version_info < (3, 2):
|
||||
from functools32 import lru_cache
|
||||
else:
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class Selected(enum.Enum):
|
||||
"""Enum representing an explicitly or implicitly selected code."""
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import mock
|
|||
import pytest
|
||||
|
||||
from flake8 import checker
|
||||
from flake8._compat import importlib_metadata
|
||||
from flake8.plugins import manager
|
||||
|
||||
PHYSICAL_LINE = "# Physical line content"
|
||||
|
|
@ -100,7 +101,11 @@ def mock_file_checker_with_plugin(plugin_target):
|
|||
entry_point.load.return_value = plugin_target
|
||||
|
||||
# Load the checker plugins using the entry point mock
|
||||
with mock.patch('entrypoints.get_group_all', return_value=[entry_point]):
|
||||
with mock.patch.object(
|
||||
importlib_metadata,
|
||||
'entry_points',
|
||||
return_value={'flake8.extension': [entry_point]},
|
||||
):
|
||||
checks = manager.Checkers()
|
||||
|
||||
# Prevent it from reading lines from stdin or somewhere else
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""Tests for our debugging module."""
|
||||
import entrypoints
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
|
|
@ -9,9 +8,7 @@ from flake8.options import manager
|
|||
|
||||
def test_dependencies():
|
||||
"""Verify that we format our dependencies appropriately."""
|
||||
expected = [{'dependency': 'entrypoints',
|
||||
'version': entrypoints.__version__}]
|
||||
assert expected == debug.dependencies()
|
||||
assert [] == debug.dependencies()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('plugins, expected', [
|
||||
|
|
@ -46,8 +43,7 @@ def test_information(system, pyversion, pyimpl):
|
|||
'is_local': False},
|
||||
{'plugin': 'pycodestyle', 'version': '2.0.0',
|
||||
'is_local': False}],
|
||||
'dependencies': [{'dependency': 'entrypoints',
|
||||
'version': entrypoints.__version__}],
|
||||
'dependencies': [],
|
||||
'platform': {
|
||||
'python_implementation': 'CPython',
|
||||
'python_version': '3.5.3',
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
"""Tests for the flake8.exceptions module."""
|
||||
import pickle
|
||||
|
||||
import entrypoints
|
||||
|
||||
from flake8 import exceptions
|
||||
from flake8.plugins import manager as plugins_manager
|
||||
|
||||
|
||||
class _ExceptionTest:
|
||||
|
|
@ -22,10 +19,7 @@ class TestFailedToLoadPlugin(_ExceptionTest):
|
|||
"""Tests for the FailedToLoadPlugin exception."""
|
||||
|
||||
err = exceptions.FailedToLoadPlugin(
|
||||
plugin=plugins_manager.Plugin(
|
||||
'plugin_name',
|
||||
entrypoints.EntryPoint('plugin_name', 'os.path', None),
|
||||
),
|
||||
plugin_name='plugin_name',
|
||||
exception=ValueError('boom!'),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,62 +1,58 @@
|
|||
"""Tests for flake8.plugins.manager.PluginManager."""
|
||||
import mock
|
||||
|
||||
from flake8._compat import importlib_metadata
|
||||
from flake8.plugins import manager
|
||||
|
||||
|
||||
def create_entry_point_mock(name):
|
||||
"""Create a mocked EntryPoint."""
|
||||
ep = mock.Mock(spec=['name'])
|
||||
ep.name = name
|
||||
return ep
|
||||
|
||||
|
||||
@mock.patch('entrypoints.get_group_all')
|
||||
def test_calls_entrypoints_on_instantiation(get_group_all):
|
||||
"""Verify that we call get_group_all when we create a manager."""
|
||||
get_group_all.return_value = []
|
||||
@mock.patch.object(importlib_metadata, 'entry_points')
|
||||
def test_calls_entrypoints_on_instantiation(entry_points_mck):
|
||||
"""Verify that we call entry_points() when we create a manager."""
|
||||
entry_points_mck.return_value = {}
|
||||
manager.PluginManager(namespace='testing.entrypoints')
|
||||
|
||||
get_group_all.assert_called_once_with('testing.entrypoints')
|
||||
entry_points_mck.assert_called_once_with()
|
||||
|
||||
|
||||
@mock.patch('entrypoints.get_group_all')
|
||||
def test_calls_entrypoints_creates_plugins_automaticaly(get_group_all):
|
||||
@mock.patch.object(importlib_metadata, 'entry_points')
|
||||
def test_calls_entrypoints_creates_plugins_automaticaly(entry_points_mck):
|
||||
"""Verify that we create Plugins on instantiation."""
|
||||
get_group_all.return_value = [
|
||||
create_entry_point_mock('T100'),
|
||||
create_entry_point_mock('T200'),
|
||||
]
|
||||
entry_points_mck.return_value = {
|
||||
'testing.entrypoints': [
|
||||
importlib_metadata.EntryPoint('T100', '', None),
|
||||
importlib_metadata.EntryPoint('T200', '', None),
|
||||
],
|
||||
}
|
||||
plugin_mgr = manager.PluginManager(namespace='testing.entrypoints')
|
||||
|
||||
get_group_all.assert_called_once_with('testing.entrypoints')
|
||||
entry_points_mck.assert_called_once_with()
|
||||
assert 'T100' in plugin_mgr.plugins
|
||||
assert 'T200' in plugin_mgr.plugins
|
||||
assert isinstance(plugin_mgr.plugins['T100'], manager.Plugin)
|
||||
assert isinstance(plugin_mgr.plugins['T200'], manager.Plugin)
|
||||
|
||||
|
||||
@mock.patch('entrypoints.get_group_all')
|
||||
def test_handles_mapping_functions_across_plugins(get_group_all):
|
||||
@mock.patch.object(importlib_metadata, 'entry_points')
|
||||
def test_handles_mapping_functions_across_plugins(entry_points_mck):
|
||||
"""Verify we can use the PluginManager call functions on all plugins."""
|
||||
entry_point_mocks = [
|
||||
create_entry_point_mock('T100'),
|
||||
create_entry_point_mock('T200'),
|
||||
]
|
||||
get_group_all.return_value = entry_point_mocks
|
||||
entry_points_mck.return_value = {
|
||||
'testing.entrypoints': [
|
||||
importlib_metadata.EntryPoint('T100', '', None),
|
||||
importlib_metadata.EntryPoint('T200', '', None),
|
||||
],
|
||||
}
|
||||
plugin_mgr = manager.PluginManager(namespace='testing.entrypoints')
|
||||
plugins = [plugin_mgr.plugins[name] for name in plugin_mgr.names]
|
||||
|
||||
assert list(plugin_mgr.map(lambda x: x)) == plugins
|
||||
|
||||
|
||||
@mock.patch('entrypoints.get_group_all')
|
||||
def test_local_plugins(get_group_all):
|
||||
@mock.patch.object(importlib_metadata, 'entry_points')
|
||||
def test_local_plugins(entry_points_mck):
|
||||
"""Verify PluginManager can load given local plugins."""
|
||||
get_group_all.return_value = []
|
||||
entry_points_mck.return_value = {}
|
||||
plugin_mgr = manager.PluginManager(
|
||||
namespace='testing.entrypoints',
|
||||
local_plugins=['X = path.to:Plugin']
|
||||
)
|
||||
|
||||
assert plugin_mgr.plugins['X'].entry_point.module_name == 'path.to'
|
||||
assert plugin_mgr.plugins['X'].entry_point.value == 'path.to:Plugin'
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ def create_plugin_mock(raise_exception=False):
|
|||
plugin = mock.create_autospec(manager.Plugin, instance=True)
|
||||
if raise_exception:
|
||||
plugin.load_plugin.side_effect = exceptions.FailedToLoadPlugin(
|
||||
plugin=mock.Mock(name='T101'),
|
||||
plugin_name='T101',
|
||||
exception=ValueError('Test failure'),
|
||||
)
|
||||
return plugin
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue