r"""
Dependency checking
"""
import importlib
import re
import types
from abc import abstractmethod
from typing import Optional
from packaging.version import parse
from . import version
from .utils import config, run_command
[文档]class Checker:
r"""
Checks availability and version of a dependency
Parameters
----------
name
Name of the dependency
vmin
Minimal required version
install_hint
Install hint message to be printed if dependency is unavailable
"""
def __init__(
self, name: str, vmin: Optional[str] = None,
install_hint: Optional[str] = None
) -> None:
self.name = name
self.vmin = parse(vmin) if vmin else vmin
vreq = f" (>={self.vmin})" if self.vmin else ""
self.vreq_hint = f"This function relies on {self.name}{vreq}."
self.install_hint = install_hint
[文档] @abstractmethod
def check(self) -> None:
r"""
Check availability and version
"""
raise NotImplementedError # pragma: no cover
[文档]class ModuleChecker(Checker):
r"""
Checks availability and version of a Python module dependency
Parameters
----------
name
Name of the dependency
vmin
Minimal required version
install_hint
Install hint message to be printed if dependency is unavailable
"""
def __init__(
self, name: str, package_name: Optional[str] = None,
vmin: Optional[str] = None, install_hint: Optional[str] = None
) -> None:
super().__init__(name, vmin, install_hint)
self.package_name = package_name or name
[文档] def check(self) -> None:
try:
importlib.import_module(self.name)
except ModuleNotFoundError as e:
raise RuntimeError(" ".join([self.vreq_hint, self.install_hint])) from e
v = parse(version(self.package_name))
if self.vmin and v < self.vmin:
raise RuntimeError(" ".join([
self.vreq_hint, f"Detected version is {v}.",
"Please install a newer version.", self.install_hint
]))
[文档]class CmdChecker(Checker):
r"""
Checks availability and version of a command line dependency
Parameters
----------
name
Name of the dependency
cmd
Command used to check version
vregex
Regular expression used to extract version from command output
vmin
Minimal required version
install_hint
Install hint message to be printed if dependency is unavailable
"""
def __init__(
self, name: str, cmd: str, vregex: str,
vmin: Optional[str] = None, install_hint: Optional[str] = None
) -> None:
super().__init__(name, vmin=vmin, install_hint=install_hint)
self.cmd = cmd
self.vregex = vregex
[文档] def check(self) -> None:
output_lines = run_command(
self.cmd, log_command=False, print_output=False,
err_message={
127: " ".join([self.vreq_hint, self.install_hint])
}
)
for output_line in output_lines:
v = re.search(self.vregex, output_line)
if v:
v = parse(v.groups()[0])
break
else:
v = None
if self.vmin and v < self.vmin:
raise RuntimeError(" ".join([
self.vreq_hint, f"Detected version is {v}.",
"Please install a newer version.", self.install_hint
]))
INSTALL_HINTS = types.SimpleNamespace(
bedtools=
"You may install bedtools following the guide from "
"https://bedtools.readthedocs.io/en/latest/content/installation.html, "
"or use `conda install -c bioconda bedtools` "
"if a conda environment is being used.",
plotly=
"You may install plotly following the guide from "
"https://plotly.com/python/getting-started/, "
"or use `conda install -c plotly plotly` "
"if a conda environment is being used."
)
CHECKERS = dict(
bedtools=CmdChecker(
"bedtools", f"{config.BEDTOOLS_PATH or 'bedtools'} --version", r"v([0-9\.]+)",
vmin="2.29.2", install_hint=INSTALL_HINTS.bedtools
),
plotly=ModuleChecker(
"plotly",
vmin=None, install_hint=INSTALL_HINTS.plotly
)
)
[文档]def check_deps(*args) -> None:
r"""
Check whether certain dependencies are installed
Parameters
----------
args
A list of dependencies to check
"""
for item in args:
CHECKERS[item].check()