2015-11-17 08:18:42 +00:00
|
|
|
#!/usr/bin/env python3
|
2022-11-16 12:00:35 +00:00
|
|
|
"""Generate updated constraint and requirements files."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
2019-10-09 23:16:29 +00:00
|
|
|
import difflib
|
2015-11-17 08:18:42 +00:00
|
|
|
import importlib
|
|
|
|
import os
|
2019-10-09 23:16:29 +00:00
|
|
|
from pathlib import Path
|
2015-11-17 08:18:42 +00:00
|
|
|
import pkgutil
|
|
|
|
import re
|
2015-12-18 07:51:34 +00:00
|
|
|
import sys
|
2022-11-16 12:00:35 +00:00
|
|
|
from typing import Any
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2019-12-09 15:24:03 +00:00
|
|
|
from homeassistant.util.yaml.loader import load_yaml
|
2020-08-29 06:23:55 +00:00
|
|
|
from script.hassfest.model import Integration
|
2019-12-09 15:24:03 +00:00
|
|
|
|
2022-05-26 00:54:49 +00:00
|
|
|
if sys.version_info >= (3, 11):
|
|
|
|
import tomllib
|
|
|
|
else:
|
|
|
|
import tomli as tomllib
|
|
|
|
|
2016-07-02 18:22:51 +00:00
|
|
|
COMMENT_REQUIREMENTS = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"Adafruit_BBIO",
|
2020-08-26 12:50:14 +00:00
|
|
|
"avea", # depends on bluepy
|
2019-07-31 19:25:30 +00:00
|
|
|
"avion",
|
|
|
|
"beacontools",
|
2020-08-26 12:50:14 +00:00
|
|
|
"beewi_smartclim", # depends on bluepy
|
2019-07-31 19:25:30 +00:00
|
|
|
"bluepy",
|
|
|
|
"decora",
|
2020-09-11 19:38:32 +00:00
|
|
|
"decora_wifi",
|
2019-07-31 19:25:30 +00:00
|
|
|
"evdev",
|
|
|
|
"face_recognition",
|
|
|
|
"opencv-python-headless",
|
|
|
|
"pybluez",
|
|
|
|
"pycups",
|
|
|
|
"python-eq3bt",
|
2020-01-28 08:35:41 +00:00
|
|
|
"python-gammu",
|
2019-07-31 19:25:30 +00:00
|
|
|
"python-lirc",
|
|
|
|
"pyuserinput",
|
2019-08-19 14:56:57 +00:00
|
|
|
"tensorflow",
|
2020-08-07 06:56:28 +00:00
|
|
|
"tf-models-official",
|
2016-07-02 18:22:51 +00:00
|
|
|
)
|
|
|
|
|
2021-08-30 15:20:02 +00:00
|
|
|
COMMENT_REQUIREMENTS_NORMALIZED = {
|
|
|
|
commented.lower().replace("_", "-") for commented in COMMENT_REQUIREMENTS
|
|
|
|
}
|
|
|
|
|
2021-02-25 08:48:19 +00:00
|
|
|
IGNORE_PIN = ("colorlog>2.1,<3", "urllib3")
|
2017-01-21 23:31:10 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
URL_PIN = (
|
|
|
|
"https://developers.home-assistant.io/docs/"
|
|
|
|
"creating_platform_code_review.html#1-requirements"
|
|
|
|
)
|
2017-01-22 16:34:00 +00:00
|
|
|
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
CONSTRAINT_PATH = os.path.join(
|
|
|
|
os.path.dirname(__file__), "../homeassistant/package_constraints.txt"
|
|
|
|
)
|
2017-08-05 06:06:10 +00:00
|
|
|
CONSTRAINT_BASE = """
|
2021-12-15 11:28:04 +00:00
|
|
|
# Constrain pycryptodome to avoid vulnerability
|
|
|
|
# see https://github.com/home-assistant/core/pull/16238
|
2018-08-28 10:49:50 +00:00
|
|
|
pycryptodome>=3.6.6
|
|
|
|
|
2021-11-04 10:21:30 +00:00
|
|
|
# Constrain urllib3 to ensure we deal with CVE-2020-26137 and CVE-2021-33503
|
|
|
|
urllib3>=1.26.5
|
2020-02-20 18:19:09 +00:00
|
|
|
|
2021-02-18 11:31:07 +00:00
|
|
|
# Constrain httplib2 to protect against GHSA-93xj-8mrv-444m
|
|
|
|
# https://github.com/advisories/GHSA-93xj-8mrv-444m
|
|
|
|
httplib2>=0.19.0
|
2020-06-01 07:44:18 +00:00
|
|
|
|
2022-01-11 16:25:50 +00:00
|
|
|
# gRPC is an implicit dependency that we want to make explicit so we manage
|
|
|
|
# upgrades intentionally. It is a large package to build from source and we
|
|
|
|
# want to ensure we have wheels built.
|
2022-12-06 20:57:06 +00:00
|
|
|
grpcio==1.51.1
|
|
|
|
grpcio-status==1.51.1
|
2022-01-05 21:50:11 +00:00
|
|
|
|
2022-01-13 17:25:28 +00:00
|
|
|
# libcst >=0.4.0 requires a newer Rust than we currently have available,
|
|
|
|
# thus our wheels builds fail. This pins it to the last working version,
|
|
|
|
# which at this point satisfies our needs.
|
|
|
|
libcst==0.3.23
|
|
|
|
|
2018-02-27 09:58:45 +00:00
|
|
|
# This is a old unmaintained library and is replaced with pycryptodome
|
|
|
|
pycrypto==1000000000.0.0
|
2020-08-26 08:20:14 +00:00
|
|
|
|
2020-08-26 14:53:22 +00:00
|
|
|
# To remove reliance on typing
|
|
|
|
btlewrap>=0.0.10
|
|
|
|
|
2020-08-26 12:51:41 +00:00
|
|
|
# This overrides a built-in Python package
|
|
|
|
enum34==1000000000.0.0
|
|
|
|
typing==1000000000.0.0
|
2020-08-26 08:20:14 +00:00
|
|
|
uuid==1000000000.0.0
|
2020-08-26 12:51:41 +00:00
|
|
|
|
2021-08-28 13:00:14 +00:00
|
|
|
# regex causes segfault with version 2021.8.27
|
|
|
|
# https://bitbucket.org/mrabarnett/mrab-regex/issues/421/2021827-results-in-fatal-python-error
|
|
|
|
# This is fixed in 2021.8.28
|
|
|
|
regex==2021.8.28
|
2021-09-09 13:33:09 +00:00
|
|
|
|
2022-01-24 09:43:39 +00:00
|
|
|
# httpx requires httpcore, and httpcore requires anyio and h11, but the version constraints on
|
|
|
|
# these requirements are quite loose. As the entire stack has some outstanding issues, and
|
|
|
|
# even newer versions seem to introduce new issues, it's useful for us to pin all these
|
|
|
|
# requirements so we can directly link HA versions to these library versions.
|
2022-11-26 20:04:22 +00:00
|
|
|
anyio==3.6.2
|
|
|
|
h11==0.14.0
|
2023-01-03 01:45:52 +00:00
|
|
|
httpcore==0.16.3
|
2021-10-28 19:32:22 +00:00
|
|
|
|
2022-02-17 13:58:24 +00:00
|
|
|
# Ensure we have a hyperframe version that works in Python 3.10
|
|
|
|
# 5.2.0 fixed a collections abc deprecation
|
|
|
|
hyperframe>=5.2.0
|
|
|
|
|
2022-06-22 17:04:39 +00:00
|
|
|
# Ensure we run compatible with musllinux build env
|
2022-08-16 14:18:40 +00:00
|
|
|
numpy==1.23.2
|
2022-06-22 17:04:39 +00:00
|
|
|
|
2022-01-14 08:25:28 +00:00
|
|
|
# Prevent dependency conflicts between sisyphus-control and aioambient
|
|
|
|
# until upper bounds for sisyphus-control have been updated
|
|
|
|
# https://github.com/jkeljo/sisyphus-control/issues/6
|
|
|
|
python-engineio>=3.13.1,<4.0
|
|
|
|
python-socketio>=4.6.0,<5.0
|
2022-01-23 22:22:16 +00:00
|
|
|
|
|
|
|
# Constrain multidict to avoid typing issues
|
2022-02-23 11:25:54 +00:00
|
|
|
# https://github.com/home-assistant/core/pull/67046
|
|
|
|
multidict>=6.0.2
|
2022-03-15 14:56:08 +00:00
|
|
|
|
|
|
|
# Required for compatibility with point integration - ensure_active_token
|
|
|
|
# https://github.com/home-assistant/core/pull/68176
|
|
|
|
authlib<1.0
|
2022-04-26 20:12:48 +00:00
|
|
|
|
2022-10-07 18:56:29 +00:00
|
|
|
# Version 2.0 added typing, prevent accidental fallbacks
|
|
|
|
backoff>=2.0
|
2022-06-01 06:04:35 +00:00
|
|
|
|
|
|
|
# Breaking change in version
|
|
|
|
# https://github.com/samuelcolvin/pydantic/issues/4092
|
|
|
|
pydantic!=1.9.1
|
2022-07-13 21:12:53 +00:00
|
|
|
|
|
|
|
# Breaks asyncio
|
|
|
|
# https://github.com/pubnub/python/issues/130
|
|
|
|
pubnub!=6.4.0
|
2022-07-14 17:38:22 +00:00
|
|
|
|
|
|
|
# Package's __init__.pyi stub has invalid syntax and breaks mypy
|
|
|
|
# https://github.com/dahlia/iso4217/issues/16
|
|
|
|
iso4217!=1.10.20220401
|
2022-09-01 19:00:50 +00:00
|
|
|
|
|
|
|
# Pandas 1.4.4 has issues with wheels om armhf + Py3.10
|
|
|
|
pandas==1.4.3
|
2022-10-20 16:30:00 +00:00
|
|
|
|
|
|
|
# uamqp 1.6.1, has 1 failing test during built on armv7/armhf
|
|
|
|
uamqp==1.6.0
|
2017-08-05 06:06:10 +00:00
|
|
|
"""
|
2017-03-22 15:50:54 +00:00
|
|
|
|
2020-04-05 16:45:43 +00:00
|
|
|
IGNORE_PRE_COMMIT_HOOK_ID = (
|
|
|
|
"check-executables-have-shebangs",
|
|
|
|
"check-json",
|
|
|
|
"no-commit-to-branch",
|
|
|
|
"prettier",
|
2021-03-24 19:05:53 +00:00
|
|
|
"python-typing-update",
|
2020-04-05 16:45:43 +00:00
|
|
|
)
|
2019-12-16 10:06:17 +00:00
|
|
|
|
2021-08-30 15:20:02 +00:00
|
|
|
PACKAGE_REGEX = re.compile(r"^(?:--.+\s)?([-_\.\w\d]+).*==.+$")
|
|
|
|
|
2017-03-22 15:50:54 +00:00
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def has_tests(module: str) -> bool:
|
2019-10-09 23:16:29 +00:00
|
|
|
"""Test if a module has tests.
|
|
|
|
|
|
|
|
Module format: homeassistant.components.hue
|
2022-01-07 15:45:27 +00:00
|
|
|
Test if exists: tests/components/hue/__init__.py
|
2019-10-09 23:16:29 +00:00
|
|
|
"""
|
2022-01-07 15:45:27 +00:00
|
|
|
path = (
|
|
|
|
Path(module.replace(".", "/").replace("homeassistant", "tests")) / "__init__.py"
|
|
|
|
)
|
|
|
|
return path.exists()
|
2019-10-09 23:16:29 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def explore_module(package: str, explore_children: bool) -> list[str]:
|
2016-03-09 10:15:04 +00:00
|
|
|
"""Explore the modules."""
|
2015-11-17 08:18:42 +00:00
|
|
|
module = importlib.import_module(package)
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
found: list[str] = []
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if not hasattr(module, "__path__"):
|
2015-11-17 08:18:42 +00:00
|
|
|
return found
|
|
|
|
|
2020-04-05 14:01:41 +00:00
|
|
|
for _, name, _ in pkgutil.iter_modules(module.__path__, f"{package}."):
|
2015-11-17 08:18:42 +00:00
|
|
|
found.append(name)
|
|
|
|
|
|
|
|
if explore_children:
|
|
|
|
found.extend(explore_module(name, False))
|
|
|
|
|
|
|
|
return found
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def core_requirements() -> list[str]:
|
2022-05-26 00:54:49 +00:00
|
|
|
"""Gather core requirements out of pyproject.toml."""
|
|
|
|
with open("pyproject.toml", "rb") as fp:
|
|
|
|
data = tomllib.load(fp)
|
2022-11-16 12:00:35 +00:00
|
|
|
dependencies: list[str] = data["project"]["dependencies"]
|
|
|
|
return dependencies
|
2015-11-17 08:18:42 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def gather_recursive_requirements(
|
|
|
|
domain: str, seen: set[str] | None = None
|
|
|
|
) -> set[str]:
|
2019-06-10 21:38:14 +00:00
|
|
|
"""Recursively gather requirements from a module."""
|
|
|
|
if seen is None:
|
|
|
|
seen = set()
|
|
|
|
|
|
|
|
seen.add(domain)
|
2019-10-09 23:16:29 +00:00
|
|
|
integration = Integration(Path(f"homeassistant/components/{domain}"))
|
2019-06-10 21:38:14 +00:00
|
|
|
integration.load_manifest()
|
2021-12-15 11:28:04 +00:00
|
|
|
reqs = {x for x in integration.requirements if x not in CONSTRAINT_BASE}
|
2020-04-03 19:58:19 +00:00
|
|
|
for dep_domain in integration.dependencies:
|
2019-06-10 21:38:14 +00:00
|
|
|
reqs.update(gather_recursive_requirements(dep_domain, seen))
|
|
|
|
return reqs
|
|
|
|
|
|
|
|
|
2021-08-30 15:20:02 +00:00
|
|
|
def normalize_package_name(requirement: str) -> str:
|
|
|
|
"""Return a normalized package name from a requirement string."""
|
|
|
|
# This function is also used in hassfest.
|
|
|
|
match = PACKAGE_REGEX.search(requirement)
|
|
|
|
if not match:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
# pipdeptree needs lowercase and dash instead of underscore as separator
|
|
|
|
package = match.group(1).lower().replace("_", "-")
|
|
|
|
|
|
|
|
return package
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def comment_requirement(req: str) -> bool:
|
2018-08-24 08:28:43 +00:00
|
|
|
"""Comment out requirement. Some don't install on all systems."""
|
2021-08-30 15:20:02 +00:00
|
|
|
return any(
|
|
|
|
normalize_package_name(req) == ign for ign in COMMENT_REQUIREMENTS_NORMALIZED
|
|
|
|
)
|
2015-11-17 08:28:22 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def gather_modules() -> dict[str, list[str]] | None:
|
2017-05-07 05:37:31 +00:00
|
|
|
"""Collect the information."""
|
2022-11-16 12:00:35 +00:00
|
|
|
reqs: dict[str, list[str]] = {}
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
errors: list[str] = []
|
2015-11-25 22:31:04 +00:00
|
|
|
|
2019-04-13 20:17:01 +00:00
|
|
|
gather_requirements_from_manifests(errors, reqs)
|
2019-04-05 04:29:29 +00:00
|
|
|
gather_requirements_from_modules(errors, reqs)
|
|
|
|
|
|
|
|
for key in reqs:
|
2019-07-31 19:25:30 +00:00
|
|
|
reqs[key] = sorted(reqs[key], key=lambda name: (len(name.split(".")), name))
|
2019-04-05 04:29:29 +00:00
|
|
|
|
|
|
|
if errors:
|
|
|
|
print("******* ERROR")
|
2019-07-31 19:25:30 +00:00
|
|
|
print("Errors while importing: ", ", ".join(errors))
|
2019-04-05 04:29:29 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
return reqs
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def gather_requirements_from_manifests(
|
|
|
|
errors: list[str], reqs: dict[str, list[str]]
|
|
|
|
) -> None:
|
2019-04-13 20:17:01 +00:00
|
|
|
"""Gather all of the requirements from manifests."""
|
2019-10-09 23:16:29 +00:00
|
|
|
integrations = Integration.load_dir(Path("homeassistant/components"))
|
2019-04-13 20:17:01 +00:00
|
|
|
for domain in sorted(integrations):
|
|
|
|
integration = integrations[domain]
|
|
|
|
|
2020-08-26 08:20:14 +00:00
|
|
|
if integration.disabled:
|
|
|
|
continue
|
|
|
|
|
2019-04-13 20:17:01 +00:00
|
|
|
process_requirements(
|
2020-04-05 14:01:41 +00:00
|
|
|
errors, integration.requirements, f"homeassistant.components.{domain}", reqs
|
2019-04-13 20:17:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def gather_requirements_from_modules(
|
|
|
|
errors: list[str], reqs: dict[str, list[str]]
|
|
|
|
) -> None:
|
2019-04-05 04:29:29 +00:00
|
|
|
"""Collect the requirements from the modules directly."""
|
2018-08-22 07:52:34 +00:00
|
|
|
for package in sorted(
|
2019-07-31 19:25:30 +00:00
|
|
|
explore_module("homeassistant.scripts", True)
|
|
|
|
+ explore_module("homeassistant.auth", True)
|
|
|
|
):
|
2015-11-17 08:18:42 +00:00
|
|
|
try:
|
|
|
|
module = importlib.import_module(package)
|
2019-03-17 03:44:05 +00:00
|
|
|
except ImportError as err:
|
2020-04-05 15:48:55 +00:00
|
|
|
print(f"{package.replace('.', '/')}.py: {err}")
|
2019-06-10 21:38:14 +00:00
|
|
|
errors.append(package)
|
2015-11-17 08:18:42 +00:00
|
|
|
continue
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if getattr(module, "REQUIREMENTS", None):
|
2019-04-05 04:29:29 +00:00
|
|
|
process_requirements(errors, module.REQUIREMENTS, package, reqs)
|
2016-02-01 07:52:42 +00:00
|
|
|
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def process_requirements(
|
|
|
|
errors: list[str],
|
|
|
|
module_requirements: list[str],
|
|
|
|
package: str,
|
|
|
|
reqs: dict[str, list[str]],
|
|
|
|
) -> None:
|
2019-04-05 04:29:29 +00:00
|
|
|
"""Process all of the requirements."""
|
|
|
|
for req in module_requirements:
|
2019-07-31 19:25:30 +00:00
|
|
|
if "://" in req:
|
2019-08-23 16:53:33 +00:00
|
|
|
errors.append(f"{package}[Only pypi dependencies are allowed: {req}]")
|
2019-07-31 19:25:30 +00:00
|
|
|
if req.partition("==")[1] == "" and req not in IGNORE_PIN:
|
2019-08-23 16:53:33 +00:00
|
|
|
errors.append(f"{package}[Please pin requirement {req}, see {URL_PIN}]")
|
2019-04-05 04:29:29 +00:00
|
|
|
reqs.setdefault(req, []).append(package)
|
2017-05-07 05:37:31 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def generate_requirements_list(reqs: dict[str, list[str]]) -> str:
|
2017-05-07 05:37:31 +00:00
|
|
|
"""Generate a pip file based on requirements."""
|
|
|
|
output = []
|
2016-02-01 07:52:42 +00:00
|
|
|
for pkg, requirements in sorted(reqs.items(), key=lambda item: item[0]):
|
2019-04-05 04:29:29 +00:00
|
|
|
for req in sorted(requirements):
|
2019-08-23 16:53:33 +00:00
|
|
|
output.append(f"\n# {req}")
|
2015-11-17 08:28:22 +00:00
|
|
|
|
|
|
|
if comment_requirement(pkg):
|
2019-08-23 16:53:33 +00:00
|
|
|
output.append(f"\n# {pkg}\n")
|
2015-11-17 08:28:22 +00:00
|
|
|
else:
|
2019-08-23 16:53:33 +00:00
|
|
|
output.append(f"\n{pkg}\n")
|
2019-07-31 19:25:30 +00:00
|
|
|
return "".join(output)
|
2017-05-07 05:37:31 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def requirements_output() -> str:
|
2020-07-03 17:18:01 +00:00
|
|
|
"""Generate output for requirements."""
|
2020-07-11 13:20:14 +00:00
|
|
|
output = [
|
|
|
|
"-c homeassistant/package_constraints.txt\n",
|
|
|
|
"\n",
|
|
|
|
"# Home Assistant Core\n",
|
|
|
|
]
|
2019-07-31 19:25:30 +00:00
|
|
|
output.append("\n".join(core_requirements()))
|
|
|
|
output.append("\n")
|
2020-07-03 17:18:01 +00:00
|
|
|
|
|
|
|
return "".join(output)
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def requirements_all_output(reqs: dict[str, list[str]]) -> str:
|
2020-07-03 17:18:01 +00:00
|
|
|
"""Generate output for requirements_all."""
|
2020-07-11 13:20:14 +00:00
|
|
|
output = [
|
2020-08-27 14:56:53 +00:00
|
|
|
"# Home Assistant Core, full dependency set\n",
|
2020-07-11 13:20:14 +00:00
|
|
|
"-r requirements.txt\n",
|
|
|
|
]
|
2017-05-07 05:37:31 +00:00
|
|
|
output.append(generate_requirements_list(reqs))
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
return "".join(output)
|
2017-05-07 05:37:31 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def requirements_test_all_output(reqs: dict[str, list[str]]) -> str:
|
2017-05-07 05:37:31 +00:00
|
|
|
"""Generate output for test_requirements."""
|
2020-07-11 13:20:14 +00:00
|
|
|
output = [
|
|
|
|
"# Home Assistant tests, full dependency set\n",
|
|
|
|
f"# Automatically generated by {Path(__file__).name}, do not edit\n",
|
|
|
|
"\n",
|
2020-08-27 14:56:53 +00:00
|
|
|
"-r requirements_test.txt\n",
|
2020-07-11 13:20:14 +00:00
|
|
|
]
|
2019-10-09 23:16:29 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
filtered = {
|
2019-10-09 23:16:29 +00:00
|
|
|
requirement: modules
|
|
|
|
for requirement, modules in reqs.items()
|
2019-07-31 19:25:30 +00:00
|
|
|
if any(
|
2019-10-09 23:16:29 +00:00
|
|
|
# Always install requirements that are not part of integrations
|
|
|
|
not mdl.startswith("homeassistant.components.") or
|
|
|
|
# Install tests for integrations that have tests
|
|
|
|
has_tests(mdl)
|
|
|
|
for mdl in modules
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
}
|
2017-05-07 05:37:31 +00:00
|
|
|
output.append(generate_requirements_list(filtered))
|
2015-11-25 22:31:04 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
return "".join(output)
|
2015-11-25 22:31:04 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def requirements_pre_commit_output() -> str:
|
2019-11-05 05:21:52 +00:00
|
|
|
"""Generate output for pre-commit dependencies."""
|
2020-02-01 15:12:46 +00:00
|
|
|
source = ".pre-commit-config.yaml"
|
2022-11-16 12:00:35 +00:00
|
|
|
pre_commit_conf: dict[str, list[dict[str, Any]]]
|
|
|
|
pre_commit_conf = load_yaml(source) # type: ignore[assignment]
|
|
|
|
reqs: list[str] = []
|
|
|
|
hook: dict[str, Any]
|
2019-11-05 05:21:52 +00:00
|
|
|
for repo in (x for x in pre_commit_conf["repos"] if x.get("rev")):
|
2022-11-16 12:00:35 +00:00
|
|
|
rev: str = repo["rev"]
|
2019-11-05 05:21:52 +00:00
|
|
|
for hook in repo["hooks"]:
|
2019-12-16 10:06:17 +00:00
|
|
|
if hook["id"] not in IGNORE_PRE_COMMIT_HOOK_ID:
|
2022-11-16 12:00:35 +00:00
|
|
|
reqs.append(f"{hook['id']}=={rev.lstrip('v')}")
|
2019-12-16 10:06:17 +00:00
|
|
|
reqs.extend(x for x in hook.get("additional_dependencies", ()))
|
2019-11-05 05:21:52 +00:00
|
|
|
output = [
|
|
|
|
f"# Automatically generated "
|
|
|
|
f"from {source} by {Path(__file__).name}, do not edit",
|
|
|
|
"",
|
|
|
|
]
|
|
|
|
output.extend(sorted(reqs))
|
|
|
|
return "\n".join(output) + "\n"
|
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def gather_constraints() -> str:
|
2017-03-22 15:50:54 +00:00
|
|
|
"""Construct output for constraint file."""
|
2019-10-09 23:16:29 +00:00
|
|
|
return (
|
|
|
|
"\n".join(
|
|
|
|
sorted(
|
2020-07-22 02:19:32 +00:00
|
|
|
{
|
|
|
|
*core_requirements(),
|
|
|
|
*gather_recursive_requirements("default_config"),
|
|
|
|
*gather_recursive_requirements("mqtt"),
|
|
|
|
}
|
2019-10-09 23:16:29 +00:00
|
|
|
)
|
|
|
|
+ [""]
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-10-09 23:16:29 +00:00
|
|
|
+ CONSTRAINT_BASE
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2017-03-22 15:50:54 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def diff_file(filename: str, content: str) -> list[str]:
|
2019-10-09 23:16:29 +00:00
|
|
|
"""Diff a file."""
|
|
|
|
return list(
|
|
|
|
difflib.context_diff(
|
2020-01-03 13:47:06 +00:00
|
|
|
[f"{line}\n" for line in Path(filename).read_text().split("\n")],
|
|
|
|
[f"{line}\n" for line in content.split("\n")],
|
2019-10-09 23:16:29 +00:00
|
|
|
filename,
|
|
|
|
"generated",
|
|
|
|
)
|
|
|
|
)
|
2017-03-22 15:50:54 +00:00
|
|
|
|
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
def main(validate: bool) -> int:
|
2018-08-24 08:28:43 +00:00
|
|
|
"""Run the script."""
|
2019-07-31 19:25:30 +00:00
|
|
|
if not os.path.isfile("requirements_all.txt"):
|
|
|
|
print("Run this from HA root dir")
|
2018-03-09 20:27:39 +00:00
|
|
|
return 1
|
2015-11-29 21:55:46 +00:00
|
|
|
|
2015-11-25 22:31:04 +00:00
|
|
|
data = gather_modules()
|
|
|
|
|
2015-11-29 21:55:46 +00:00
|
|
|
if data is None:
|
2018-03-09 20:27:39 +00:00
|
|
|
return 1
|
2015-12-18 07:51:34 +00:00
|
|
|
|
2022-11-16 12:00:35 +00:00
|
|
|
reqs_file = requirements_output()
|
2020-07-03 17:18:01 +00:00
|
|
|
reqs_all_file = requirements_all_output(data)
|
2020-08-27 14:56:53 +00:00
|
|
|
reqs_test_all_file = requirements_test_all_output(data)
|
2019-11-05 05:21:52 +00:00
|
|
|
reqs_pre_commit_file = requirements_pre_commit_output()
|
2019-10-09 23:16:29 +00:00
|
|
|
constraints = gather_constraints()
|
|
|
|
|
|
|
|
files = (
|
2020-07-03 17:18:01 +00:00
|
|
|
("requirements.txt", reqs_file),
|
|
|
|
("requirements_all.txt", reqs_all_file),
|
2019-11-05 05:21:52 +00:00
|
|
|
("requirements_test_pre_commit.txt", reqs_pre_commit_file),
|
2020-08-27 14:56:53 +00:00
|
|
|
("requirements_test_all.txt", reqs_test_all_file),
|
2019-10-09 23:16:29 +00:00
|
|
|
("homeassistant/package_constraints.txt", constraints),
|
|
|
|
)
|
2017-05-07 05:37:31 +00:00
|
|
|
|
2018-03-09 20:27:39 +00:00
|
|
|
if validate:
|
2017-05-07 05:37:31 +00:00
|
|
|
errors = []
|
2017-03-22 15:50:54 +00:00
|
|
|
|
2019-10-09 23:16:29 +00:00
|
|
|
for filename, content in files:
|
|
|
|
diff = diff_file(filename, content)
|
|
|
|
if diff:
|
|
|
|
errors.append("".join(diff))
|
2017-05-07 05:37:31 +00:00
|
|
|
|
|
|
|
if errors:
|
2019-10-09 23:16:29 +00:00
|
|
|
print("ERROR - FOUND THE FOLLOWING DIFFERENCES")
|
|
|
|
print()
|
|
|
|
print()
|
|
|
|
print("\n\n".join(errors))
|
|
|
|
print()
|
|
|
|
print("Please run python3 -m script.gen_requirements_all")
|
2018-03-09 20:27:39 +00:00
|
|
|
return 1
|
2017-03-22 15:50:54 +00:00
|
|
|
|
2018-03-09 20:27:39 +00:00
|
|
|
return 0
|
2017-03-22 15:50:54 +00:00
|
|
|
|
2019-10-09 23:16:29 +00:00
|
|
|
for filename, content in files:
|
|
|
|
Path(filename).write_text(content)
|
|
|
|
|
2018-03-09 20:27:39 +00:00
|
|
|
return 0
|
2015-11-17 08:18:42 +00:00
|
|
|
|
2016-11-19 05:47:59 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
_VAL = sys.argv[-1] == "validate"
|
2018-03-09 20:27:39 +00:00
|
|
|
sys.exit(main(_VAL))
|