compat.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """Stuff that differs in different Python versions and platform
  2. distributions."""
  3. import importlib.resources
  4. import logging
  5. import os
  6. import sys
  7. from typing import IO
  8. __all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"]
  9. logger = logging.getLogger(__name__)
  10. def has_tls() -> bool:
  11. try:
  12. import _ssl # noqa: F401 # ignore unused
  13. return True
  14. except ImportError:
  15. pass
  16. from pip._vendor.urllib3.util import IS_PYOPENSSL
  17. return IS_PYOPENSSL
  18. def get_path_uid(path: str) -> int:
  19. """
  20. Return path's uid.
  21. Does not follow symlinks:
  22. https://github.com/pypa/pip/pull/935#discussion_r5307003
  23. Placed this function in compat due to differences on AIX and
  24. Jython, that should eventually go away.
  25. :raises OSError: When path is a symlink or can't be read.
  26. """
  27. if hasattr(os, "O_NOFOLLOW"):
  28. fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
  29. file_uid = os.fstat(fd).st_uid
  30. os.close(fd)
  31. else: # AIX and Jython
  32. # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW
  33. if not os.path.islink(path):
  34. # older versions of Jython don't have `os.fstat`
  35. file_uid = os.stat(path).st_uid
  36. else:
  37. # raise OSError for parity with os.O_NOFOLLOW above
  38. raise OSError(f"{path} is a symlink; Will not return uid for symlinks")
  39. return file_uid
  40. # The importlib.resources.open_text function was deprecated in 3.11 with suggested
  41. # replacement we use below.
  42. if sys.version_info < (3, 11):
  43. open_text_resource = importlib.resources.open_text
  44. else:
  45. def open_text_resource(
  46. package: str, resource: str, encoding: str = "utf-8", errors: str = "strict"
  47. ) -> IO[str]:
  48. return (importlib.resources.files(package) / resource).open(
  49. "r", encoding=encoding, errors=errors
  50. )
  51. # packages in the stdlib that may have installation metadata, but should not be
  52. # considered 'installed'. this theoretically could be determined based on
  53. # dist.location (py27:`sysconfig.get_paths()['stdlib']`,
  54. # py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may
  55. # make this ineffective, so hard-coding
  56. stdlib_pkgs = {"python", "wsgiref", "argparse"}
  57. # windows detection, covers cpython and ironpython
  58. WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")