Compare commits

...

5 Commits

Author SHA1 Message Date
Robert Resch
1780477655 Ruff 2026-01-22 12:24:12 +00:00
Marc Mueller
34fd679f2c Add type ignores 2026-01-22 12:20:19 +00:00
Marc Mueller
4dbe42213c Fix IPAddress typing issues 2026-01-22 12:19:15 +00:00
Robert Resch
4fb0d99964 Merge branch 'dev' into edenhaus-3.14-default 2026-01-22 12:13:45 +00:00
Robert Resch
cd01e623ff Use Python 3.14 as default one 2026-01-22 12:02:03 +00:00
24 changed files with 42 additions and 48 deletions

View File

@@ -10,7 +10,7 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.14.2"
PIP_TIMEOUT: 60
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"

View File

@@ -41,8 +41,8 @@ env:
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.2"
DEFAULT_PYTHON: "3.13.11"
ALL_PYTHON_VERSIONS: "['3.13.11', '3.14.2']"
DEFAULT_PYTHON: "3.14.2"
ALL_PYTHON_VERSIONS: "['3.14.2']"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support

View File

@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.14.2"
jobs:
upload:

View File

@@ -17,7 +17,7 @@ on:
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.14.2"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name}}

View File

@@ -1 +1 @@
3.13
3.14

View File

@@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Callable
from concurrent.futures.thread import _threads_queues, _worker
import sys
import threading
from typing import Any
import weakref
@@ -54,17 +53,10 @@ class DBInterruptibleThreadPoolExecutor(InterruptibleThreadPoolExecutor):
) -> None:
q.put(None)
if sys.version_info >= (3, 14):
additional_args = (
self._create_worker_context(),
self._work_queue,
)
else:
additional_args = (
self._work_queue,
self._initializer,
self._initargs,
)
additional_args = (
self._create_worker_context(),
self._work_queue,
)
num_threads = len(self._threads)
if num_threads < self._max_workers:

View File

@@ -253,7 +253,7 @@ async def get_coap_context(hass: HomeAssistant) -> COAP:
ipv4: list[IPv4Address] = []
if not network.async_only_default_interface_enabled(adapters):
ipv4.extend(
address
cast(IPv4Address, address)
for address in await network.async_get_enabled_source_ips(hass)
if address.version == 4
and not (

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
from ipaddress import IPv4Address, IPv6Address
from typing import cast
from homeassistant.components import network
from homeassistant.core import HomeAssistant
@@ -15,5 +16,8 @@ async def async_build_source_set(hass: HomeAssistant) -> set[IPv4Address | IPv6A
for source_ip in await network.async_get_enabled_source_ips(hass)
if not source_ip.is_loopback
and not source_ip.is_global
and ((source_ip.version == 6 and source_ip.scope_id) or source_ip.version == 4)
and (
(source_ip.version == 6 and cast(IPv6Address, source_ip).scope_id)
or source_ip.version == 4
)
}

View File

@@ -6,9 +6,9 @@ import asyncio
from collections.abc import Callable, Coroutine, Mapping
from datetime import timedelta
from enum import Enum
from ipaddress import IPv4Address
from ipaddress import IPv4Address, IPv6Address
import logging
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast
from async_upnp_client.aiohttp import AiohttpSessionRequester
from async_upnp_client.const import AddressTupleVXType, DeviceOrServiceType, SsdpSource
@@ -260,6 +260,7 @@ class Scanner:
for source_ip in await async_build_source_set(self.hass):
source_ip_str = str(source_ip)
if source_ip.version == 6:
source_ip = cast(IPv6Address, source_ip)
assert source_ip.scope_id is not None
source_tuple: AddressTupleVXType = (
source_ip_str,

View File

@@ -4,10 +4,11 @@ from __future__ import annotations
import asyncio
from contextlib import ExitStack
from ipaddress import IPv6Address
import logging
import socket
from time import time
from typing import Any
from typing import Any, cast
from urllib.parse import urljoin
import xml.etree.ElementTree as ET
@@ -171,6 +172,7 @@ class Server:
for source_ip in await async_build_source_set(self.hass):
source_ip_str = str(source_ip)
if source_ip.version == 6:
source_ip = cast(IPv6Address, source_ip)
assert source_ip.scope_id is not None
source_tuple: AddressTupleVXType = (
source_ip_str,

View File

@@ -165,7 +165,7 @@ def number(
attribute: str,
minimum: float | None = None,
maximum: float | None = None,
return_type: type[float] | type[int] = float,
return_type: type[float | int] = float,
**kwargs: Any,
) -> Callable[[Any], float | int | None]:
"""Convert the result to a number (float or int).

View File

@@ -47,7 +47,7 @@ class VeluxEntity(Entity):
_attr_should_poll = False
_attr_has_entity_name = True
update_callback: Callable[["Node"], Awaitable[None]] | None = None
update_callback: Callable[[Node], Awaitable[None]] | None = None
_attr_available = True
_unavailable_logged = False

View File

@@ -20,8 +20,8 @@ MINOR_VERSION: Final = 2
PATCH_VERSION: Final = "0.dev0"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 2)
REQUIRED_NEXT_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 2)
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2)
REQUIRED_NEXT_PYTHON_VER: Final[tuple[int, int, int]] = (3, 14, 2)
# Truthy date string triggers showing related deprecation warning messages.
REQUIRED_NEXT_PYTHON_HA_RELEASE: Final = ""

View File

@@ -173,7 +173,7 @@ class RuntimeConfig:
safe_mode: bool = False
class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[name-defined,misc]
"""Event loop policy for Home Assistant."""
def __init__(self, debug: bool) -> None:
@@ -184,7 +184,7 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
@property
def loop_name(self) -> str:
"""Return name of the loop."""
return self._loop_factory.__name__ # type: ignore[no-any-return,attr-defined]
return self._loop_factory.__name__ # type: ignore[no-any-return]
def new_event_loop(self) -> asyncio.AbstractEventLoop:
"""Get the event loop."""
@@ -281,7 +281,7 @@ def run(runtime_config: RuntimeConfig) -> int:
"""Run Home Assistant."""
_enable_posix_spawn()
set_open_file_descriptor_limit()
asyncio.set_event_loop_policy(HassEventLoopPolicy(runtime_config.debug))
asyncio.set_event_loop_policy(HassEventLoopPolicy(runtime_config.debug)) # type: ignore[deprecated]
# Backport of cpython 3.9 asyncio.run with a _cancel_all_tasks that times out
loop = asyncio.new_event_loop()
try:

View File

@@ -61,7 +61,7 @@ def run(args: list[str]) -> int:
print("Aborting script, could not install dependency", req)
return 1
asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False))
asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False)) # type: ignore[deprecated]
return script.run(args[1:])

View File

@@ -48,7 +48,7 @@ def run(args: Sequence[str] | None) -> None:
parser_change_pw.add_argument("new_password", type=str)
parser_change_pw.set_defaults(func=change_password)
asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False))
asyncio.set_event_loop_policy(runner.HassEventLoopPolicy(False)) # type: ignore[deprecated]
asyncio.run(run_command(parser.parse_args(args)))

View File

@@ -36,7 +36,7 @@ def run(args):
args = parser.parse_args()
bench = BENCHMARKS[args.name]
print("Using event loop:", asyncio.get_event_loop_policy().loop_name)
print("Using event loop:", asyncio.get_event_loop_policy().loop_name) # type: ignore[deprecated]
with suppress(KeyboardInterrupt):
while True:

View File

@@ -6,15 +6,11 @@ derived from EntityDescription and sub classes thereof.
from __future__ import annotations
from annotationlib import Format, get_annotations
import dataclasses
import sys
from typing import TYPE_CHECKING, Any, cast, dataclass_transform
if sys.version_info >= (3, 14):
from annotationlib import Format, get_annotations
else:
from typing_extensions import Format, get_annotations
if TYPE_CHECKING:
from _typeshed import DataclassInstance
@@ -103,7 +99,7 @@ class FrozenOrThawed(type):
continue
annotations |= get_annotations(parent, format=Format.FORWARDREF)
if "__annotations__" in cls.__dict__ or sys.version_info < (3, 14):
if "__annotations__" in cls.__dict__:
cls.__annotations__ = annotations
else:

2
mypy.ini generated
View File

@@ -3,7 +3,7 @@
# To update, run python3 -m script.hassfest -p mypy_config
[mypy]
python_version = 3.13
python_version = 3.14
platform = linux
plugins = pydantic.mypy, pydantic.v1.mypy
show_error_codes = true

View File

@@ -18,11 +18,10 @@ classifiers = [
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Home Automation",
]
requires-python = ">=3.13.2"
requires-python = ">=3.14.2"
dependencies = [
"aiodns==4.0.0",
# Integrations may depend on hassio integration without listing it to
@@ -102,7 +101,7 @@ include-package-data = true
include = ["homeassistant*"]
[tool.pylint.MAIN]
py-version = "3.13"
py-version = "3.14"
# Use a conservative default here; 2 should speed up most setups and not hurt
# any too bad. Override on command line as appropriate.
jobs = 2

View File

@@ -80,7 +80,7 @@ WORKDIR /config
_HASSFEST_TEMPLATE = r"""# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
FROM python:3.13-alpine
FROM python:3.14-alpine
ENV \
UV_SYSTEM_PYTHON=true \

View File

@@ -1,7 +1,7 @@
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
FROM python:3.13-alpine
FROM python:3.14-alpine
ENV \
UV_SYSTEM_PYTHON=true \

View File

@@ -380,7 +380,7 @@ async def test_cloud_heater(
before_attrs: dict,
service_name: str,
service_params: dict,
effect: "contextlib.AbstractContextManager",
effect: contextlib.AbstractContextManager,
heater_control_calls: list,
heater_set_temp_calls: list,
after_state: HVACMode,
@@ -533,7 +533,7 @@ async def test_local_heater(
before_attrs: dict,
service_name: str,
service_params: dict,
effect: "contextlib.AbstractContextManager",
effect: contextlib.AbstractContextManager,
heater_mode_set_individually_calls: list,
heater_mode_set_off_calls: list,
heater_set_target_temperature_calls: list,

View File

@@ -139,7 +139,7 @@ async def test_sensors(hass: HomeAssistant) -> None:
class CoordinatorStub:
"""Coordinator stub for testing entity restoration behavior."""
instances: list["CoordinatorStub"] = []
instances: list[CoordinatorStub] = []
def __init__(
self,