-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenv_utils.py
More file actions
134 lines (110 loc) · 4.76 KB
/
env_utils.py
File metadata and controls
134 lines (110 loc) · 4.76 KB
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
128
129
130
131
132
133
134
# env_utils.py
import os
from dotenv import dotenv_values
def summarize_value(value: str) -> str:
"""Return masked form: ****last4 or boolean string."""
lower = value.lower()
if lower in ("true", "false"):
return lower
return "****" + value[-4:] if len(value) > 4 else "****" + value
def doublecheck_env(file_path: str):
"""Check environment variables against a .env file and print summaries."""
if not os.path.exists(file_path):
print(f"Did not find file {file_path}.")
print("This is used to double check the key settings for the notebook.")
print("This is just a check and is not required.\n")
return
parsed = dotenv_values(file_path)
for key in parsed.keys():
current = os.getenv(key)
if current is not None:
print(f"{key}={summarize_value(current)}")
else:
print(f"{key}=<not set>")
# ========== utility to check packages and python based on pyproject.toml =====================================
# Requires: pip install packaging
import sys, tomllib
from pathlib import Path
from importlib import metadata
from packaging.requirements import Requirement
from packaging.specifiers import SpecifierSet
from packaging.version import Version
def _fmt_row(cols, widths):
return " | ".join(str(c).ljust(w) for c, w in zip(cols, widths))
def doublecheck_pkgs(pyproject_path="pyproject.toml", verbose=False):
p = Path(pyproject_path)
if not p.exists():
print(f"ERROR: {pyproject_path} not found.")
return None
# Load pyproject + python requirement
with p.open("rb") as f:
data = tomllib.load(f)
project = data.get("project", {})
python_spec_str = project.get("requires-python") or ">=3.11"
py_ver = Version(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
py_ok = py_ver in SpecifierSet(python_spec_str)
# Load deps (PEP 621)
deps = project.get("dependencies", [])
if not deps:
if verbose or not py_ok:
print("No [project].dependencies found in pyproject.toml.")
print(f"Python {py_ver} {'satisfies' if py_ok else 'DOES NOT satisfy'} requires-python: {python_spec_str}")
print(f"Executable: {sys.executable}")
return None
# Evaluate deps
results = []
problems = []
for dep in deps:
try:
req = Requirement(dep)
name = req.name
spec = str(req.specifier) if req.specifier else "(any)"
except Exception:
name, spec = dep, "(unparsed)"
rec = {"package": name, "required": spec, "installed": "-", "path": "-", "status": "❌ Missing"}
try:
installed_ver = metadata.version(name)
rec["installed"] = installed_ver
try:
dist = metadata.distribution(name)
rec["path"] = str(dist.locate_file(""))
except Exception:
rec["path"] = "(unknown)"
if spec not in ("(any)", "(unparsed)") and any(op in spec for op in "<>="):
sset = SpecifierSet(spec)
if Version(installed_ver) in sset:
rec["status"] = "✅ OK"
else:
rec["status"] = "⚠️ Version mismatch"
else:
rec["status"] = "✅ OK"
except metadata.PackageNotFoundError:
# keep defaults: installed "-", status "❌ Missing"
pass
results.append(rec)
if rec["status"] != "✅ OK":
problems.append(rec)
should_print = verbose or (not py_ok) or bool(problems)
if should_print:
# Python status
print(f"Python {py_ver} {'satisfies' if py_ok else 'DOES NOT satisfy'} requires-python: {python_spec_str}")
# Table (no hints column)
headers = ["package", "required", "installed", "status", "path"]
def short_path(s, maxlen=80):
s = str(s)
return s if len(s) <= maxlen else ("…" + s[-(maxlen-1):])
rows = [[r["package"], r["required"], r["installed"], r["status"], short_path(r["path"])] for r in results]
widths = [max(len(h), *(len(str(row[i])) for row in rows)) for i, h in enumerate(headers)]
print(_fmt_row(headers, widths))
print(_fmt_row(["-"*w for w in widths], widths))
for row in rows:
print(_fmt_row(row, widths))
# Summarize issues without prescribing a tool
if problems:
print("\nIssues detected:")
for r in problems:
print(f"- {r['package']}: {r['status']} (required {r['required']}, installed {r['installed']}, path {r['path']})")
if verbose or problems or not py_ok:
print("\nEnvironment:")
print(f"- Executable: {sys.executable}")
return None