From fce93b952a423a53c204c12aa0a20eede6948758 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 31 Jul 2022 18:01:12 -0400 Subject: [PATCH] prevent duplicate plugin discovery on misconfigured pythons for example, `venv`-virtualenvs on fedora have both `lib` and `lib64` on `sys.path` despite them being the same. this causes `importlib.metadata.distributions` to double-discover. ```console $ docker run --rm -t fedora:latest bash -c 'dnf install -qq -y python3 >& /dev/null && python3 -m venv venv && venv/bin/pip -qq install cfgv && venv/bin/python - <<< "from importlib.metadata import distributions; print(len([d for d in distributions() if d.name == '"'"'cfgv'"'"']))"' 2 ``` --- src/flake8/plugins/finder.py | 7 +++++++ tests/unit/plugins/finder_test.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/flake8/plugins/finder.py b/src/flake8/plugins/finder.py index fb87d0d6..9e9e3afc 100644 --- a/src/flake8/plugins/finder.py +++ b/src/flake8/plugins/finder.py @@ -179,6 +179,8 @@ def _flake8_plugins( def _find_importlib_plugins() -> Generator[Plugin, None, None]: + # some misconfigured pythons (RHEL) have things on `sys.path` twice + seen = set() for dist in importlib_metadata.distributions(): # assigned to prevent continual reparsing eps = dist.entry_points @@ -190,6 +192,11 @@ def _find_importlib_plugins() -> Generator[Plugin, None, None]: # assigned to prevent continual reparsing meta = dist.metadata + if meta["name"] in seen: + continue + else: + seen.add(meta["name"]) + if meta["name"] in BANNED_PLUGINS: LOG.warning( "%s plugin is obsolete in flake8>=%s", diff --git a/tests/unit/plugins/finder_test.py b/tests/unit/plugins/finder_test.py index 3c11c643..63f81569 100644 --- a/tests/unit/plugins/finder_test.py +++ b/tests/unit/plugins/finder_test.py @@ -361,6 +361,23 @@ def test_importlib_plugins( ] +def test_duplicate_dists(flake8_dist): + # some poorly packaged pythons put lib and lib64 on sys.path resulting in + # duplicates from `importlib.metadata.distributions` + with mock.patch.object( + importlib_metadata, + "distributions", + return_value=[ + flake8_dist, + flake8_dist, + ], + ): + ret = list(finder._find_importlib_plugins()) + + # we should not have duplicates + assert len(ret) == len(set(ret)) + + def test_find_local_plugins_nothing(): cfg = configparser.RawConfigParser() assert set(finder._find_local_plugins(cfg)) == set()