packaging.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import logging
  2. from email.message import Message
  3. from email.parser import FeedParser
  4. from typing import Optional, Tuple
  5. from pip._vendor import pkg_resources
  6. from pip._vendor.packaging import specifiers, version
  7. from pip._vendor.pkg_resources import Distribution
  8. from pip._internal.exceptions import NoneMetadataError
  9. from pip._internal.utils.misc import display_path
  10. logger = logging.getLogger(__name__)
  11. def check_requires_python(requires_python, version_info):
  12. # type: (Optional[str], Tuple[int, ...]) -> bool
  13. """
  14. Check if the given Python version matches a "Requires-Python" specifier.
  15. :param version_info: A 3-tuple of ints representing a Python
  16. major-minor-micro version to check (e.g. `sys.version_info[:3]`).
  17. :return: `True` if the given Python version satisfies the requirement.
  18. Otherwise, return `False`.
  19. :raises InvalidSpecifier: If `requires_python` has an invalid format.
  20. """
  21. if requires_python is None:
  22. # The package provides no information
  23. return True
  24. requires_python_specifier = specifiers.SpecifierSet(requires_python)
  25. python_version = version.parse(".".join(map(str, version_info)))
  26. return python_version in requires_python_specifier
  27. def get_metadata(dist):
  28. # type: (Distribution) -> Message
  29. """
  30. :raises NoneMetadataError: if the distribution reports `has_metadata()`
  31. True but `get_metadata()` returns None.
  32. """
  33. metadata_name = "METADATA"
  34. if isinstance(dist, pkg_resources.DistInfoDistribution) and dist.has_metadata(
  35. metadata_name
  36. ):
  37. metadata = dist.get_metadata(metadata_name)
  38. elif dist.has_metadata("PKG-INFO"):
  39. metadata_name = "PKG-INFO"
  40. metadata = dist.get_metadata(metadata_name)
  41. else:
  42. logger.warning("No metadata found in %s", display_path(dist.location))
  43. metadata = ""
  44. if metadata is None:
  45. raise NoneMetadataError(dist, metadata_name)
  46. feed_parser = FeedParser()
  47. # The following line errors out if with a "NoneType" TypeError if
  48. # passed metadata=None.
  49. feed_parser.feed(metadata)
  50. return feed_parser.close()
  51. def get_requires_python(dist):
  52. # type: (pkg_resources.Distribution) -> Optional[str]
  53. """
  54. Return the "Requires-Python" metadata for a distribution, or None
  55. if not present.
  56. """
  57. pkg_info_dict = get_metadata(dist)
  58. requires_python = pkg_info_dict.get("Requires-Python")
  59. if requires_python is not None:
  60. # Convert to a str to satisfy the type checker, since requires_python
  61. # can be a Header object.
  62. requires_python = str(requires_python)
  63. return requires_python
  64. def get_installer(dist):
  65. # type: (Distribution) -> str
  66. if dist.has_metadata("INSTALLER"):
  67. for line in dist.get_metadata_lines("INSTALLER"):
  68. if line.strip():
  69. return line.strip()
  70. return ""