79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
|
from __future__ import annotations
|
||
|
|
||
|
"""Utilities to find the site and prefix information of a Python executable.
|
||
|
|
||
|
This file MUST remain compatible with all Python 3.8+ versions. 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 3.8. This file is run each mypy run, so it should be kept
|
||
|
as fast as possible.
|
||
|
"""
|
||
|
import sys
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
# HACK: We don't want to pick up mypy.types as the top-level types
|
||
|
# module. This could happen if this file is run as a script.
|
||
|
# This workaround fixes this for Python versions before 3.11.
|
||
|
if sys.version_info < (3, 11):
|
||
|
old_sys_path = sys.path
|
||
|
sys.path = sys.path[1:]
|
||
|
import types # noqa: F401
|
||
|
|
||
|
sys.path = old_sys_path
|
||
|
|
||
|
import os
|
||
|
import site
|
||
|
import sysconfig
|
||
|
|
||
|
|
||
|
def getsitepackages() -> list[str]:
|
||
|
res = []
|
||
|
if hasattr(site, "getsitepackages"):
|
||
|
res.extend(site.getsitepackages())
|
||
|
|
||
|
if hasattr(site, "getusersitepackages") and site.ENABLE_USER_SITE:
|
||
|
res.insert(0, site.getusersitepackages())
|
||
|
else:
|
||
|
res = [sysconfig.get_paths()["purelib"]]
|
||
|
return res
|
||
|
|
||
|
|
||
|
def getsyspath() -> list[str]:
|
||
|
# Do not include things from the standard library
|
||
|
# because those should come from typeshed.
|
||
|
stdlib_zip = os.path.join(
|
||
|
sys.base_exec_prefix,
|
||
|
getattr(sys, "platlibdir", "lib"),
|
||
|
f"python{sys.version_info.major}{sys.version_info.minor}.zip",
|
||
|
)
|
||
|
stdlib = sysconfig.get_path("stdlib")
|
||
|
stdlib_ext = os.path.join(stdlib, "lib-dynload")
|
||
|
excludes = {stdlib_zip, stdlib, stdlib_ext}
|
||
|
|
||
|
# Drop the first entry of sys.path
|
||
|
# - If pyinfo.py is executed as a script (in a subprocess), this is the directory
|
||
|
# containing pyinfo.py
|
||
|
# - Otherwise, if mypy launched via console script, this is the directory of the script
|
||
|
# - Otherwise, if mypy launched via python -m mypy, this is the current directory
|
||
|
# In all these cases, it is desirable to drop the first entry
|
||
|
# Note that mypy adds the cwd to SearchPaths.python_path, so we still find things on the
|
||
|
# cwd consistently (the return value here sets SearchPaths.package_path)
|
||
|
|
||
|
# Python 3.11 adds a "safe_path" flag wherein Python won't automatically prepend
|
||
|
# anything to sys.path. In this case, the first entry of sys.path is no longer special.
|
||
|
offset = 0 if sys.version_info >= (3, 11) and sys.flags.safe_path else 1
|
||
|
|
||
|
abs_sys_path = (os.path.abspath(p) for p in sys.path[offset:])
|
||
|
return [p for p in abs_sys_path if p not in excludes]
|
||
|
|
||
|
|
||
|
def getsearchdirs() -> tuple[list[str], list[str]]:
|
||
|
return (getsyspath(), getsitepackages())
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
if sys.argv[-1] == "getsearchdirs":
|
||
|
print(repr(getsearchdirs()))
|
||
|
else:
|
||
|
print("ERROR: incorrect argument to pyinfo.py.", file=sys.stderr)
|
||
|
sys.exit(1)
|