118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
|
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
"""
|
||
|
Utilities for working with package versions
|
||
|
"""
|
||
|
|
||
|
import importlib.metadata
|
||
|
import operator
|
||
|
import re
|
||
|
import sys
|
||
|
from typing import Optional
|
||
|
|
||
|
from packaging import version
|
||
|
|
||
|
|
||
|
ops = {
|
||
|
"<": operator.lt,
|
||
|
"<=": operator.le,
|
||
|
"==": operator.eq,
|
||
|
"!=": operator.ne,
|
||
|
">=": operator.ge,
|
||
|
">": operator.gt,
|
||
|
}
|
||
|
|
||
|
|
||
|
def _compare_versions(op, got_ver, want_ver, requirement, pkg, hint):
|
||
|
if got_ver is None or want_ver is None:
|
||
|
raise ValueError(
|
||
|
f"Unable to compare versions for {requirement}: need={want_ver} found={got_ver}. This is unusual. Consider"
|
||
|
f" reinstalling {pkg}."
|
||
|
)
|
||
|
if not ops[op](version.parse(got_ver), version.parse(want_ver)):
|
||
|
raise ImportError(
|
||
|
f"{requirement} is required for a normal functioning of this module, but found {pkg}=={got_ver}.{hint}"
|
||
|
)
|
||
|
|
||
|
|
||
|
def require_version(requirement: str, hint: Optional[str] = None) -> None:
|
||
|
"""
|
||
|
Perform a runtime check of the dependency versions, using the exact same syntax used by pip.
|
||
|
|
||
|
The installed module version comes from the *site-packages* dir via *importlib.metadata*.
|
||
|
|
||
|
Args:
|
||
|
requirement (`str`): pip style definition, e.g., "tokenizers==0.9.4", "tqdm>=4.27", "numpy"
|
||
|
hint (`str`, *optional*): what suggestion to print in case of requirements not being met
|
||
|
|
||
|
Example:
|
||
|
|
||
|
```python
|
||
|
require_version("pandas>1.1.2")
|
||
|
require_version("numpy>1.18.5", "this is important to have for whatever reason")
|
||
|
```"""
|
||
|
|
||
|
hint = f"\n{hint}" if hint is not None else ""
|
||
|
|
||
|
# non-versioned check
|
||
|
if re.match(r"^[\w_\-\d]+$", requirement):
|
||
|
pkg, op, want_ver = requirement, None, None
|
||
|
else:
|
||
|
match = re.findall(r"^([^!=<>\s]+)([\s!=<>]{1,2}.+)", requirement)
|
||
|
if not match:
|
||
|
raise ValueError(
|
||
|
"requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23, but"
|
||
|
f" got {requirement}"
|
||
|
)
|
||
|
pkg, want_full = match[0]
|
||
|
want_range = want_full.split(",") # there could be multiple requirements
|
||
|
wanted = {}
|
||
|
for w in want_range:
|
||
|
match = re.findall(r"^([\s!=<>]{1,2})(.+)", w)
|
||
|
if not match:
|
||
|
raise ValueError(
|
||
|
"requirement needs to be in the pip package format, .e.g., package_a==1.23, or package_b>=1.23,"
|
||
|
f" but got {requirement}"
|
||
|
)
|
||
|
op, want_ver = match[0]
|
||
|
wanted[op] = want_ver
|
||
|
if op not in ops:
|
||
|
raise ValueError(f"{requirement}: need one of {list(ops.keys())}, but got {op}")
|
||
|
|
||
|
# special case
|
||
|
if pkg == "python":
|
||
|
got_ver = ".".join([str(x) for x in sys.version_info[:3]])
|
||
|
for op, want_ver in wanted.items():
|
||
|
_compare_versions(op, got_ver, want_ver, requirement, pkg, hint)
|
||
|
return
|
||
|
|
||
|
# check if any version is installed
|
||
|
try:
|
||
|
got_ver = importlib.metadata.version(pkg)
|
||
|
except importlib.metadata.PackageNotFoundError:
|
||
|
raise importlib.metadata.PackageNotFoundError(
|
||
|
f"The '{requirement}' distribution was not found and is required by this application. {hint}"
|
||
|
)
|
||
|
|
||
|
# check that the right version is installed if version number or a range was provided
|
||
|
if want_ver is not None:
|
||
|
for op, want_ver in wanted.items():
|
||
|
_compare_versions(op, got_ver, want_ver, requirement, pkg, hint)
|
||
|
|
||
|
|
||
|
def require_version_core(requirement):
|
||
|
"""require_version wrapper which emits a core-specific hint on failure"""
|
||
|
hint = "Try: `pip install transformers -U` or `pip install -e '.[dev]'` if you're working with git main"
|
||
|
return require_version(requirement, hint)
|