mirror of
https://github.com/m5stack/esphome.git
synced 2026-05-20 11:52:52 -07:00
Drop Python 3.10 support, require Python 3.11+ (#9522)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
parent
30c4b91697
commit
f745135bdc
@@ -47,7 +47,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.6.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.10"
|
||||
PYUPGRADE_TARGET: "--py310-plus"
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
PYUPGRADE_TARGET: "--py311-plus"
|
||||
|
||||
concurrency:
|
||||
# yamllint disable-line rule:line-length
|
||||
@@ -112,7 +112,6 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
- "3.13"
|
||||
@@ -128,14 +127,10 @@ jobs:
|
||||
os: windows-latest
|
||||
- python-version: "3.12"
|
||||
os: windows-latest
|
||||
- python-version: "3.10"
|
||||
os: windows-latest
|
||||
- python-version: "3.13"
|
||||
os: macOS-latest
|
||||
- python-version: "3.12"
|
||||
os: macOS-latest
|
||||
- python-version: "3.10"
|
||||
os: macOS-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs:
|
||||
- common
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.6.0
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
|
||||
@@ -40,7 +40,7 @@ repos:
|
||||
rev: v3.20.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py310-plus]
|
||||
args: [--py311-plus]
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.37.1
|
||||
hooks:
|
||||
|
||||
@@ -76,6 +76,7 @@ async def theme_to_code(config):
|
||||
for w_name, style in theme.items():
|
||||
# Work around Python 3.10 bug with nested async comprehensions
|
||||
# With Python 3.11 this could be simplified
|
||||
# TODO: Now that we require Python 3.11+, this can be updated to use nested comprehensions
|
||||
styles = {}
|
||||
for part, states in collect_parts(style).items():
|
||||
styles[part] = {
|
||||
|
||||
@@ -3,15 +3,9 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
from ipaddress import ip_address
|
||||
import sys
|
||||
|
||||
from icmplib import NameLookupError, async_resolve
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from asyncio import timeout as async_timeout
|
||||
else:
|
||||
from async_timeout import timeout as async_timeout
|
||||
|
||||
RESOLVE_TIMEOUT = 3.0
|
||||
|
||||
|
||||
@@ -20,9 +14,9 @@ async def _async_resolve_wrapper(hostname: str) -> list[str] | Exception:
|
||||
with suppress(ValueError):
|
||||
return [str(ip_address(hostname))]
|
||||
try:
|
||||
async with async_timeout(RESOLVE_TIMEOUT):
|
||||
async with asyncio.timeout(RESOLVE_TIMEOUT):
|
||||
return await async_resolve(hostname)
|
||||
except (asyncio.TimeoutError, NameLookupError, UnicodeError) as ex:
|
||||
except (TimeoutError, NameLookupError, UnicodeError) as ex:
|
||||
return ex
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Home Automation",
|
||||
]
|
||||
requires-python = ">=3.10.0"
|
||||
requires-python = ">=3.11.0"
|
||||
|
||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||
|
||||
@@ -62,7 +62,7 @@ addopts = [
|
||||
]
|
||||
|
||||
[tool.pylint.MAIN]
|
||||
py-version = "3.10"
|
||||
py-version = "3.11"
|
||||
ignore = [
|
||||
"api_pb2.py",
|
||||
]
|
||||
@@ -106,7 +106,7 @@ expected-line-ending-format = "LF"
|
||||
|
||||
[tool.ruff]
|
||||
required-version = ">=0.5.0"
|
||||
target-version = "py310"
|
||||
target-version = "py311"
|
||||
exclude = ['generated']
|
||||
|
||||
[tool.ruff.lint]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
async_timeout==5.0.1; python_version <= "3.10"
|
||||
cryptography==45.0.1
|
||||
voluptuous==0.15.2
|
||||
PyYAML==6.0.2
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ def main():
|
||||
print()
|
||||
print("Running pyupgrade...")
|
||||
print()
|
||||
PYUPGRADE_TARGET = "--py310-plus"
|
||||
PYUPGRADE_TARGET = "--py311-plus"
|
||||
for files in filesets:
|
||||
cmd = ["pyupgrade", PYUPGRADE_TARGET] + files
|
||||
log = get_err(*cmd)
|
||||
|
||||
@@ -395,7 +395,7 @@ async def wait_and_connect_api_client(
|
||||
# Wait for connection with timeout
|
||||
try:
|
||||
await asyncio.wait_for(connected_future, timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
raise TimeoutError(f"Failed to connect to API after {timeout} seconds")
|
||||
|
||||
yield client
|
||||
@@ -575,12 +575,12 @@ async def run_binary_and_wait_for_port(
|
||||
process.send_signal(signal.SIGINT)
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=SIGINT_TIMEOUT)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
# If SIGINT didn't work, try SIGTERM
|
||||
process.terminate()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=SIGTERM_TIMEOUT)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
# Last resort: SIGKILL
|
||||
process.kill()
|
||||
await process.wait()
|
||||
|
||||
@@ -177,7 +177,7 @@ async def test_api_message_size_batching(
|
||||
# Wait for states with timeout
|
||||
try:
|
||||
await asyncio.wait_for(states_future, timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
missing_keys = expected_keys - received_keys
|
||||
pytest.fail(
|
||||
f"Did not receive states from all entities within 5 seconds. "
|
||||
|
||||
@@ -29,7 +29,7 @@ async def test_api_reboot_timeout(
|
||||
# (0.5s reboot timeout + some margin for processing)
|
||||
try:
|
||||
await asyncio.wait_for(reboot_future, timeout=2.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail("Device did not reboot within expected timeout")
|
||||
|
||||
# Test passes if we get here - reboot was detected
|
||||
|
||||
@@ -98,7 +98,7 @@ async def test_areas_and_devices(
|
||||
# Wait for sensor states
|
||||
try:
|
||||
await asyncio.wait_for(states_future, timeout=10.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive all sensor states within 10 seconds. "
|
||||
f"Received {len(states)} states"
|
||||
|
||||
@@ -77,7 +77,7 @@ async def test_device_id_in_state(
|
||||
# Wait for states
|
||||
try:
|
||||
await asyncio.wait_for(states_future, timeout=10.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive all entity states within 10 seconds. "
|
||||
f"Received {len(states)} states, expected {len(entity_device_mapping)}"
|
||||
|
||||
@@ -206,7 +206,7 @@ async def test_duplicate_entities_not_allowed_on_different_devices(
|
||||
# Wait for all entity states
|
||||
try:
|
||||
await asyncio.wait_for(states_future, timeout=10.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive all entity states within 10 seconds. "
|
||||
f"Expected {expected_count}, received {state_count}"
|
||||
|
||||
@@ -82,7 +82,7 @@ async def test_entity_icon(
|
||||
# Wait for states
|
||||
try:
|
||||
await asyncio.wait_for(state_received.wait(), timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail("No states received within 5 seconds")
|
||||
|
||||
# Verify we received states
|
||||
|
||||
@@ -44,7 +44,7 @@ async def test_host_mode_batch_delay(
|
||||
# Wait for states from all entities with timeout
|
||||
try:
|
||||
entity_count = await asyncio.wait_for(entity_count_future, timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive states from at least 7 entities within 5 seconds. "
|
||||
f"Received {len(states)} states"
|
||||
|
||||
@@ -99,7 +99,7 @@ async def test_host_mode_empty_string_options(
|
||||
# Wait for initial states with timeout
|
||||
try:
|
||||
await asyncio.wait_for(states_received_future, timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive states for all select entities. "
|
||||
f"Expected keys: {expected_select_keys}, Received: {received_select_keys}"
|
||||
|
||||
@@ -86,7 +86,7 @@ async def test_host_mode_entity_fields(
|
||||
# Wait for at least one state
|
||||
try:
|
||||
await asyncio.wait_for(state_received.wait(), timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
pytest.fail("No states received within 5 seconds")
|
||||
|
||||
# Verify we received states (which means has_state flag is working)
|
||||
|
||||
@@ -41,7 +41,7 @@ async def test_host_mode_many_entities(
|
||||
# Wait for states from at least 50 sensors with timeout
|
||||
try:
|
||||
sensor_count = await asyncio.wait_for(sensor_count_future, timeout=10.0)
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
sensor_states = [
|
||||
s
|
||||
for s in states.values()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user