summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Mueller <30130371+cdce8p@users.noreply.github.com>2021-03-01 12:49:45 +0100
committerPierre Sassoulas <pierre.sassoulas@gmail.com>2021-03-05 19:08:42 +0100
commit56b8e229d9150d57e090a1e4bd046b2fe8541d97 (patch)
tree822bcba13ce360023b18dbb234b37088ae77a850
parentb8c9472706de23e79d540a4df717cf2966483891 (diff)
downloadpylint-git-56b8e229d9150d57e090a1e4bd046b2fe8541d97.tar.gz
Refactor modify sys_path for execution as python module
-rw-r--r--ChangeLog5
-rw-r--r--pylint/__init__.py23
-rw-r--r--pylint/__main__.py11
-rw-r--r--tests/test_self.py69
4 files changed, 98 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 6af40d64b..d1d4aea6e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -25,6 +25,11 @@ Release date: TBA
Close #3263
+* Fix issue when executing with ``python -m pylint``
+
+ Closes #4161
+
+
What's New in Pylint 2.7.2?
===========================
Release date: 2021-02-28
diff --git a/pylint/__init__.py b/pylint/__init__.py
index 1fd6ffd16..20bb554d2 100644
--- a/pylint/__init__.py
+++ b/pylint/__init__.py
@@ -8,6 +8,7 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+import os
import sys
from pylint.__pkginfo__ import version as __version__
@@ -44,4 +45,26 @@ def run_symilar():
SimilarRun(sys.argv[1:])
+def modify_sys_path() -> None:
+ """Modify sys path for execution as Python module.
+
+ Strip out the current working directory from sys.path.
+ Having the working directory in `sys.path` means that `pylint` might
+ inadvertently import user code from modules having the same name as
+ stdlib or pylint's own modules.
+ CPython issue: https://bugs.python.org/issue33053
+
+ - Remove the first entry. This will always be either "" or the working directory
+ - Remove the working directory from the second and third entries. This can
+ occur if PYTHONPATH includes a ":" at the beginning or the end.
+ https://github.com/PyCQA/pylint/issues/3636
+ - Don't remove the working directory from the rest. It will be included
+ if pylint is installed in an editable configuration (as the last item).
+ https://github.com/PyCQA/pylint/issues/4161
+ """
+ sys.path = [
+ p for i, p in enumerate(sys.path) if i > 0 and not (i < 3 and p == os.getcwd())
+ ]
+
+
__all__ = ["__version__"]
diff --git a/pylint/__main__.py b/pylint/__main__.py
index 89bd19924..4d7653718 100644
--- a/pylint/__main__.py
+++ b/pylint/__main__.py
@@ -3,16 +3,7 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
-import os
-import sys
-
import pylint
-# Strip out the current working directory from sys.path.
-# Having the working directory in `sys.path` means that `pylint` might
-# inadvertently import user code from modules having the same name as
-# stdlib or pylint's own modules.
-# CPython issue: https://bugs.python.org/issue33053
-sys.path = [p for p in sys.path if p not in ("", os.getcwd())]
-
+pylint.modify_sys_path()
pylint.run_pylint()
diff --git a/tests/test_self.py b/tests/test_self.py
index 5b55f6cca..42ef732ec 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -42,12 +42,16 @@ import subprocess
import sys
import textwrap
import warnings
+from copy import copy
from io import StringIO
from os.path import abspath, dirname, join
+from typing import Generator
from unittest import mock
+from unittest.mock import patch
import pytest
+from pylint import modify_sys_path
from pylint.constants import MAIN_CHECKER_NAME, MSG_TYPES_STATUS
from pylint.lint import Run
from pylint.reporters import JSONReporter
@@ -700,6 +704,71 @@ class TestRunTC:
)
@staticmethod
+ def test_modify_sys_path() -> None:
+ @contextlib.contextmanager
+ def test_sys_path() -> Generator[None, None, None]:
+ original_path = sys.path
+ try:
+ yield
+ finally:
+ sys.path = original_path
+
+ with test_sys_path(), patch("os.getcwd") as mock_getcwd:
+ cwd = "/tmp/pytest-of-root/pytest-0/test_do_not_import_files_from_0"
+ mock_getcwd.return_value = cwd
+
+ paths = [
+ cwd,
+ cwd,
+ "/usr/local/lib/python39.zip",
+ "/usr/local/lib/python3.9",
+ "/usr/local/lib/python3.9/lib-dynload",
+ "/usr/local/lib/python3.9/site-packages",
+ ]
+ sys.path = copy(paths)
+ modify_sys_path()
+ assert sys.path == paths[2:]
+
+ paths = [
+ cwd,
+ "/custom_pythonpath",
+ cwd,
+ "/usr/local/lib/python39.zip",
+ "/usr/local/lib/python3.9",
+ "/usr/local/lib/python3.9/lib-dynload",
+ "/usr/local/lib/python3.9/site-packages",
+ ]
+ sys.path = copy(paths)
+ modify_sys_path()
+ assert sys.path == [paths[1]] + paths[3:]
+
+ paths = [
+ "",
+ cwd,
+ "/custom_pythonpath",
+ "/usr/local/lib/python39.zip",
+ "/usr/local/lib/python3.9",
+ "/usr/local/lib/python3.9/lib-dynload",
+ "/usr/local/lib/python3.9/site-packages",
+ ]
+ sys.path = copy(paths)
+ modify_sys_path()
+ assert sys.path == paths[2:]
+
+ paths = [
+ "",
+ cwd,
+ "/usr/local/lib/python39.zip",
+ "/usr/local/lib/python3.9",
+ "/usr/local/lib/python3.9/lib-dynload",
+ "/usr/local/lib/python3.9/site-packages",
+ cwd,
+ ]
+ sys.path = copy(paths)
+ modify_sys_path()
+ assert sys.path == paths[2:]
+
+ @staticmethod
def test_do_not_import_files_from_local_directory(tmpdir):
p_astroid = tmpdir / "astroid.py"
p_astroid.write("'Docstring'\nimport completely_unknown\n")