ComfyUI/utils/install_util.py
NeonScreams f3f989c79f fix: accept PEP 440 multi-part versions in install_util version parser
is_valid_version() previously only matched strict 3-part semver (X.Y.Z),
causing a false ERROR log on startup whenever requirements.txt contained a
2-part constraint such as SQLAlchemy>=2.0.

Steps to reproduce (before fix):
  ComfyUI currently logs at ERROR level on every startup:
  [2026-04-21 16:06:11.527] Invalid version format in requirements.txt: 2.0
  The offending entry is SQLAlchemy>=2.0 (merged in #13316).

Root cause:
  The regex r'^(\d+)\.(\d+)\.(\d+)$' requires exactly three numeric parts.
  PEP 440 treats 2.0 and 2.0.0 as equivalent release identifiers, so the
  constraint is valid but the validator rejected it.

Fix:
  Replace the regex with r'^\d+(\.\d+)*$' which accepts 1-, 2-, and 3-part
  (and longer) all-numeric release identifiers: 2, 2.0, 2.0.0, 1.25.3.
  Also downgrade the log level from ERROR to WARNING, since an unparseable
  entry is skipped gracefully and does not break the install.

Previously issue #12813 was worked around by changing simpleeval>=1.0 to
simpleeval>=1.0.0 in requirements.txt.  This commit fixes the underlying
parser so the same workaround is not needed for future entries.
2026-04-21 16:45:15 -07:00

56 lines
1.8 KiB
Python

from pathlib import Path
import sys
import logging
import re
# The path to the requirements.txt file
requirements_path = Path(__file__).parents[1] / "requirements.txt"
def get_missing_requirements_message():
"""The warning message to display when a package is missing."""
extra = ""
if sys.flags.no_user_site:
extra = "-s "
return f"""
Please install the updated requirements.txt file by running:
{sys.executable} {extra}-m pip install -r {requirements_path}
If you are on the portable package you can run: update\\update_comfyui.bat to solve this problem.
""".strip()
def is_valid_version(version: str) -> bool:
"""Validate if a string is a valid version number.
Accepts PEP 440-style numeric versions with one or more dot-separated
components (e.g. ``2``, ``2.0``, ``2.0.0``, ``1.25.3``).
"""
pattern = r"^\d+(\.\d+)*$"
return bool(re.match(pattern, version))
PACKAGE_VERSIONS = {}
def get_required_packages_versions():
if len(PACKAGE_VERSIONS) > 0:
return PACKAGE_VERSIONS.copy()
out = PACKAGE_VERSIONS
try:
with open(requirements_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip().replace(">=", "==")
s = line.split("==")
if len(s) == 2:
version_str = s[-1]
if not is_valid_version(version_str):
logging.warning(f"Skipping unrecognised version format in requirements.txt: {version_str}")
continue
out[s[0]] = version_str
return out.copy()
except FileNotFoundError:
logging.error("requirements.txt not found.")
return None
except Exception as e:
logging.error(f"Error reading requirements.txt: {e}")
return None