|
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