Add ability to set exceptions in dependency version checks (#145442)

* Add ability to set exceptions in dependency version checks

* Fix message

* Improve

* Auto-load from requirements.txt

* Revert "Auto-load from requirements.txt"

This reverts commit f893d4611a4b6ebedccaa639622c3f8f4ea64005.
This commit is contained in:
epenet 2025-05-26 19:45:26 +02:00 committed by GitHub
parent cfa4d37909
commit c3dec7fb2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -24,9 +24,9 @@ from .model import Config, Integration
PACKAGE_CHECK_VERSION_RANGE = { PACKAGE_CHECK_VERSION_RANGE = {
"aiohttp": "SemVer", "aiohttp": "SemVer",
# https://github.com/iMicknl/python-overkiz-api/issues/1644 "attrs": "CalVer",
# "attrs": "CalVer"
"grpcio": "SemVer", "grpcio": "SemVer",
"httpx": "SemVer",
"mashumaro": "SemVer", "mashumaro": "SemVer",
"pydantic": "SemVer", "pydantic": "SemVer",
"pyjwt": "SemVer", "pyjwt": "SemVer",
@ -34,6 +34,20 @@ PACKAGE_CHECK_VERSION_RANGE = {
"typing_extensions": "SemVer", "typing_extensions": "SemVer",
"yarl": "SemVer", "yarl": "SemVer",
} }
PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
# In the form dict("domain": {"package": {"dependency1", "dependency2"}})
# - domain is the integration domain
# - package is the package (can be transitive) referencing the dependency
# - dependencyX should be the name of the referenced dependency
"ollama": {
# https://github.com/ollama/ollama-python/pull/445 (not yet released)
"ollama": {"httpx"}
},
"overkiz": {
# https://github.com/iMicknl/python-overkiz-api/issues/1644 (not yet released)
"pyoverkiz": {"attrs"},
},
}
PACKAGE_REGEX = re.compile( PACKAGE_REGEX = re.compile(
r"^(?:--.+\s)?([-_,\.\w\d\[\]]+)(==|>=|<=|~=|!=|<|>|===)*(.*)$" r"^(?:--.+\s)?([-_,\.\w\d\[\]]+)(==|>=|<=|~=|!=|<|>|===)*(.*)$"
@ -399,6 +413,11 @@ def get_requirements(integration: Integration, packages: set[str]) -> set[str]:
) )
needs_forbidden_package_exceptions = False needs_forbidden_package_exceptions = False
package_version_check_exceptions = PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS.get(
integration.domain, {}
)
needs_package_version_check_exception = False
while to_check: while to_check:
package = to_check.popleft() package = to_check.popleft()
@ -433,7 +452,14 @@ def get_requirements(integration: Integration, packages: set[str]) -> set[str]:
"requirements", "requirements",
f"Package {pkg} should {reason} in {package}", f"Package {pkg} should {reason} in {package}",
) )
check_dependency_version_range(integration, package, pkg, version) if not check_dependency_version_range(
integration,
package,
pkg,
version,
package_version_check_exceptions.get(package, set()),
):
needs_package_version_check_exception = True
to_check.extend(dependencies) to_check.extend(dependencies)
@ -443,27 +469,48 @@ def get_requirements(integration: Integration, packages: set[str]) -> set[str]:
f"Integration {integration.domain} runtime dependency exceptions " f"Integration {integration.domain} runtime dependency exceptions "
"have been resolved, please remove from `FORBIDDEN_PACKAGE_EXCEPTIONS`", "have been resolved, please remove from `FORBIDDEN_PACKAGE_EXCEPTIONS`",
) )
if package_version_check_exceptions and not needs_package_version_check_exception:
integration.add_error(
"requirements",
f"Integration {integration.domain} version restrictions checks have been "
"resolved, please remove from `PACKAGE_CHECK_VERSION_RANGE_EXCEPTIONS`",
)
return all_requirements return all_requirements
def check_dependency_version_range( def check_dependency_version_range(
integration: Integration, source: str, pkg: str, version: str integration: Integration,
) -> None: source: str,
pkg: str,
version: str,
package_exceptions: set[str],
) -> bool:
"""Check requirement version range. """Check requirement version range.
We want to avoid upper version bounds that are too strict for common packages. We want to avoid upper version bounds that are too strict for common packages.
""" """
if version == "Any" or (convention := PACKAGE_CHECK_VERSION_RANGE.get(pkg)) is None: if (
return version == "Any"
or (convention := PACKAGE_CHECK_VERSION_RANGE.get(pkg)) is None
if not all( or all(
_is_dependency_version_range_valid(version_part, convention) _is_dependency_version_range_valid(version_part, convention)
for version_part in version.split(";", 1)[0].split(",") for version_part in version.split(";", 1)[0].split(",")
)
): ):
return True
if pkg in package_exceptions:
integration.add_warning(
"requirements",
f"Version restrictions for {pkg} are too strict ({version}) in {source}",
)
else:
integration.add_error( integration.add_error(
"requirements", "requirements",
f"Version restrictions for {pkg} are too strict ({version}) in {source}", f"Version restrictions for {pkg} are too strict ({version}) in {source}",
) )
return False
def _is_dependency_version_range_valid(version_part: str, convention: str) -> bool: def _is_dependency_version_range_valid(version_part: str, convention: str) -> bool: