Skip to content

Commit c85cb41

Browse files
authored
Fix regression with sys.path filter (#4258)
* Fix regression with sys.path filter
1 parent 95daeca commit c85cb41

File tree

3 files changed

+141
-30
lines changed

3 files changed

+141
-30
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ Release date: TBA
8484

8585
Closes #3202
8686

87+
* Fix regression with plugins on PYTHONPATH if latter is cwd
88+
89+
Closes #4252
90+
8791

8892
What's New in Pylint 2.7.2?
8993
===========================

pylint/__init__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,22 @@ def modify_sys_path() -> None:
5555
CPython issue: https://bugs.python.org/issue33053
5656
5757
- Remove the first entry. This will always be either "" or the working directory
58-
- Remove the working directory from the second and third entries. This can
59-
occur if PYTHONPATH includes a ":" at the beginning or the end.
58+
- Remove the working directory from the second and third entries
59+
if PYTHONPATH includes a ":" at the beginning or the end.
6060
https://github.com/PyCQA/pylint/issues/3636
61+
Don't remove it if PYTHONPATH contains the cwd or '.' as the entry will
62+
only be added once.
6163
- Don't remove the working directory from the rest. It will be included
6264
if pylint is installed in an editable configuration (as the last item).
6365
https://github.com/PyCQA/pylint/issues/4161
6466
"""
65-
sys.path = [
66-
p for i, p in enumerate(sys.path) if i > 0 and not (i < 3 and p == os.getcwd())
67-
]
67+
sys.path.pop(0)
68+
env_pythonpath = os.environ.get("PYTHONPATH", "")
69+
cwd = os.getcwd()
70+
if env_pythonpath.startswith(":") and env_pythonpath not in (f":{cwd}", ":."):
71+
sys.path.pop(0)
72+
elif env_pythonpath.endswith(":") and env_pythonpath not in (f"{cwd}:", ".:"):
73+
sys.path.pop(1)
6874

6975

7076
__all__ = ["__version__"]

tests/test_self.py

Lines changed: 126 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from copy import copy
4646
from io import StringIO
4747
from os.path import abspath, dirname, join
48-
from typing import Generator
48+
from typing import Generator, Optional
4949
from unittest import mock
5050
from unittest.mock import patch
5151

@@ -724,61 +724,131 @@ def test_sys_path() -> Generator[None, None, None]:
724724
finally:
725725
sys.path = original_path
726726

727+
@contextlib.contextmanager
728+
def test_environ_pythonpath(
729+
new_pythonpath: Optional[str],
730+
) -> Generator[None, None, None]:
731+
original_pythonpath = os.environ.get("PYTHONPATH")
732+
if new_pythonpath:
733+
os.environ["PYTHONPATH"] = new_pythonpath
734+
elif new_pythonpath is None and original_pythonpath is not None:
735+
# If new_pythonpath is None, make sure to delete PYTHONPATH if present
736+
del os.environ["PYTHONPATH"]
737+
try:
738+
yield
739+
finally:
740+
if original_pythonpath:
741+
os.environ["PYTHONPATH"] = original_pythonpath
742+
elif new_pythonpath is not None:
743+
# Only delete PYTHONPATH if new_pythonpath wasn't None
744+
del os.environ["PYTHONPATH"]
745+
727746
with test_sys_path(), patch("os.getcwd") as mock_getcwd:
728747
cwd = "/tmp/pytest-of-root/pytest-0/test_do_not_import_files_from_0"
729748
mock_getcwd.return_value = cwd
730-
731-
paths = [
732-
cwd,
733-
cwd,
749+
default_paths = [
734750
"/usr/local/lib/python39.zip",
735751
"/usr/local/lib/python3.9",
736752
"/usr/local/lib/python3.9/lib-dynload",
737753
"/usr/local/lib/python3.9/site-packages",
738754
]
755+
756+
paths = [
757+
cwd,
758+
*default_paths,
759+
]
739760
sys.path = copy(paths)
740-
modify_sys_path()
741-
assert sys.path == paths[2:]
761+
with test_environ_pythonpath(None):
762+
modify_sys_path()
763+
assert sys.path == paths[1:]
764+
765+
paths = [
766+
cwd,
767+
cwd,
768+
*default_paths,
769+
]
770+
sys.path = copy(paths)
771+
with test_environ_pythonpath("."):
772+
modify_sys_path()
773+
assert sys.path == paths[1:]
742774

743775
paths = [
744776
cwd,
745777
"/custom_pythonpath",
778+
*default_paths,
779+
]
780+
sys.path = copy(paths)
781+
with test_environ_pythonpath("/custom_pythonpath"):
782+
modify_sys_path()
783+
assert sys.path == paths[1:]
784+
785+
paths = [
746786
cwd,
747-
"/usr/local/lib/python39.zip",
748-
"/usr/local/lib/python3.9",
749-
"/usr/local/lib/python3.9/lib-dynload",
750-
"/usr/local/lib/python3.9/site-packages",
787+
"/custom_pythonpath",
788+
cwd,
789+
*default_paths,
751790
]
752791
sys.path = copy(paths)
753-
modify_sys_path()
792+
with test_environ_pythonpath("/custom_pythonpath:"):
793+
modify_sys_path()
754794
assert sys.path == [paths[1]] + paths[3:]
755795

756796
paths = [
757797
"",
758798
cwd,
759799
"/custom_pythonpath",
760-
"/usr/local/lib/python39.zip",
761-
"/usr/local/lib/python3.9",
762-
"/usr/local/lib/python3.9/lib-dynload",
763-
"/usr/local/lib/python3.9/site-packages",
800+
*default_paths,
764801
]
765802
sys.path = copy(paths)
766-
modify_sys_path()
803+
with test_environ_pythonpath(":/custom_pythonpath"):
804+
modify_sys_path()
767805
assert sys.path == paths[2:]
768806

769807
paths = [
770-
"",
771808
cwd,
772-
"/usr/local/lib/python39.zip",
773-
"/usr/local/lib/python3.9",
774-
"/usr/local/lib/python3.9/lib-dynload",
775-
"/usr/local/lib/python3.9/site-packages",
776809
cwd,
810+
"/custom_pythonpath",
811+
*default_paths,
777812
]
778813
sys.path = copy(paths)
779-
modify_sys_path()
814+
with test_environ_pythonpath(":/custom_pythonpath:"):
815+
modify_sys_path()
780816
assert sys.path == paths[2:]
781817

818+
paths = [
819+
cwd,
820+
cwd,
821+
*default_paths,
822+
]
823+
sys.path = copy(paths)
824+
with test_environ_pythonpath(":."):
825+
modify_sys_path()
826+
assert sys.path == paths[1:]
827+
sys.path = copy(paths)
828+
with test_environ_pythonpath(f":{cwd}"):
829+
modify_sys_path()
830+
assert sys.path == paths[1:]
831+
832+
sys.path = copy(paths)
833+
with test_environ_pythonpath(".:"):
834+
modify_sys_path()
835+
assert sys.path == paths[1:]
836+
sys.path = copy(paths)
837+
with test_environ_pythonpath(f"{cwd}:"):
838+
modify_sys_path()
839+
assert sys.path == paths[1:]
840+
841+
paths = [
842+
"",
843+
cwd,
844+
*default_paths,
845+
cwd,
846+
]
847+
sys.path = copy(paths)
848+
with test_environ_pythonpath(cwd):
849+
modify_sys_path()
850+
assert sys.path == paths[1:]
851+
782852
@staticmethod
783853
def test_do_not_import_files_from_local_directory(tmpdir):
784854
p_astroid = tmpdir / "astroid.py"
@@ -838,7 +908,7 @@ def test_do_not_import_files_from_local_directory_with_pythonpath(tmpdir):
838908
# https://github.com/PyCQA/pylint/issues/3636
839909
with tmpdir.as_cwd():
840910
orig_pythonpath = os.environ.get("PYTHONPATH")
841-
os.environ["PYTHONPATH"] = os.environ.get("PYTHONPATH", "") + ":"
911+
os.environ["PYTHONPATH"] = f"{(orig_pythonpath or '').strip(':')}:"
842912
subprocess.check_output(
843913
[
844914
sys.executable,
@@ -849,8 +919,39 @@ def test_do_not_import_files_from_local_directory_with_pythonpath(tmpdir):
849919
],
850920
cwd=str(tmpdir),
851921
)
852-
if orig_pythonpath is not None:
922+
if orig_pythonpath:
923+
os.environ["PYTHONPATH"] = orig_pythonpath
924+
else:
925+
del os.environ["PYTHONPATH"]
926+
927+
@staticmethod
928+
def test_import_plugin_from_local_directory_if_pythonpath_cwd(tmpdir):
929+
p_plugin = tmpdir / "plugin.py"
930+
p_plugin.write("# Some plugin content")
931+
932+
with tmpdir.as_cwd():
933+
orig_pythonpath = os.environ.get("PYTHONPATH")
934+
os.environ["PYTHONPATH"] = "."
935+
process = subprocess.run(
936+
[
937+
sys.executable,
938+
"-m",
939+
"pylint",
940+
"--load-plugins",
941+
"plugin",
942+
],
943+
cwd=str(tmpdir),
944+
stderr=subprocess.PIPE,
945+
check=False,
946+
)
947+
assert (
948+
"AttributeError: module 'plugin' has no attribute 'register'"
949+
in process.stderr.decode()
950+
)
951+
if orig_pythonpath:
853952
os.environ["PYTHONPATH"] = orig_pythonpath
953+
else:
954+
del os.environ["PYTHONPATH"]
854955

855956
def test_allow_import_of_files_found_in_modules_during_parallel_check(self, tmpdir):
856957
test_directory = tmpdir / "test_directory"

0 commit comments

Comments
 (0)