-
Notifications
You must be signed in to change notification settings - Fork 6.1k
/
Copy pathpython_environment_check.py
127 lines (113 loc) · 4.89 KB
/
python_environment_check.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Copyright (c) Sebastian Raschka under Apache License 2.0 (see LICENSE.txt).
# Source for "Build a Large Language Model From Scratch"
# - https://www.manning.com/books/build-a-large-language-model-from-scratch
# Code: https://github.com/rasbt/LLMs-from-scratch
from importlib.metadata import PackageNotFoundError, import_module, version as get_version
from os.path import dirname, exists, join, realpath
from packaging.version import parse as version_parse
from packaging.requirements import Requirement
from packaging.specifiers import SpecifierSet
import platform
import sys
if version_parse(platform.python_version()) < version_parse("3.9"):
print("[FAIL] We recommend Python 3.9 or newer but found version %s" % sys.version)
else:
print("[OK] Your Python version is %s" % platform.python_version())
def get_packages(pkgs):
"""
Returns a dictionary mapping package names (in lowercase) to their installed version.
"""
PACKAGE_MODULE_OVERRIDES = {
"tensorflow-cpu": ["tensorflow", "tensorflow_cpu"],
}
result = {}
for p in pkgs:
# Determine possible module names to try.
module_names = PACKAGE_MODULE_OVERRIDES.get(p.lower(), [p])
version_found = None
for module_name in module_names:
try:
imported = import_module(module_name)
version_found = getattr(imported, "__version__", None)
if version_found is None:
try:
version_found = get_version(module_name)
except PackageNotFoundError:
version_found = None
if version_found is not None:
break # Stop if we successfully got a version.
except ImportError:
# Also try replacing hyphens with underscores as a fallback.
alt_module = module_name.replace("-", "_")
if alt_module != module_name:
try:
imported = import_module(alt_module)
version_found = getattr(imported, "__version__", None)
if version_found is None:
try:
version_found = get_version(alt_module)
except PackageNotFoundError:
version_found = None
if version_found is not None:
break
except ImportError:
continue
continue
if version_found is None:
version_found = "0.0"
result[p.lower()] = version_found
return result
def get_requirements_dict():
"""
Parses requirements.txt and returns a dictionary mapping package names (lowercase)
to a specifier string (e.g. ">=2.18.0,<3.0"). It uses packaging.requirements.Requirement
to properly handle environment markers.
"""
PROJECT_ROOT = dirname(realpath(__file__))
PROJECT_ROOT_UP_TWO = dirname(dirname(PROJECT_ROOT))
REQUIREMENTS_FILE = join(PROJECT_ROOT_UP_TWO, "requirements.txt")
if not exists(REQUIREMENTS_FILE):
REQUIREMENTS_FILE = join(PROJECT_ROOT, "requirements.txt")
reqs = {}
with open(REQUIREMENTS_FILE) as f:
for line in f:
# Remove inline comments and trailing whitespace.
# This splits on the first '#' and takes the part before it.
line = line.split("#", 1)[0].strip()
if not line:
continue
try:
req = Requirement(line)
except Exception as e:
print(f"Skipping line due to parsing error: {line} ({e})")
continue
# Evaluate the marker if present.
if req.marker is not None and not req.marker.evaluate():
continue
# Store the package name and its version specifier.
spec = str(req.specifier) if req.specifier else ">=0"
reqs[req.name.lower()] = spec
return reqs
def check_packages(reqs):
"""
Checks the installed versions of packages against the requirements.
"""
installed = get_packages(reqs.keys())
for pkg_name, spec_str in reqs.items():
spec_set = SpecifierSet(spec_str)
actual_ver = installed.get(pkg_name, "0.0")
if actual_ver == "N/A":
continue
actual_ver_parsed = version_parse(actual_ver)
# If the installed version is a pre-release, allow pre-releases in the specifier.
if actual_ver_parsed.is_prerelease:
spec_set.prereleases = True
if actual_ver_parsed not in spec_set:
print(f"[FAIL] {pkg_name} {actual_ver_parsed}, please install a version matching {spec_set}")
else:
print(f"[OK] {pkg_name} {actual_ver_parsed}")
def main():
reqs = get_requirements_dict()
check_packages(reqs)
if __name__ == "__main__":
main()