packaging.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import functools
  2. import logging
  3. import re
  4. from typing import NewType, Optional, Tuple, cast
  5. from pip._vendor.packaging import specifiers, version
  6. from pip._vendor.packaging.requirements import Requirement
  7. NormalizedExtra = NewType("NormalizedExtra", str)
  8. logger = logging.getLogger(__name__)
  9. @functools.lru_cache(maxsize=32)
  10. def check_requires_python(
  11. requires_python: Optional[str], version_info: Tuple[int, ...]
  12. ) -> 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. @functools.lru_cache(maxsize=2048)
  28. def get_requirement(req_string: str) -> Requirement:
  29. """Construct a packaging.Requirement object with caching"""
  30. # Parsing requirement strings is expensive, and is also expected to happen
  31. # with a low diversity of different arguments (at least relative the number
  32. # constructed). This method adds a cache to requirement object creation to
  33. # minimize repeated parsing of the same string to construct equivalent
  34. # Requirement objects.
  35. return Requirement(req_string)
  36. def safe_extra(extra: str) -> NormalizedExtra:
  37. """Convert an arbitrary string to a standard 'extra' name
  38. Any runs of non-alphanumeric characters are replaced with a single '_',
  39. and the result is always lowercased.
  40. This function is duplicated from ``pkg_resources``. Note that this is not
  41. the same to either ``canonicalize_name`` or ``_egg_link_name``.
  42. """
  43. return cast(NormalizedExtra, re.sub("[^A-Za-z0-9.-]+", "_", extra).lower())