Skip to content

Commit

Permalink
Fix site.getsitepackages() broken on python2 on debian (#2108)
Browse files Browse the repository at this point in the history
  • Loading branch information
freundTech authored May 5, 2021
1 parent 2a080a0 commit 7bfd444
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 15 deletions.
1 change: 1 addition & 0 deletions docs/changelog/2105.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``site.getsitepackages()`` broken on python2 on debian - by :user:`freundTech`.
42 changes: 34 additions & 8 deletions src/virtualenv/create/via_global_ref/builtin/python2/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@

def main():
"""Patch what needed, and invoke the original site.py"""
here = __file__ # the distutils.install patterns will be injected relative to this site.py, save it here
config = read_pyvenv()
sys.real_prefix = sys.base_prefix = config["base-prefix"]
sys.base_exec_prefix = config["base-exec-prefix"]
sys.base_executable = config["base-executable"]
global_site_package_enabled = config.get("include-system-site-packages", False) == "true"
rewrite_standard_library_sys_path()
disable_user_site_package()
load_host_site()
load_host_site(here)
if global_site_package_enabled:
add_global_site_package()
rewrite_getsitepackages(here)


def load_host_site():
def load_host_site(here):
"""trigger reload of site.py - now it will use the standard library instance that will take care of init"""
# we have a duality here, we generate the platform and pure library path based on what distutils.install specifies
# because this is what pip will be using; the host site.py though may contain it's own pattern for where the
Expand All @@ -36,23 +38,26 @@ def load_host_site():
# to facilitate when the two match, or not we first reload the site.py, now triggering the import of host site.py,
# as this will ensure that initialization code within host site.py runs

here = __file__ # the distutils.install patterns will be injected relative to this site.py, save it here

# ___RELOAD_CODE___

# and then if the distutils site packages are not on the sys.path we add them via add_site_dir; note we must add
# them by invoking add_site_dir to trigger the processing of pth files

add_site_dir = sys.modules["site"].addsitedir
for path in get_site_packages_dirs(here):
add_site_dir(path)


def get_site_packages_dirs(here):
import json
import os

site_packages = r"""
___EXPECTED_SITE_PACKAGES___
"""
import json

add_site_dir = sys.modules["site"].addsitedir
for path in json.loads(site_packages):
full_path = os.path.abspath(os.path.join(here, path.encode("utf-8")))
add_site_dir(full_path)
yield os.path.abspath(os.path.join(here, path.encode("utf-8")))


sep = "\\" if sys.platform == "win32" else "/" # no os module here yet - poor mans version
Expand Down Expand Up @@ -161,4 +166,25 @@ def add_global_site_package():
site.PREFIXES = orig_prefixes + site.PREFIXES


# Debian and it's derivatives patch this function. We undo the damage
def rewrite_getsitepackages(here):
site = sys.modules["site"]

site_package_dirs = get_site_packages_dirs(here)
orig_getsitepackages = site.getsitepackages

def getsitepackages():
sitepackages = orig_getsitepackages()
if sys.prefix not in site.PREFIXES or sys.exec_prefix not in site.PREFIXES:
# Someone messed with the prefixes, so we stop patching
return sitepackages
for path in site_package_dirs:
if path not in sitepackages:
sitepackages.insert(0, path)

return sitepackages

site.getsitepackages = getsitepackages


main()
43 changes: 36 additions & 7 deletions tests/unit/create/test_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging
import os
import shutil
import site
import stat
import subprocess
import sys
Expand Down Expand Up @@ -625,15 +626,11 @@ def test_pth_in_site_vs_PYTHONPATH(tmp_path):


def test_getsitepackages_system_site(tmp_path):
import site

old_prefixes = site.PREFIXES
site.PREFIXES = [sys.base_prefix, sys.base_exec_prefix]
system_site_packages = site.getsitepackages()
site.PREFIXES = old_prefixes

# Test without --system-site-packages
session = cli_run([ensure_text(str(tmp_path))])

system_site_packages = get_expected_system_site_packages(session)

out = subprocess.check_output(
[str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"],
universal_newlines=True,
Expand All @@ -645,6 +642,9 @@ def test_getsitepackages_system_site(tmp_path):

# Test with --system-site-packages
session = cli_run([ensure_text(str(tmp_path)), "--system-site-packages"])

system_site_packages = get_expected_system_site_packages(session)

out = subprocess.check_output(
[str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"],
universal_newlines=True,
Expand All @@ -653,3 +653,32 @@ def test_getsitepackages_system_site(tmp_path):

for system_site_package in system_site_packages:
assert system_site_package in site_packages


def get_expected_system_site_packages(session):
base_prefix = session.creator.pyenv_cfg["base-prefix"]
base_exec_prefix = session.creator.pyenv_cfg["base-exec-prefix"]
old_prefixes = site.PREFIXES
site.PREFIXES = [base_prefix, base_exec_prefix]
system_site_packages = site.getsitepackages()
site.PREFIXES = old_prefixes

return system_site_packages


def test_get_site_packages(tmp_path):
case_sensitive = fs_is_case_sensitive()
session = cli_run([ensure_text(str(tmp_path))])
env_site_packages = [str(session.creator.purelib), str(session.creator.platlib)]
out = subprocess.check_output(
[str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"],
universal_newlines=True,
)
site_packages = ast.literal_eval(out)

if not case_sensitive:
env_site_packages = [x.lower() for x in env_site_packages]
site_packages = [x.lower() for x in site_packages]

for env_site_package in env_site_packages:
assert env_site_package in site_packages

0 comments on commit 7bfd444

Please sign in to comment.