Files
cellxgene/server/eb/check_requirements.py
Matt Weiden 730410c5e1 Autoformat python to fix lint errors (#1470)
* Autoformat python to fix lint errors

* Fix lint errors not caught by black
2020-05-12 13:19:38 -07:00

99 lines
3.3 KiB
Python

"""This is a simple script to ensure the custom requirements.txt do not violate
the server requirements.txt. A hosted cellxgene deployment may specify the exact
version requirements on all the modules, and may add additional modules.
This script is meant to aid in making that list of custom requirements easier to maintain.
If cellxgene adds a new dependency, or changes the version requirements of an existing
dependency, then this script can check if the custom requirements are still valid"""
import sys
import requirements
from packaging.version import Version
import pkg_resources
def check(expected, custom):
"""checks that the custom requirements meet all the requirements of the expected requirements.
The custom set of requirements may contain additional entries than expected.
The requirements in custom must all be exact (==).
An expected requirement must be present in custom, and must match all the specs
for that requirement.
expected : name of the expected requirement.txt file
custom : name of the custom requirements.txt file
"""
edict = parse_requirements(expected)
cdict = parse_requirements(custom)
okay = True
# cdict must only have exact requirements (==)
for cname, cspecs in cdict.items():
if len(cspecs) != 1 or cspecs[0][0] != "==":
print(f"Error, spec must be an exact requirement {custom}: {cname} {str(cspecs)}")
okay = False
for ename, especs in edict.items():
if ename not in cdict:
print(f"Error, missing requirement from {custom}: {ename} {str(especs)}")
okay = False
continue
cver = Version(cdict[ename][0][1])
for espec in especs:
rokay = check_version(cver, espec[0], Version(espec[1]))
if not rokay:
print(f"Error, failed requirement from {custom}: {ename} {espec}, {cver}")
okay = False
if okay:
print("requirements check successful")
sys.exit(0)
else:
sys.exit(1)
def parse_requirements(fname):
"""Read a requirements file and return a dict of modules name / specification"""
try:
with open(fname, "r") as fd:
try:
# pylint: disable=no-member
rdict = {req.name: req.specs for req in requirements.parse(fd)}
except pkg_resources.RequirementParseError:
print(f"Unable to parse the requirements file: {fname}")
sys.exit(1)
except Exception as e:
print(f"Unable to open file {fname}: {str(e)}")
sys.exit(1)
return rdict
# pylint: disable=too-many-return-statements
def check_version(cver, optype, ever):
"""
Simple version check.
Note: There is more complexity to comparing version (PEP440).
However the use cases in cellxgene are limited, and do not require a general solution.
"""
if optype == "==":
return cver == ever
if optype == "!=":
return cver != ever
if optype == ">=":
return cver >= ever
if optype == ">":
return cver > ever
if optype == "<=":
return cver <= ever
if optype == "<":
return cver < ever
print(f"Error, optype not handled: {optype}")
return False
if __name__ == "__main__":
check(sys.argv[1], sys.argv[2])