versions.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # Copyright 2020 The HuggingFace Team. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. Utilities for working with package versions
  16. """
  17. import importlib.metadata
  18. import operator
  19. import re
  20. import sys
  21. from typing import Optional
  22. from packaging import version
  23. ops = {
  24. "<": operator.lt,
  25. "<=": operator.le,
  26. "==": operator.eq,
  27. "!=": operator.ne,
  28. ">=": operator.ge,
  29. ">": operator.gt,
  30. }
  31. def _compare_versions(op, got_ver, want_ver, requirement, pkg, hint):
  32. if got_ver is None or want_ver is None:
  33. raise ValueError(
  34. f"Unable to compare versions for {requirement}: need={want_ver} found={got_ver}. This is unusual. Consider"
  35. f" reinstalling {pkg}."
  36. )
  37. if not ops[op](version.parse(got_ver), version.parse(want_ver)):
  38. raise ImportError(
  39. f"{requirement} is required for a normal functioning of this module, but found {pkg}=={got_ver}.{hint}"
  40. )
  41. def require_version(requirement: str, hint: Optional[str] = None) -> None:
  42. """
  43. Perform a runtime check of the dependency versions, using the exact same syntax used by pip.
  44. The installed module version comes from the *site-packages* dir via *importlib.metadata*.
  45. Args:
  46. requirement (`str`): pip style definition, e.g., "tokenizers==0.9.4", "tqdm>=4.27", "numpy"
  47. hint (`str`, *optional*): what suggestion to print in case of requirements not being met
  48. Example:
  49. ```python
  50. require_version("pandas>1.1.2")
  51. require_version("numpy>1.18.5", "this is important to have for whatever reason")
  52. ```"""
  53. hint = f"\n{hint}" if hint is not None else ""
  54. # non-versioned check
  55. if re.match(r"^[\w_\-\d]+$", requirement):
  56. pkg, op, want_ver = requirement, None, None
  57. else:
  58. match = re.findall(r"^([^!=<>\s]+)([\s!=<>]{1,2}.+)", requirement)
  59. if not match:
  60. raise ValueError(
  61. "requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23, but"
  62. f" got {requirement}"
  63. )
  64. pkg, want_full = match[0]
  65. want_range = want_full.split(",") # there could be multiple requirements
  66. wanted = {}
  67. for w in want_range:
  68. match = re.findall(r"^([\s!=<>]{1,2})(.+)", w)
  69. if not match:
  70. raise ValueError(
  71. "requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23,"
  72. f" but got {requirement}"
  73. )
  74. op, want_ver = match[0]
  75. wanted[op] = want_ver
  76. if op not in ops:
  77. raise ValueError(f"{requirement}: need one of {list(ops.keys())}, but got {op}")
  78. # special case
  79. if pkg == "python":
  80. got_ver = ".".join([str(x) for x in sys.version_info[:3]])
  81. for op, want_ver in wanted.items():
  82. _compare_versions(op, got_ver, want_ver, requirement, pkg, hint)
  83. return
  84. # check if any version is installed
  85. try:
  86. got_ver = importlib.metadata.version(pkg)
  87. except importlib.metadata.PackageNotFoundError:
  88. raise importlib.metadata.PackageNotFoundError(
  89. f"The '{requirement}' distribution was not found and is required by this application. {hint}"
  90. )
  91. # check that the right version is installed if version number or a range was provided
  92. if want_ver is not None:
  93. for op, want_ver in wanted.items():
  94. _compare_versions(op, got_ver, want_ver, requirement, pkg, hint)
  95. def require_version_core(requirement):
  96. """require_version wrapper which emits a core-specific hint on failure"""
  97. hint = "Try: `pip install transformers -U` or `pip install -e '.[dev]'` if you're working with git main"
  98. return require_version(requirement, hint)