1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 08:26:41 +01:00

Warn about *.pth files in dependencies (#166411)

This commit is contained in:
Marc Mueller
2026-03-25 07:18:47 +01:00
committed by GitHub
parent b6508c2ca4
commit 193f519366
4 changed files with 89 additions and 7 deletions

View File

@@ -235,3 +235,12 @@ auth0-python<5.0
# Setuptools >=82.0.0 doesn't contain pkg_resources anymore
setuptools<82.0.0
# Pin dependencies with '.pth' files to exact versions, only update manually!
# https://github.com/Azure/azure-kusto-python/ -> '.pth' files removed with >=5.0.5
# https://github.com/xolox/python-coloredlogs -> unmaintained
# https://github.com/pypa/setuptools
azure-kusto-data==4.5.1
azure-kusto-ingest==4.5.1
coloredlogs==15.0.1
setuptools==81.0.0

View File

@@ -225,6 +225,15 @@ auth0-python<5.0
# Setuptools >=82.0.0 doesn't contain pkg_resources anymore
setuptools<82.0.0
# Pin dependencies with '.pth' files to exact versions, only update manually!
# https://github.com/Azure/azure-kusto-python/ -> '.pth' files removed with >=5.0.5
# https://github.com/xolox/python-coloredlogs -> unmaintained
# https://github.com/pypa/setuptools
azure-kusto-data==4.5.1
azure-kusto-ingest==4.5.1
coloredlogs==15.0.1
setuptools==81.0.0
"""
GENERATED_MESSAGE = (

View File

@@ -95,6 +95,8 @@ FORBIDDEN_PACKAGES = {
"async-timeout": "be replaced by asyncio.timeout (Python 3.11+)",
# Only needed for tests
"codecov": "not be a runtime dependency",
# Coloredlogs is unmaintained and contains a '.pth' file
"coloredlogs": "be replaced with colorlog",
# Only needed for docs
"mkdocs": "not be a runtime dependency",
# Does blocking I/O and should be replaced by pyserial-asyncio-fast
@@ -149,11 +151,13 @@ FORBIDDEN_PACKAGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
},
"flux_led": {"flux-led": {"async-timeout"}},
"foobot": {"foobot-async": {"async-timeout"}},
"google_maps": {"locationsharinglib": {"coloredlogs"}},
"harmony": {"aioharmony": {"async-timeout"}},
"here_travel_time": {
"here-routing": {"async-timeout"},
"here-transit": {"async-timeout"},
},
"homeassistant_hardware": {"universal-silabs-flasher": {"coloredlogs"}},
"homewizard": {"python-homewizard-energy": {"async-timeout"}},
"imeon_inverter": {"imeon-inverter-api": {"async-timeout"}},
"izone": {"python-izone": {"async-timeout"}},
@@ -217,6 +221,7 @@ FORBIDDEN_PACKAGE_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
# https://github.com/waveform80/colorzero/issues/9
# zha > zigpy-zigate > gpiozero > colorzero > setuptools
"colorzero": {"setuptools"},
"zigpy-znp": {"coloredlogs"},
},
}
@@ -236,16 +241,42 @@ FORBIDDEN_PACKAGE_FILES_EXCEPTIONS = {
# - reasonX should be the name of the invalid dependency
# https://github.com/jaraco/jaraco.net
"abode": {"jaraco-abode": {"jaraco-net"}},
# https://github.com/Azure/azure-kusto-python/
"azure_data_explorer": {
# Legacy namespace packages, resolved with >=5.0.5
# azure_kusto_data-*-nspkg.pth
# azure_kusto_ingest-*-nspkg.pth
"homeassistant": {"azure-kusto-data", "azure-kusto-ingest"},
"azure-kusto-ingest": {"azure-kusto-data"},
},
# https://github.com/coinbase/coinbase-advanced-py
"cmus": {
# Setuptools - distutils-precedence.pth
"pbr": {"setuptools"}
},
"coinbase": {"homeassistant": {"coinbase-advanced-py"}},
# https://github.com/u9n/dlms-cosem
"dsmr": {"dsmr-parser": {"dlms-cosem"}},
# https://github.com/ChrisMandich/PyFlume # Fixed with >=0.7.1
"fitbit": {
# Setuptools - distutils-precedence.pth
"fitbit": {"setuptools"}
},
"flume": {"homeassistant": {"pyflume"}},
# https://github.com/fortinet-solutions-cse/fortiosapi
"fortios": {"homeassistant": {"fortiosapi"}},
# https://github.com/manzanotti/geniushub-client
"geniushub": {"homeassistant": {"geniushub-client"}},
# https://github.com/costastf/locationsharinglib
"google_maps": {
# Coloredlogs, unmaintained - coloredlogs.pth
"locationsharinglib": {"coloredlogs"},
},
# https://github.com/NabuCasa/universal-silabs-flasher
"homeassistant_hardware": {
# Coloredlogs, unmaintained - coloredlogs.pth
"universal-silabs-flasher": {"coloredlogs"},
},
# https://github.com/basnijholt/aiokef
"kef": {"homeassistant": {"aiokef"}},
# https://github.com/danifus/pyzipper
@@ -257,11 +288,26 @@ FORBIDDEN_PACKAGE_FILES_EXCEPTIONS = {
# https://github.com/timmo001/aiolyric
"lyric": {"homeassistant": {"aiolyric"}},
# https://github.com/microBeesTech/pythonSDK/
"microbees": {"homeassistant": {"microbeespy"}},
"microbees": {
"homeassistant": {"microbeespy"},
"microbeespy": {"setuptools"},
},
"mochad": {
# Setuptools - distutils-precedence.pth
"pbr": {"setuptools"}
},
# https://github.com/ejpenney/pyobihai
"obihai": {"homeassistant": {"pyobihai"}},
"opnsense": {
# Setuptools - distutils-precedence.pth
"pbr": {"setuptools"}
},
# https://github.com/iamkubi/pydactyl
"pterodactyl": {"homeassistant": {"py-dactyl"}},
"remote_rpi_gpio": {
# Setuptools - distutils-precedence.pth
"colorzero": {"setuptools"}
},
# https://github.com/sstallion/sensorpush-api
"sensorpush_cloud": {
"homeassistant": {"sensorpush-api"},
@@ -273,6 +319,14 @@ FORBIDDEN_PACKAGE_FILES_EXCEPTIONS = {
"watergate": {"homeassistant": {"watergate-local-api"}},
# https://github.com/markusressel/xs1-api-client
"xs1": {"homeassistant": {"xs1-api-client"}},
# https://github.com/zigpy/zigpy-znp
"zha": {
# Setuptools - distutils-precedence.pth
"colorzero": {"setuptools"},
# Coloredlogs, unmaintained - coloredlogs.pth
# https://github.com/xolox/python-coloredlogs/blob/15.0.1/coloredlogs.pth
"zigpy-znp": {"coloredlogs"},
},
}
PYTHON_VERSION_CHECK_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
@@ -670,8 +724,10 @@ def check_dependency_files(
for file in files(pkg) or ():
if not (top := file.parts[0].lower()).endswith((".dist-info", ".py")):
top_level.add(top)
if (name := str(file)).lower() in FORBIDDEN_FILE_NAMES:
file_names.add(name)
if (name := str(file).lower()) in FORBIDDEN_FILE_NAMES or (
name.endswith(".pth") and len(file.parts) == 1
):
file_names.add(str(file))
results = _PackageFilesCheckResult(
top_level=FORBIDDEN_PACKAGE_NAMES & top_level,
file_names=file_names,
@@ -687,7 +743,8 @@ def check_dependency_files(
f"Package {pkg} has a forbidden top level directory '{dir_name}' in {package}",
)
for file_name in results["file_names"]:
integration.add_error(
integration.add_warning_or_error(
pkg in package_exceptions,
"requirements",
f"Package {pkg} has a forbidden file '{file_name}' in {package}",
)

View File

@@ -275,6 +275,7 @@ def test_check_dependency_file_names(integration: Integration) -> None:
pkg_files = [
PackagePath("py.typed"),
PackagePath("my_package.py"),
PackagePath("some_script.Pth"),
PackagePath("my_package-1.0.0.dist-info/METADATA"),
]
with (
@@ -285,17 +286,23 @@ def test_check_dependency_file_names(integration: Integration) -> None:
):
assert not _packages_checked_files_cache
assert check_dependency_files(integration, package, pkg, ()) is False
assert _packages_checked_files_cache[pkg]["file_names"] == {"py.typed"}
assert len(integration.errors) == 1
assert _packages_checked_files_cache[pkg]["file_names"] == {
"py.typed",
"some_script.Pth",
}
assert len(integration.errors) == 2
assert f"Package {pkg} has a forbidden file 'py.typed' in {package}" in [
x.error for x in integration.errors
]
assert f"Package {pkg} has a forbidden file 'some_script.Pth' in {package}" in [
x.error for x in integration.errors
]
integration.errors.clear()
# Repeated call should use cache
assert check_dependency_files(integration, package, pkg, ()) is False
assert mock_files.call_count == 1
assert len(integration.errors) == 1
assert len(integration.errors) == 2
integration.errors.clear()
# All good