Skip to content

Commit f29cd7e

Browse files
committed
fixup! Search sys.path for PEP-561 compliant packages
1 parent 64742db commit f29cd7e

File tree

4 files changed

+65
-81
lines changed

4 files changed

+65
-81
lines changed

mypy/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1033,10 +1033,10 @@ def set_strict_flags() -> None:
10331033
# Set target.
10341034
if special_opts.modules + special_opts.packages:
10351035
options.build_type = BuildType.MODULE
1036-
egg_dirs, site_packages, sys_path = get_search_dirs(options.python_executable)
1036+
search_dirs = get_search_dirs(options.python_executable)
10371037
search_paths = SearchPaths((os.getcwd(),),
10381038
tuple(mypy_path() + options.mypy_path),
1039-
tuple(egg_dirs + site_packages + sys_path),
1039+
tuple(search_dirs),
10401040
())
10411041
targets = []
10421042
# TODO: use the same cache that the BuildManager will

mypy/modulefinder.py

+15-59
Original file line numberDiff line numberDiff line change
@@ -608,70 +608,26 @@ def default_lib_path(data_dir: str,
608608

609609

610610
@functools.lru_cache(maxsize=None)
611-
def get_search_dirs(python_executable: Optional[str]) -> Tuple[List[str], List[str], List[str]]:
611+
def get_search_dirs(python_executable: Optional[str]) -> List[str]:
612612
"""Find package directories for given python.
613613
614-
This runs a subprocess call, which generates a list of the egg directories, and the site
615-
package directories. To avoid repeatedly calling a subprocess (which can be slow!) we
614+
This runs a subprocess call, which generates a list of the directories in sys.path.
615+
To avoid repeatedly calling a subprocess (which can be slow!) we
616616
lru_cache the results.
617617
"""
618618

619619
if python_executable is None:
620-
return [], [], []
620+
return []
621621
elif python_executable == sys.executable:
622622
# Use running Python's package dirs
623-
site_packages, sys_path = pyinfo.getsearchdirs()
623+
sys_path = pyinfo.getsearchdirs()
624624
else:
625625
# Use subprocess to get the package directory of given Python
626626
# executable
627-
site_packages, sys_path = ast.literal_eval(
627+
sys_path = ast.literal_eval(
628628
subprocess.check_output([python_executable, pyinfo.__file__, 'getsearchdirs'],
629629
stderr=subprocess.PIPE).decode())
630-
return expand_site_packages(site_packages) + (sys_path,)
631-
632-
633-
def expand_site_packages(site_packages: List[str]) -> Tuple[List[str], List[str]]:
634-
"""Expands .pth imports in site-packages directories"""
635-
egg_dirs: List[str] = []
636-
for dir in site_packages:
637-
if not os.path.isdir(dir):
638-
continue
639-
pth_filenames = sorted(name for name in os.listdir(dir) if name.endswith(".pth"))
640-
for pth_filename in pth_filenames:
641-
egg_dirs.extend(_parse_pth_file(dir, pth_filename))
642-
643-
return egg_dirs, site_packages
644-
645-
646-
def _parse_pth_file(dir: str, pth_filename: str) -> Iterator[str]:
647-
"""
648-
Mimics a subset of .pth import hook from Lib/site.py
649-
See https://github.com/python/cpython/blob/3.5/Lib/site.py#L146-L185
650-
"""
651-
652-
pth_file = os.path.join(dir, pth_filename)
653-
try:
654-
f = open(pth_file, "r")
655-
except OSError:
656-
return
657-
with f:
658-
for line in f.readlines():
659-
if line.startswith("#"):
660-
# Skip comment lines
661-
continue
662-
if line.startswith(("import ", "import\t")):
663-
# import statements in .pth files are not supported
664-
continue
665-
666-
yield _make_abspath(line.rstrip(), dir)
667-
668-
669-
def _make_abspath(path: str, root: str) -> str:
670-
"""Take a path and make it absolute relative to root if not already absolute."""
671-
if os.path.isabs(path):
672-
return os.path.normpath(path)
673-
else:
674-
return os.path.join(root, os.path.normpath(path))
630+
return sys_path
675631

676632

677633
def add_py2_mypypath_entries(mypypath: List[str]) -> List[str]:
@@ -760,20 +716,20 @@ def compute_search_paths(sources: List[BuildSource],
760716
if options.python_version[0] == 2:
761717
mypypath = add_py2_mypypath_entries(mypypath)
762718

763-
egg_dirs, site_packages, sys_path = get_search_dirs(options.python_executable)
764-
for site_dir in site_packages + sys_path:
765-
assert site_dir not in lib_path
766-
if (site_dir in mypypath or
767-
any(p.startswith(site_dir + os.path.sep) for p in mypypath) or
768-
os.path.altsep and any(p.startswith(site_dir + os.path.altsep) for p in mypypath)):
769-
print("{} is in the MYPYPATH. Please remove it.".format(site_dir), file=sys.stderr)
719+
search_dirs = get_search_dirs(options.python_executable)
720+
for search_dir in search_dirs:
721+
assert search_dir not in lib_path
722+
if (search_dir in mypypath or
723+
any(p.startswith(search_dir + os.path.sep) for p in mypypath) or
724+
os.path.altsep and any(p.startswith(search_dir + os.path.altsep) for p in mypypath)):
725+
print("{} is in the MYPYPATH. Please remove it.".format(search_dir), file=sys.stderr)
770726
print("See https://mypy.readthedocs.io/en/stable/running_mypy.html"
771727
"#how-mypy-handles-imports for more info", file=sys.stderr)
772728
sys.exit(1)
773729

774730
return SearchPaths(python_path=tuple(reversed(python_path)),
775731
mypy_path=tuple(mypypath),
776-
package_path=tuple(egg_dirs + site_packages + sys_path),
732+
package_path=tuple(search_dirs),
777733
typeshed_path=tuple(lib_path))
778734

779735

mypy/pyinfo.py

+3-19
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020

2121

2222
def getsearchdirs():
23-
# type: () -> Tuple[List[str], List[str]]
24-
site_packages = _getsitepackages()
25-
23+
# type: () -> List[str]
2624
# Do not include things from the standard library
2725
# because those should come from typeshed.
2826
stdlib_zip = os.path.join(
@@ -33,24 +31,10 @@ def getsearchdirs():
3331
stdlib = sysconfig.get_path("stdlib")
3432
stdlib_ext = os.path.join(stdlib, "lib-dynload")
3533
cwd = os.path.abspath(os.getcwd())
36-
excludes = set(site_packages + [cwd, stdlib_zip, stdlib, stdlib_ext])
34+
excludes = set([cwd, stdlib_zip, stdlib, stdlib_ext])
3735

3836
abs_sys_path = (os.path.abspath(p) for p in sys.path)
39-
return (site_packages, [p for p in abs_sys_path if p not in excludes])
40-
41-
42-
def _getsitepackages():
43-
# type: () -> List[str]
44-
res = []
45-
if hasattr(site, 'getsitepackages'):
46-
res.extend(site.getsitepackages())
47-
48-
if hasattr(site, 'getusersitepackages') and site.ENABLE_USER_SITE:
49-
res.insert(0, site.getusersitepackages())
50-
else:
51-
from distutils.sysconfig import get_python_lib
52-
res = [get_python_lib()]
53-
return res
37+
return [p for p in abs_sys_path if p not in excludes]
5438

5539

5640
if __name__ == '__main__':

mypy/test/testmodulefinder.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,62 @@
11
import os
2+
from typing import Iterator, List, Tuple
23

34
from mypy.options import Options
45
from mypy.modulefinder import (
56
FindModuleCache,
67
SearchPaths,
78
ModuleNotFoundReason,
8-
expand_site_packages
99
)
1010

1111
from mypy.test.helpers import Suite, assert_equal
1212
from mypy.test.config import package_path
1313
data_path = os.path.relpath(os.path.join(package_path, "modulefinder"))
1414

1515

16+
def expand_site_packages(site_packages: List[str]) -> Tuple[List[str], List[str]]:
17+
"""Mimic the .pth imports in site-packages directories."""
18+
egg_dirs: List[str] = []
19+
for dir in site_packages:
20+
if not os.path.isdir(dir):
21+
continue
22+
pth_filenames = sorted(name for name in os.listdir(dir) if name.endswith(".pth"))
23+
for pth_filename in pth_filenames:
24+
egg_dirs.extend(_parse_pth_file(dir, pth_filename))
25+
26+
return egg_dirs, site_packages
27+
28+
29+
def _parse_pth_file(dir: str, pth_filename: str) -> Iterator[str]:
30+
"""
31+
Mimics a subset of .pth import hook from Lib/site.py
32+
See https://github.com/python/cpython/blob/3.5/Lib/site.py#L146-L185
33+
"""
34+
35+
pth_file = os.path.join(dir, pth_filename)
36+
try:
37+
f = open(pth_file, "r")
38+
except OSError:
39+
return
40+
with f:
41+
for line in f.readlines():
42+
if line.startswith("#"):
43+
# Skip comment lines
44+
continue
45+
if line.startswith(("import ", "import\t")):
46+
# import statements in .pth files are not supported
47+
continue
48+
49+
yield _make_abspath(line.rstrip(), dir)
50+
51+
52+
def _make_abspath(path: str, root: str) -> str:
53+
"""Take a path and make it absolute relative to root if not already absolute."""
54+
if os.path.isabs(path):
55+
return os.path.normpath(path)
56+
else:
57+
return os.path.join(root, os.path.normpath(path))
58+
59+
1660
class ModuleFinderSuite(Suite):
1761

1862
def setUp(self) -> None:

0 commit comments

Comments
 (0)