|
19 | 19 | else:
|
20 | 20 | import tomli as tomllib
|
21 | 21 |
|
22 |
| -from typing import Dict, Iterator, List, NamedTuple, Optional, Set, Tuple, Union |
| 22 | +from typing import Dict, List, NamedTuple, Optional, Set, Tuple, Union |
23 | 23 | from typing_extensions import Final, TypeAlias as _TypeAlias
|
24 | 24 |
|
25 | 25 | from mypy.fscache import FileSystemCache
|
@@ -330,6 +330,9 @@ def _find_module_non_stub_helper(self, components: List[str],
|
330 | 330 | elif not plausible_match and (self.fscache.isdir(dir_path)
|
331 | 331 | or self.fscache.isfile(dir_path + ".py")):
|
332 | 332 | plausible_match = True
|
| 333 | + # If this is not a directory then we can't traverse further into it |
| 334 | + if not self.fscache.isdir(dir_path): |
| 335 | + break |
333 | 336 | if is_legacy_bundled_package(components[0], self.python_major_ver):
|
334 | 337 | if (len(components) == 1
|
335 | 338 | or (self.find_module(components[0]) is
|
@@ -724,97 +727,32 @@ def default_lib_path(data_dir: str,
|
724 | 727 |
|
725 | 728 |
|
726 | 729 | @functools.lru_cache(maxsize=None)
|
727 |
| -def get_prefixes(python_executable: Optional[str]) -> Tuple[str, str]: |
728 |
| - """Get the sys.base_prefix and sys.prefix for the given python. |
729 |
| -
|
730 |
| - This runs a subprocess call to get the prefix paths of the given Python executable. |
731 |
| - To avoid repeatedly calling a subprocess (which can be slow!) we |
732 |
| - lru_cache the results. |
733 |
| - """ |
734 |
| - if python_executable is None: |
735 |
| - return '', '' |
736 |
| - elif python_executable == sys.executable: |
737 |
| - # Use running Python's package dirs |
738 |
| - return pyinfo.getprefixes() |
739 |
| - else: |
740 |
| - # Use subprocess to get the package directory of given Python |
741 |
| - # executable |
742 |
| - return ast.literal_eval( |
743 |
| - subprocess.check_output([python_executable, pyinfo.__file__, 'getprefixes'], |
744 |
| - stderr=subprocess.PIPE).decode()) |
745 |
| - |
746 |
| - |
747 |
| -@functools.lru_cache(maxsize=None) |
748 |
| -def get_site_packages_dirs(python_executable: Optional[str]) -> Tuple[List[str], List[str]]: |
| 730 | +def get_search_dirs(python_executable: Optional[str]) -> List[str]: |
749 | 731 | """Find package directories for given python.
|
750 | 732 |
|
751 |
| - This runs a subprocess call, which generates a list of the egg directories, and the site |
752 |
| - package directories. To avoid repeatedly calling a subprocess (which can be slow!) we |
| 733 | + This runs a subprocess call, which generates a list of the directories in sys.path. |
| 734 | + To avoid repeatedly calling a subprocess (which can be slow!) we |
753 | 735 | lru_cache the results.
|
754 | 736 | """
|
755 | 737 |
|
756 | 738 | if python_executable is None:
|
757 |
| - return [], [] |
| 739 | + return [] |
758 | 740 | elif python_executable == sys.executable:
|
759 | 741 | # Use running Python's package dirs
|
760 |
| - site_packages = pyinfo.getsitepackages() |
| 742 | + sys_path = pyinfo.getsearchdirs() |
761 | 743 | else:
|
762 | 744 | # Use subprocess to get the package directory of given Python
|
763 | 745 | # executable
|
764 | 746 | try:
|
765 |
| - site_packages = ast.literal_eval( |
766 |
| - subprocess.check_output([python_executable, pyinfo.__file__, 'getsitepackages'], |
| 747 | + sys_path = ast.literal_eval( |
| 748 | + subprocess.check_output([python_executable, pyinfo.__file__, 'getsearchdirs'], |
767 | 749 | stderr=subprocess.PIPE).decode())
|
768 | 750 | except OSError as err:
|
769 | 751 | reason = os.strerror(err.errno)
|
770 | 752 | raise CompileError(
|
771 | 753 | [f"mypy: Invalid python executable '{python_executable}': {reason}"]
|
772 | 754 | ) from err
|
773 |
| - return expand_site_packages(site_packages) |
774 |
| - |
775 |
| - |
776 |
| -def expand_site_packages(site_packages: List[str]) -> Tuple[List[str], List[str]]: |
777 |
| - """Expands .pth imports in site-packages directories""" |
778 |
| - egg_dirs: List[str] = [] |
779 |
| - for dir in site_packages: |
780 |
| - if not os.path.isdir(dir): |
781 |
| - continue |
782 |
| - pth_filenames = sorted(name for name in os.listdir(dir) if name.endswith(".pth")) |
783 |
| - for pth_filename in pth_filenames: |
784 |
| - egg_dirs.extend(_parse_pth_file(dir, pth_filename)) |
785 |
| - |
786 |
| - return egg_dirs, site_packages |
787 |
| - |
788 |
| - |
789 |
| -def _parse_pth_file(dir: str, pth_filename: str) -> Iterator[str]: |
790 |
| - """ |
791 |
| - Mimics a subset of .pth import hook from Lib/site.py |
792 |
| - See https://github.com/python/cpython/blob/3.5/Lib/site.py#L146-L185 |
793 |
| - """ |
794 |
| - |
795 |
| - pth_file = os.path.join(dir, pth_filename) |
796 |
| - try: |
797 |
| - f = open(pth_file) |
798 |
| - except OSError: |
799 |
| - return |
800 |
| - with f: |
801 |
| - for line in f.readlines(): |
802 |
| - if line.startswith("#"): |
803 |
| - # Skip comment lines |
804 |
| - continue |
805 |
| - if line.startswith(("import ", "import\t")): |
806 |
| - # import statements in .pth files are not supported |
807 |
| - continue |
808 |
| - |
809 |
| - yield _make_abspath(line.rstrip(), dir) |
810 |
| - |
811 |
| - |
812 |
| -def _make_abspath(path: str, root: str) -> str: |
813 |
| - """Take a path and make it absolute relative to root if not already absolute.""" |
814 |
| - if os.path.isabs(path): |
815 |
| - return os.path.normpath(path) |
816 |
| - else: |
817 |
| - return os.path.join(root, os.path.normpath(path)) |
| 755 | + return sys_path |
818 | 756 |
|
819 | 757 |
|
820 | 758 | def add_py2_mypypath_entries(mypypath: List[str]) -> List[str]:
|
@@ -903,27 +841,21 @@ def compute_search_paths(sources: List[BuildSource],
|
903 | 841 | if options.python_version[0] == 2:
|
904 | 842 | mypypath = add_py2_mypypath_entries(mypypath)
|
905 | 843 |
|
906 |
| - egg_dirs, site_packages = get_site_packages_dirs(options.python_executable) |
907 |
| - base_prefix, prefix = get_prefixes(options.python_executable) |
908 |
| - is_venv = base_prefix != prefix |
909 |
| - for site_dir in site_packages: |
910 |
| - assert site_dir not in lib_path |
911 |
| - if (site_dir in mypypath or |
912 |
| - any(p.startswith(site_dir + os.path.sep) for p in mypypath) or |
913 |
| - os.path.altsep and any(p.startswith(site_dir + os.path.altsep) for p in mypypath)): |
914 |
| - print(f"{site_dir} is in the MYPYPATH. Please remove it.", file=sys.stderr) |
| 844 | + search_dirs = get_search_dirs(options.python_executable) |
| 845 | + for search_dir in search_dirs: |
| 846 | + assert search_dir not in lib_path |
| 847 | + if (search_dir in mypypath or |
| 848 | + any(p.startswith(search_dir + os.path.sep) for p in mypypath) or |
| 849 | + (os.path.altsep |
| 850 | + and any(p.startswith(search_dir + os.path.altsep) for p in mypypath))): |
| 851 | + print(f"{search_dir} is in the MYPYPATH. Please remove it.", file=sys.stderr) |
915 | 852 | print("See https://mypy.readthedocs.io/en/stable/running_mypy.html"
|
916 | 853 | "#how-mypy-handles-imports for more info", file=sys.stderr)
|
917 | 854 | sys.exit(1)
|
918 |
| - elif site_dir in python_path and (is_venv and not site_dir.startswith(prefix)): |
919 |
| - print("{} is in the PYTHONPATH. Please change directory" |
920 |
| - " so it is not.".format(site_dir), |
921 |
| - file=sys.stderr) |
922 |
| - sys.exit(1) |
923 | 855 |
|
924 | 856 | return SearchPaths(python_path=tuple(reversed(python_path)),
|
925 | 857 | mypy_path=tuple(mypypath),
|
926 |
| - package_path=tuple(egg_dirs + site_packages), |
| 858 | + package_path=tuple(search_dirs), |
927 | 859 | typeshed_path=tuple(lib_path))
|
928 | 860 |
|
929 | 861 |
|
|
0 commit comments