图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
3.4 KiB

  1. import logging
  2. import os
  3. import re
  4. import site
  5. import sys
  6. from typing import List, Optional
  7. logger = logging.getLogger(__name__)
  8. _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile(
  9. r"include-system-site-packages\s*=\s*(?P<value>true|false)"
  10. )
  11. def _running_under_venv() -> bool:
  12. """Checks if sys.base_prefix and sys.prefix match.
  13. This handles PEP 405 compliant virtual environments.
  14. """
  15. return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
  16. def _running_under_legacy_virtualenv() -> bool:
  17. """Checks if sys.real_prefix is set.
  18. This handles virtual environments created with pypa's virtualenv.
  19. """
  20. # pypa/virtualenv case
  21. return hasattr(sys, "real_prefix")
  22. def running_under_virtualenv() -> bool:
  23. """True if we're running inside a virtual environment, False otherwise."""
  24. return _running_under_venv() or _running_under_legacy_virtualenv()
  25. def _get_pyvenv_cfg_lines() -> Optional[List[str]]:
  26. """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines
  27. Returns None, if it could not read/access the file.
  28. """
  29. pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg")
  30. try:
  31. # Although PEP 405 does not specify, the built-in venv module always
  32. # writes with UTF-8. (pypa/pip#8717)
  33. with open(pyvenv_cfg_file, encoding="utf-8") as f:
  34. return f.read().splitlines() # avoids trailing newlines
  35. except OSError:
  36. return None
  37. def _no_global_under_venv() -> bool:
  38. """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion
  39. PEP 405 specifies that when system site-packages are not supposed to be
  40. visible from a virtual environment, `pyvenv.cfg` must contain the following
  41. line:
  42. include-system-site-packages = false
  43. Additionally, log a warning if accessing the file fails.
  44. """
  45. cfg_lines = _get_pyvenv_cfg_lines()
  46. if cfg_lines is None:
  47. # We're not in a "sane" venv, so assume there is no system
  48. # site-packages access (since that's PEP 405's default state).
  49. logger.warning(
  50. "Could not access 'pyvenv.cfg' despite a virtual environment "
  51. "being active. Assuming global site-packages is not accessible "
  52. "in this environment."
  53. )
  54. return True
  55. for line in cfg_lines:
  56. match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line)
  57. if match is not None and match.group("value") == "false":
  58. return True
  59. return False
  60. def _no_global_under_legacy_virtualenv() -> bool:
  61. """Check if "no-global-site-packages.txt" exists beside site.py
  62. This mirrors logic in pypa/virtualenv for determining whether system
  63. site-packages are visible in the virtual environment.
  64. """
  65. site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
  66. no_global_site_packages_file = os.path.join(
  67. site_mod_dir,
  68. "no-global-site-packages.txt",
  69. )
  70. return os.path.exists(no_global_site_packages_file)
  71. def virtualenv_no_global() -> bool:
  72. """Returns a boolean, whether running in venv with no system site-packages."""
  73. # PEP 405 compliance needs to be checked first since virtualenv >=20 would
  74. # return True for both checks, but is only able to use the PEP 405 config.
  75. if _running_under_venv():
  76. return _no_global_under_venv()
  77. if _running_under_legacy_virtualenv():
  78. return _no_global_under_legacy_virtualenv()
  79. return False