Skip to content

Fix mypy for new virtualenv #10855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions mypy/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from mypy.fscache import FileSystemCache
from mypy.options import Options
from mypy.stubinfo import is_legacy_bundled_package
from mypy import sitepkgs
from mypy import pyinfo

# Paths to be searched in find_module().
SearchPaths = NamedTuple(
Expand Down Expand Up @@ -582,6 +582,27 @@ def default_lib_path(data_dir: str,
return path


@functools.lru_cache(maxsize=None)
def get_prefixes(python_executable: Optional[str]) -> Tuple[str, str]:
"""Get the sys.base_prefix and sys.prefix for the given python.

This runs a subprocess call to get the prefix paths of the given Python executable.
To avoid repeatedly calling a subprocess (which can be slow!) we
lru_cache the results.
"""
if python_executable is None:
return '', ''
elif python_executable == sys.executable:
# Use running Python's package dirs
return pyinfo.getprefixes()
else:
# Use subprocess to get the package directory of given Python
# executable
return ast.literal_eval(
subprocess.check_output([python_executable, pyinfo.__file__, 'getprefixes'],
stderr=subprocess.PIPE).decode())


@functools.lru_cache(maxsize=None)
def get_site_packages_dirs(python_executable: Optional[str]) -> Tuple[List[str], List[str]]:
"""Find package directories for given python.
Expand All @@ -595,12 +616,12 @@ def get_site_packages_dirs(python_executable: Optional[str]) -> Tuple[List[str],
return [], []
elif python_executable == sys.executable:
# Use running Python's package dirs
site_packages = sitepkgs.getsitepackages()
site_packages = pyinfo.getsitepackages()
else:
# Use subprocess to get the package directory of given Python
# executable
site_packages = ast.literal_eval(
subprocess.check_output([python_executable, sitepkgs.__file__],
subprocess.check_output([python_executable, pyinfo.__file__, 'getsitepackages'],
stderr=subprocess.PIPE).decode())
return expand_site_packages(site_packages)

Expand Down Expand Up @@ -736,6 +757,8 @@ def compute_search_paths(sources: List[BuildSource],
mypypath = add_py2_mypypath_entries(mypypath)

egg_dirs, site_packages = get_site_packages_dirs(options.python_executable)
base_prefix, prefix = get_prefixes(options.python_executable)
is_venv = base_prefix != prefix
for site_dir in site_packages:
assert site_dir not in lib_path
if (site_dir in mypypath or
Expand All @@ -745,7 +768,7 @@ def compute_search_paths(sources: List[BuildSource],
print("See https://mypy.readthedocs.io/en/stable/running_mypy.html"
"#how-mypy-handles-imports for more info", file=sys.stderr)
sys.exit(1)
elif site_dir in python_path:
elif site_dir in python_path and (is_venv and not site_dir.startswith(prefix)):
print("{} is in the PYTHONPATH. Please change directory"
" so it is not.".format(site_dir),
file=sys.stderr)
Expand Down
22 changes: 16 additions & 6 deletions mypy/sitepkgs.py → mypy/pyinfo.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
from __future__ import print_function
"""This file is used to find the site packages of a Python executable, which may be Python 2.
"""Utilities to find the site and prefix information of a Python executable, which may be Python 2.

This file MUST remain compatible with Python 2. Since we cannot make any assumptions about the
Python being executed, this module should not use *any* dependencies outside of the standard
library found in Python 2. This file is run each mypy run, so it should be kept as fast as
possible.
"""
import site
import sys

if __name__ == '__main__':
import sys
sys.path = sys.path[1:] # we don't want to pick up mypy.types

import site

MYPY = False
if MYPY:
from typing import List
from typing import List, Tuple


def getprefixes():
# type: () -> Tuple[str, str]
return sys.base_prefix, sys.prefix


def getsitepackages():
Expand All @@ -29,4 +33,10 @@ def getsitepackages():


if __name__ == '__main__':
print(repr(getsitepackages()))
if sys.argv[-1] == 'getsitepackages':
print(repr(getsitepackages()))
elif sys.argv[-1] == 'getprefixes':
print(repr(getprefixes()))
else:
print("ERROR: incorrect argument to pyinfo.py.", file=sys.stderr)
sys.exit(1)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def run(self):
MYPYC_BLACKLIST = tuple(os.path.join('mypy', x) for x in (
# Need to be runnable as scripts
'__main__.py',
'sitepkgs.py',
'pyinfo.py',
os.path.join('dmypy', '__main__.py'),

# Uses __getattr__/__setattr__
Expand Down
9 changes: 4 additions & 5 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
-r build-requirements.txt
attrs>=18.0
flake8>=3.8.1
flake8-bugbear; python_version >= '3.5'
flake8-pyi>=20.5; python_version >= '3.6'
flake8-bugbear
flake8-pyi>=20.5
lxml>=4.4.0
psutil>=4.0
pytest>=6.2.0,<7.0.0
pytest-xdist>=1.34.0,<2.0.0
pytest-forked>=1.3.0,<2.0.0
pytest-cov>=2.10.0,<3.0.0
typing>=3.5.2; python_version < '3.5'
py>=1.5.2
virtualenv<16.7.11
virtualenv>=20.6.0
setuptools!=50
importlib-metadata==0.20
importlib-metadata>=4.6.1,<5.0.0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The very old version of this was causing a failure with the new virtualenv.