Skip to content
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
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