1
0
mirror of https://github.com/esphome/esphome.git synced 2025-01-18 20:10:55 +00:00

pyupgrade -py311-plus

This commit is contained in:
Katherine Whitlock 2025-01-17 19:30:44 -05:00
parent 6b55b7e1b6
commit b9511d45d0
28 changed files with 161 additions and 180 deletions

View File

@ -4,7 +4,7 @@
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.5.4 rev: v0.9.2
hooks: hooks:
# Run the linter. # Run the linter.
- id: ruff - id: ruff

View File

@ -1,18 +1,17 @@
import base64 import base64
import secrets
from pathlib import Path from pathlib import Path
from typing import Optional
import re import re
import secrets
import requests import requests
from ruamel.yaml import YAML from ruamel.yaml import YAML
import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.final_validate as fv
from esphome import git from esphome import git
import esphome.codegen as cg
from esphome.components.packages import validate_source_shorthand from esphome.components.packages import validate_source_shorthand
from esphome.const import CONF_REF, CONF_WIFI, CONF_ESPHOME, CONF_PROJECT import esphome.config_validation as cv
from esphome.const import CONF_ESPHOME, CONF_PROJECT, CONF_REF, CONF_WIFI
import esphome.final_validate as fv
from esphome.yaml_util import dump from esphome.yaml_util import dump
dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import") dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import")
@ -84,7 +83,7 @@ async def to_code(config):
def import_config( def import_config(
path: str, path: str,
name: str, name: str,
friendly_name: Optional[str], friendly_name: str | None,
project_name: str, project_name: str,
import_url: str, import_url: str,
network: str = CONF_WIFI, network: str = CONF_WIFI,

View File

@ -2,7 +2,6 @@ from dataclasses import dataclass
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
from typing import Optional, Union
from esphome import git from esphome import git
import esphome.codegen as cg import esphome.codegen as cg
@ -142,7 +141,7 @@ class RawSdkconfigValue:
value: str value: str
SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue] SdkconfigValueType = bool | int | HexInt | str | RawSdkconfigValue
def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType): def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
@ -159,8 +158,8 @@ def add_idf_component(
ref: str = None, ref: str = None,
path: str = None, path: str = None,
refresh: TimePeriod = None, refresh: TimePeriod = None,
components: Optional[list[str]] = None, components: list[str] | None = None,
submodules: Optional[list[str]] = None, submodules: list[str] | None = None,
): ):
"""Add an esp-idf component to the project.""" """Add an esp-idf component to the project."""
if not CORE.using_esp_idf: if not CORE.using_esp_idf:

View File

@ -1,5 +1,5 @@
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Callable
import esphome.codegen as cg import esphome.codegen as cg

View File

@ -1,4 +1,5 @@
from typing import Any, Callable from collections.abc import Callable
from typing import Any
from esphome import automation from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg

View File

@ -1,5 +1,3 @@
from typing import Union
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import image from esphome.components import image
from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw
@ -344,7 +342,7 @@ lv_image_list = LValidator(
lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal)
def lv_pct(value: Union[int, float]): def lv_pct(value: int | float):
if isinstance(value, float): if isinstance(value, float):
value = int(value * 100) value = int(value * 100)
return literal(f"lv_pct({value})") return literal(f"lv_pct({value})")

View File

@ -1,5 +1,4 @@
import abc import abc
from typing import Union
from esphome import codegen as cg from esphome import codegen as cg
from esphome.config import Config from esphome.config import Config
@ -75,7 +74,7 @@ class CodeContext(abc.ABC):
code_context = None code_context = None
@abc.abstractmethod @abc.abstractmethod
def add(self, expression: Union[Expression, Statement]): def add(self, expression: Expression | Statement):
pass pass
@staticmethod @staticmethod
@ -89,13 +88,13 @@ class CodeContext(abc.ABC):
CodeContext.append(RawStatement("}")) CodeContext.append(RawStatement("}"))
@staticmethod @staticmethod
def append(expression: Union[Expression, Statement]): def append(expression: Expression | Statement):
if CodeContext.code_context is not None: if CodeContext.code_context is not None:
CodeContext.code_context.add(expression) CodeContext.code_context.add(expression)
return expression return expression
def __init__(self): def __init__(self):
self.previous: Union[CodeContext | None] = None self.previous: CodeContext | None = None
self.indent_level = 0 self.indent_level = 0
async def __aenter__(self): async def __aenter__(self):
@ -121,7 +120,7 @@ class MainContext(CodeContext):
Code generation into the main() function Code generation into the main() function
""" """
def add(self, expression: Union[Expression, Statement]): def add(self, expression: Expression | Statement):
return cg.add(self.indented_statement(expression)) return cg.add(self.indented_statement(expression))
@ -144,7 +143,7 @@ class LambdaContext(CodeContext):
self.capture = capture self.capture = capture
self.where = where self.where = where
def add(self, expression: Union[Expression, Statement]): def add(self, expression: Expression | Statement):
self.code_list.append(self.indented_statement(expression)) self.code_list.append(self.indented_statement(expression))
return expression return expression
@ -185,7 +184,7 @@ class LvContext(LambdaContext):
async def __aexit__(self, exc_type, exc_val, exc_tb): async def __aexit__(self, exc_type, exc_val, exc_tb):
await super().__aexit__(exc_type, exc_val, exc_tb) await super().__aexit__(exc_type, exc_val, exc_tb)
def add(self, expression: Union[Expression, Statement]): def add(self, expression: Expression | Statement):
cg.add(expression) cg.add(expression)
return expression return expression
@ -301,7 +300,7 @@ lvgl_static = MockObj("LvglComponent", "::")
# equivalent to cg.add() for the current code context # equivalent to cg.add() for the current code context
def lv_add(expression: Union[Expression, Statement]): def lv_add(expression: Expression | Statement):
return CodeContext.append(expression) return CodeContext.append(expression)

View File

@ -1,5 +1,5 @@
import sys import sys
from typing import Any, Union from typing import Any
from esphome import codegen as cg, config_validation as cv from esphome import codegen as cg, config_validation as cv
from esphome.config_validation import Invalid from esphome.config_validation import Invalid
@ -263,7 +263,7 @@ async def wait_for_widgets():
await FakeAwaitable(widgets_wait_generator()) await FakeAwaitable(widgets_wait_generator())
async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: async def get_widgets(config: dict | list, id: str = CONF_ID) -> list[Widget]:
if not config: if not config:
return [] return []
if not isinstance(config, list): if not isinstance(config, list):

View File

@ -1,10 +1,11 @@
from collections.abc import Awaitable from collections.abc import Awaitable, Callable
from typing import Any, Callable, Optional from typing import Any
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_ID from esphome.const import CONF_ID
from . import const from . import const
from .schema import TSchema, SettingSchema from .schema import SettingSchema, TSchema
opentherm_ns = cg.esphome_ns.namespace("opentherm") opentherm_ns = cg.esphome_ns.namespace("opentherm")
OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component)
@ -102,7 +103,7 @@ def define_setting_readers(component_type: str, keys: list[str]) -> None:
def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]):
messages: dict[str, tuple[bool, Optional[int]]] = {} messages: dict[str, tuple[bool, int | None]] = {}
for key in keys: for key in keys:
messages[schemas[key].message] = ( messages[schemas[key].message] = (
schemas[key].keep_updated, schemas[key].keep_updated,
@ -112,11 +113,10 @@ def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]):
msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}") msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}")
if keep_updated: if keep_updated:
cg.add(hub.add_repeating_message(msg_expr)) cg.add(hub.add_repeating_message(msg_expr))
elif order is not None:
cg.add(hub.add_initial_message(msg_expr, order))
else: else:
if order is not None: cg.add(hub.add_initial_message(msg_expr))
cg.add(hub.add_initial_message(msg_expr, order))
else:
cg.add(hub.add_initial_message(msg_expr))
def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None: def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None:
@ -128,7 +128,7 @@ Create = Callable[[dict[str, Any], str, cg.MockObj], Awaitable[cg.Pvariable]]
def create_only_conf( def create_only_conf(
create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]] create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]],
) -> Create: ) -> Create:
return lambda conf, _key, _hub: create(conf) return lambda conf, _key, _hub: create(conf)

View File

@ -2,16 +2,10 @@
# inputs of the OpenTherm component. # inputs of the OpenTherm component.
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, TypeVar, Any from typing import Any, TypeVar
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_KILOWATT,
UNIT_MICROAMP,
UNIT_PERCENT,
UNIT_REVOLUTIONS_PER_MINUTE,
DEVICE_CLASS_COLD, DEVICE_CLASS_COLD,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
@ -22,6 +16,12 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE, STATE_CLASS_NONE,
STATE_CLASS_TOTAL_INCREASING, STATE_CLASS_TOTAL_INCREASING,
UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_KILOWATT,
UNIT_MICROAMP,
UNIT_PERCENT,
UNIT_REVOLUTIONS_PER_MINUTE,
) )
@ -61,11 +61,11 @@ TSchema = TypeVar("TSchema", bound=EntitySchema)
class SensorSchema(EntitySchema): class SensorSchema(EntitySchema):
accuracy_decimals: int accuracy_decimals: int
state_class: str state_class: str
unit_of_measurement: Optional[str] = None unit_of_measurement: str | None = None
icon: Optional[str] = None icon: str | None = None
device_class: Optional[str] = None device_class: str | None = None
disabled_by_default: bool = False disabled_by_default: bool = False
order: Optional[int] = None order: int | None = None
SENSORS: dict[str, SensorSchema] = { SENSORS: dict[str, SensorSchema] = {
@ -461,9 +461,9 @@ SENSORS: dict[str, SensorSchema] = {
@dataclass @dataclass
class BinarySensorSchema(EntitySchema): class BinarySensorSchema(EntitySchema):
icon: Optional[str] = None icon: str | None = None
device_class: Optional[str] = None device_class: str | None = None
order: Optional[int] = None order: int | None = None
BINARY_SENSORS: dict[str, BinarySensorSchema] = { BINARY_SENSORS: dict[str, BinarySensorSchema] = {
@ -654,7 +654,7 @@ BINARY_SENSORS: dict[str, BinarySensorSchema] = {
@dataclass @dataclass
class SwitchSchema(EntitySchema): class SwitchSchema(EntitySchema):
default_mode: Optional[str] = None default_mode: str | None = None
SWITCHES: dict[str, SwitchSchema] = { SWITCHES: dict[str, SwitchSchema] = {
@ -721,9 +721,9 @@ class InputSchema(EntitySchema):
unit_of_measurement: str unit_of_measurement: str
step: float step: float
range: tuple[int, int] range: tuple[int, int]
icon: Optional[str] = None icon: str | None = None
auto_max_value: Optional[AutoConfigure] = None auto_max_value: AutoConfigure | None = None
auto_min_value: Optional[AutoConfigure] = None auto_min_value: AutoConfigure | None = None
INPUTS: dict[str, InputSchema] = { INPUTS: dict[str, InputSchema] = {
@ -834,7 +834,7 @@ class SettingSchema(EntitySchema):
backing_type: str backing_type: str
validation_schema: cv.Schema validation_schema: cv.Schema
default_value: Any default_value: Any
order: Optional[int] = None order: int | None = None
SETTINGS: dict[str, SettingSchema] = { SETTINGS: dict[str, SettingSchema] = {

View File

@ -1,10 +1,10 @@
from typing import Callable from collections.abc import Callable
from voluptuous import Schema from voluptuous import Schema
import esphome.config_validation as cv import esphome.config_validation as cv
from . import const, schema, generate from . import const, generate, schema
from .schema import TSchema from .schema import TSchema

View File

@ -1,5 +1,3 @@
from typing import Optional
from esphome import automation from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import mqtt, web_server from esphome.components import mqtt, web_server
@ -61,9 +59,9 @@ async def setup_text_core_(
var, var,
config, config,
*, *,
min_length: Optional[int], min_length: int | None,
max_length: Optional[int], max_length: int | None,
pattern: Optional[str], pattern: str | None,
): ):
await setup_entity(var, config) await setup_entity(var, config)
@ -90,9 +88,9 @@ async def register_text(
var, var,
config, config,
*, *,
min_length: Optional[int] = 0, min_length: int | None = 0,
max_length: Optional[int] = 255, max_length: int | None = 255,
pattern: Optional[str] = None, pattern: str | None = None,
): ):
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
@ -105,9 +103,9 @@ async def register_text(
async def new_text( async def new_text(
config, config,
*, *,
min_length: Optional[int] = 0, min_length: int | None = 0,
max_length: Optional[int] = 255, max_length: int | None = 255,
pattern: Optional[str] = None, pattern: str | None = None,
): ):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await register_text( await register_text(

View File

@ -1,6 +1,5 @@
from importlib import resources from importlib import resources
import logging import logging
from typing import Optional
import tzlocal import tzlocal
@ -40,7 +39,7 @@ SyncTrigger = time_ns.class_("SyncTrigger", automation.Trigger.template(), cg.Co
TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition) TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition)
def _load_tzdata(iana_key: str) -> Optional[bytes]: def _load_tzdata(iana_key: str) -> bytes | None:
# From https://tzdata.readthedocs.io/en/latest/#examples # From https://tzdata.readthedocs.io/en/latest/#examples
try: try:
package_loc, resource = iana_key.rsplit("/", 1) package_loc, resource = iana_key.rsplit("/", 1)

View File

@ -1,36 +1,36 @@
from typing import Optional
import re import re
from esphome import automation, pins
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.final_validate as fv
from esphome.yaml_util import make_data_base
from esphome import pins, automation
from esphome.const import ( from esphome.const import (
CONF_BAUD_RATE,
CONF_ID,
CONF_NUMBER,
CONF_RX_PIN,
CONF_TX_PIN,
CONF_PORT,
CONF_UART_ID,
CONF_DATA,
CONF_RX_BUFFER_SIZE,
CONF_INVERTED,
CONF_INVERT,
CONF_TRIGGER_ID,
CONF_SEQUENCE,
CONF_TIMEOUT,
CONF_DEBUG,
CONF_DIRECTION,
CONF_AFTER, CONF_AFTER,
CONF_BAUD_RATE,
CONF_BYTES, CONF_BYTES,
CONF_DATA,
CONF_DEBUG,
CONF_DELIMITER, CONF_DELIMITER,
CONF_DIRECTION,
CONF_DUMMY_RECEIVER, CONF_DUMMY_RECEIVER,
CONF_DUMMY_RECEIVER_ID, CONF_DUMMY_RECEIVER_ID,
CONF_ID,
CONF_INVERT,
CONF_INVERTED,
CONF_LAMBDA, CONF_LAMBDA,
CONF_NUMBER,
CONF_PORT,
CONF_RX_BUFFER_SIZE,
CONF_RX_PIN,
CONF_SEQUENCE,
CONF_TIMEOUT,
CONF_TRIGGER_ID,
CONF_TX_PIN,
CONF_UART_ID,
PLATFORM_HOST, PLATFORM_HOST,
) )
from esphome.core import CORE from esphome.core import CORE
import esphome.final_validate as fv
from esphome.yaml_util import make_data_base
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
uart_ns = cg.esphome_ns.namespace("uart") uart_ns = cg.esphome_ns.namespace("uart")
@ -321,12 +321,12 @@ def final_validate_device_schema(
name: str, name: str,
*, *,
uart_bus: str = CONF_UART_ID, uart_bus: str = CONF_UART_ID,
baud_rate: Optional[int] = None, baud_rate: int | None = None,
require_tx: bool = False, require_tx: bool = False,
require_rx: bool = False, require_rx: bool = False,
data_bits: Optional[int] = None, data_bits: int | None = None,
parity: Optional[str] = None, parity: str | None = None,
stop_bits: Optional[int] = None, stop_bits: int | None = None,
): ):
def validate_baud_rate(value): def validate_baud_rate(value):
if value != baud_rate: if value != baud_rate:

View File

@ -2,7 +2,7 @@ import logging
import math import math
import os import os
import re import re
from typing import TYPE_CHECKING, Optional, Union from typing import TYPE_CHECKING
from esphome.const import ( from esphome.const import (
CONF_COMMENT, CONF_COMMENT,
@ -326,7 +326,7 @@ class ID:
else: else:
self.is_manual = is_manual self.is_manual = is_manual
self.is_declaration = is_declaration self.is_declaration = is_declaration
self.type: Optional[MockObjClass] = type self.type: MockObjClass | None = type
def resolve(self, registered_ids): def resolve(self, registered_ids):
from esphome.config_validation import RESERVED_IDS from esphome.config_validation import RESERVED_IDS
@ -477,20 +477,20 @@ class EsphomeCore:
self.vscode = False self.vscode = False
self.ace = False self.ace = False
# The name of the node # The name of the node
self.name: Optional[str] = None self.name: str | None = None
# The friendly name of the node # The friendly name of the node
self.friendly_name: Optional[str] = None self.friendly_name: str | None = None
# The area / zone of the node # The area / zone of the node
self.area: Optional[str] = None self.area: str | None = None
# Additional data components can store temporary data in # Additional data components can store temporary data in
# The first key to this dict should always be the integration name # The first key to this dict should always be the integration name
self.data = {} self.data = {}
# The relative path to the configuration YAML # The relative path to the configuration YAML
self.config_path: Optional[str] = None self.config_path: str | None = None
# The relative path to where all build files are stored # The relative path to where all build files are stored
self.build_path: Optional[str] = None self.build_path: str | None = None
# The validated configuration, this is None until the config has been validated # The validated configuration, this is None until the config has been validated
self.config: Optional[ConfigType] = None self.config: ConfigType | None = None
# The pending tasks in the task queue (mostly for C++ generation) # The pending tasks in the task queue (mostly for C++ generation)
# This is a priority queue (with heapq) # This is a priority queue (with heapq)
# Each item is a tuple of form: (-priority, unique number, task) # Each item is a tuple of form: (-priority, unique number, task)
@ -510,7 +510,7 @@ class EsphomeCore:
# A set of defines to set for the compile process in esphome/core/defines.h # A set of defines to set for the compile process in esphome/core/defines.h
self.defines: set[Define] = set() self.defines: set[Define] = set()
# A map of all platformio options to apply # A map of all platformio options to apply
self.platformio_options: dict[str, Union[str, list[str]]] = {} self.platformio_options: dict[str, str | list[str]] = {}
# A set of strings of names of loaded integrations, used to find namespace ID conflicts # A set of strings of names of loaded integrations, used to find namespace ID conflicts
self.loaded_integrations = set() self.loaded_integrations = set()
# A set of component IDs to track what Component subclasses are declared # A set of component IDs to track what Component subclasses are declared
@ -545,7 +545,7 @@ class EsphomeCore:
PIN_SCHEMA_REGISTRY.reset() PIN_SCHEMA_REGISTRY.reset()
@property @property
def address(self) -> Optional[str]: def address(self) -> str | None:
if self.config is None: if self.config is None:
raise ValueError("Config has not been loaded yet") raise ValueError("Config has not been loaded yet")
@ -558,7 +558,7 @@ class EsphomeCore:
return None return None
@property @property
def web_port(self) -> Optional[int]: def web_port(self) -> int | None:
if self.config is None: if self.config is None:
raise ValueError("Config has not been loaded yet") raise ValueError("Config has not been loaded yet")
@ -571,7 +571,7 @@ class EsphomeCore:
return None return None
@property @property
def comment(self) -> Optional[str]: def comment(self) -> str | None:
if self.config is None: if self.config is None:
raise ValueError("Config has not been loaded yet") raise ValueError("Config has not been loaded yet")
@ -769,7 +769,7 @@ class EsphomeCore:
_LOGGER.debug("Adding define: %s", define) _LOGGER.debug("Adding define: %s", define)
return define return define
def add_platformio_option(self, key: str, value: Union[str, list[str]]) -> None: def add_platformio_option(self, key: str, value: str | list[str]) -> None:
new_val = value new_val = value
old_val = self.platformio_options.get(key) old_val = self.platformio_options.get(key)
if isinstance(old_val, list): if isinstance(old_val, list):

View File

@ -43,13 +43,13 @@ the last `yield` expression defines what is returned.
""" """
import collections import collections
from collections.abc import Awaitable, Generator, Iterator from collections.abc import Awaitable, Callable, Generator, Iterator
import functools import functools
import heapq import heapq
import inspect import inspect
import logging import logging
import types import types
from typing import Any, Callable from typing import Any
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -1,9 +1,9 @@
import abc import abc
from collections.abc import Sequence from collections.abc import Callable, Sequence
import inspect import inspect
import math import math
import re import re
from typing import Any, Callable, Optional, Union from typing import Any
from esphome.core import ( from esphome.core import (
CORE, CORE,
@ -35,19 +35,19 @@ class Expression(abc.ABC):
""" """
SafeExpType = Union[ SafeExpType = (
Expression, Expression
bool, | bool
str, | str
str, | str
int, | int
float, | float
TimePeriod, | TimePeriod
type[bool], | type[bool]
type[int], | type[int]
type[float], | type[float]
Sequence[Any], | Sequence[Any]
] )
class RawExpression(Expression): class RawExpression(Expression):
@ -90,7 +90,7 @@ class VariableDeclarationExpression(Expression):
class ExpressionList(Expression): class ExpressionList(Expression):
__slots__ = ("args",) __slots__ = ("args",)
def __init__(self, *args: Optional[SafeExpType]): def __init__(self, *args: SafeExpType | None):
# Remove every None on end # Remove every None on end
args = list(args) args = list(args)
while args and args[-1] is None: while args and args[-1] is None:
@ -139,7 +139,7 @@ class CallExpression(Expression):
class StructInitializer(Expression): class StructInitializer(Expression):
__slots__ = ("base", "args") __slots__ = ("base", "args")
def __init__(self, base: Expression, *args: tuple[str, Optional[SafeExpType]]): def __init__(self, base: Expression, *args: tuple[str, SafeExpType | None]):
self.base = base self.base = base
# TODO: args is always a Tuple, is this check required? # TODO: args is always a Tuple, is this check required?
if not isinstance(args, OrderedDict): if not isinstance(args, OrderedDict):
@ -197,9 +197,7 @@ class ParameterExpression(Expression):
class ParameterListExpression(Expression): class ParameterListExpression(Expression):
__slots__ = ("parameters",) __slots__ = ("parameters",)
def __init__( def __init__(self, *parameters: ParameterExpression | tuple[SafeExpType, str]):
self, *parameters: Union[ParameterExpression, tuple[SafeExpType, str]]
):
self.parameters = [] self.parameters = []
for parameter in parameters: for parameter in parameters:
if not isinstance(parameter, ParameterExpression): if not isinstance(parameter, ParameterExpression):
@ -362,7 +360,7 @@ def safe_exp(obj: SafeExpType) -> Expression:
return IntLiteral(int(obj.total_seconds)) return IntLiteral(int(obj.total_seconds))
if isinstance(obj, TimePeriodMinutes): if isinstance(obj, TimePeriodMinutes):
return IntLiteral(int(obj.total_minutes)) return IntLiteral(int(obj.total_minutes))
if isinstance(obj, (tuple, list)): if isinstance(obj, tuple | list):
return ArrayInitializer(*[safe_exp(o) for o in obj]) return ArrayInitializer(*[safe_exp(o) for o in obj])
if obj is bool: if obj is bool:
return bool_ return bool_
@ -461,7 +459,7 @@ def static_const_array(id_, rhs) -> "MockObj":
return obj return obj
def statement(expression: Union[Expression, Statement]) -> Statement: def statement(expression: Expression | Statement) -> Statement:
"""Convert expression into a statement unless is already a statement.""" """Convert expression into a statement unless is already a statement."""
if isinstance(expression, Statement): if isinstance(expression, Statement):
return expression return expression
@ -506,9 +504,9 @@ def with_local_variable(id_: ID, rhs: SafeExpType, callback: Callable, *args) ->
""" """
# throw if the callback is async: # throw if the callback is async:
assert not inspect.iscoroutinefunction( assert not inspect.iscoroutinefunction(callback), (
callback "with_local_variable() callback cannot be async!"
), "with_local_variable() callback cannot be async!" )
CORE.add(RawStatement("{")) # output opening curly brace CORE.add(RawStatement("{")) # output opening curly brace
obj = variable(id_, rhs, None, True) obj = variable(id_, rhs, None, True)
@ -579,7 +577,7 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable:
return Pvariable(id_, rhs) return Pvariable(id_, rhs)
def add(expression: Union[Expression, Statement]): def add(expression: Expression | Statement):
"""Add an expression to the codegen section. """Add an expression to the codegen section.
After this is called, the given given expression will After this is called, the given given expression will
@ -588,12 +586,12 @@ def add(expression: Union[Expression, Statement]):
CORE.add(expression) CORE.add(expression)
def add_global(expression: Union[SafeExpType, Statement]): def add_global(expression: SafeExpType | Statement):
"""Add an expression to the codegen global storage (above setup()).""" """Add an expression to the codegen global storage (above setup())."""
CORE.add_global(expression) CORE.add_global(expression)
def add_library(name: str, version: Optional[str], repository: Optional[str] = None): def add_library(name: str, version: str | None, repository: str | None = None):
"""Add a library to the codegen library storage. """Add a library to the codegen library storage.
:param name: The name of the library (for example 'AsyncTCP') :param name: The name of the library (for example 'AsyncTCP')
@ -619,7 +617,7 @@ def add_define(name: str, value: SafeExpType = None):
CORE.add_define(Define(name, safe_exp(value))) CORE.add_define(Define(name, safe_exp(value)))
def add_platformio_option(key: str, value: Union[str, list[str]]): def add_platformio_option(key: str, value: str | list[str]):
CORE.add_platformio_option(key, value) CORE.add_platformio_option(key, value)
@ -654,7 +652,7 @@ async def process_lambda(
parameters: list[tuple[SafeExpType, str]], parameters: list[tuple[SafeExpType, str]],
capture: str = "=", capture: str = "=",
return_type: SafeExpType = None, return_type: SafeExpType = None,
) -> Union[LambdaExpression, None]: ) -> LambdaExpression | None:
"""Process the given lambda value into a LambdaExpression. """Process the given lambda value into a LambdaExpression.
This is a coroutine because lambdas can depend on other IDs, This is a coroutine because lambdas can depend on other IDs,
@ -711,8 +709,8 @@ def is_template(value):
async def templatable( async def templatable(
value: Any, value: Any,
args: list[tuple[SafeExpType, str]], args: list[tuple[SafeExpType, str]],
output_type: Optional[SafeExpType], output_type: SafeExpType | None,
to_exp: Union[Callable, dict] = None, to_exp: Callable | dict = None,
): ):
"""Generate code for a templatable config option. """Generate code for a templatable config option.
@ -817,7 +815,7 @@ class MockObj(Expression):
assert self.op == "::" assert self.op == "::"
return MockObj(f"using namespace {self.base}") return MockObj(f"using namespace {self.base}")
def __getitem__(self, item: Union[str, Expression]) -> "MockObj": def __getitem__(self, item: str | Expression) -> "MockObj":
next_op = "." next_op = "."
if isinstance(item, str) and item.startswith("P"): if isinstance(item, str) and item.startswith("P"):
item = item[1:] item = item[1:]

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Coroutine from collections.abc import Callable, Coroutine
import contextlib import contextlib
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
@ -9,7 +9,7 @@ import json
import logging import logging
from pathlib import Path from pathlib import Path
import threading import threading
from typing import TYPE_CHECKING, Any, Callable from typing import TYPE_CHECKING, Any
from esphome.storage_json import ignored_devices_storage_path from esphome.storage_json import ignored_devices_storage_path

View File

@ -1,22 +1,16 @@
from __future__ import annotations from __future__ import annotations
import asyncio from asyncio import timeout as async_timeout
import sys
from icmplib import NameLookupError, async_resolve 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
async def _async_resolve_wrapper(hostname: str) -> list[str] | Exception: async def _async_resolve_wrapper(hostname: str) -> list[str] | Exception:
"""Wrap the icmplib async_resolve function.""" """Wrap the icmplib async_resolve function."""
try: try:
async with async_timeout(2): async with async_timeout(2):
return await async_resolve(hostname) return await async_resolve(hostname)
except (asyncio.TimeoutError, NameLookupError, UnicodeError) as ex: except (TimeoutError, NameLookupError, UnicodeError) as ex:
return ex return ex

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio import asyncio
import base64 import base64
from collections.abc import Iterable from collections.abc import Callable, Iterable
import datetime import datetime
import functools import functools
import gzip import gzip
@ -17,7 +17,7 @@ import shutil
import subprocess import subprocess
import threading import threading
import time import time
from typing import TYPE_CHECKING, Any, Callable, TypeVar from typing import TYPE_CHECKING, Any, TypeVar
from urllib.parse import urlparse from urllib.parse import urlparse
import tornado import tornado

View File

@ -1,3 +1,4 @@
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
import hashlib import hashlib
@ -5,7 +6,6 @@ import logging
from pathlib import Path from pathlib import Path
import re import re
import subprocess import subprocess
from typing import Callable, Optional
import urllib.parse import urllib.parse
import esphome.config_validation as cv import esphome.config_validation as cv
@ -45,12 +45,12 @@ def clone_or_update(
*, *,
url: str, url: str,
ref: str = None, ref: str = None,
refresh: Optional[TimePeriodSeconds], refresh: TimePeriodSeconds | None,
domain: str, domain: str,
username: str = None, username: str = None,
password: str = None, password: str = None,
submodules: Optional[list[str]] = None, submodules: list[str] | None = None,
) -> tuple[Path, Optional[Callable[[], None]]]: ) -> tuple[Path, Callable[[], None] | None]:
key = f"{url}@{ref}" key = f"{url}@{ref}"
if username is not None and password is not None: if username is not None and password is not None:

View File

@ -7,7 +7,6 @@ from pathlib import Path
import platform import platform
import re import re
import tempfile import tempfile
from typing import Union
from urllib.parse import urlparse from urllib.parse import urlparse
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -243,7 +242,7 @@ def read_file(path):
raise EsphomeError(f"Error reading file {path}: {err}") from err raise EsphomeError(f"Error reading file {path}: {err}") from err
def _write_file(path: Union[Path, str], text: Union[str, bytes]): def _write_file(path: Path | str, text: str | bytes):
"""Atomically writes `text` to the given path. """Atomically writes `text` to the given path.
Automatically creates all parent directories. Automatically creates all parent directories.
@ -276,7 +275,7 @@ def _write_file(path: Union[Path, str], text: Union[str, bytes]):
_LOGGER.error("Write file cleanup failed: %s", err) _LOGGER.error("Write file cleanup failed: %s", err)
def write_file(path: Union[Path, str], text: str): def write_file(path: Path | str, text: str):
try: try:
_write_file(path, text) _write_file(path, text)
except OSError as err: except OSError as err:
@ -285,7 +284,7 @@ def write_file(path: Union[Path, str], text: str):
raise EsphomeError(f"Could not write file at {path}") from err raise EsphomeError(f"Could not write file at {path}") from err
def write_file_if_changed(path: Union[Path, str], text: str) -> bool: def write_file_if_changed(path: Path | str, text: str) -> bool:
"""Write text to the given path, but not if the contents match already. """Write text to the given path, but not if the contents match already.
Returns true if the file was changed. Returns true if the file was changed.

View File

@ -1,3 +1,4 @@
from collections.abc import Callable
from contextlib import AbstractContextManager from contextlib import AbstractContextManager
from dataclasses import dataclass from dataclasses import dataclass
import importlib import importlib
@ -8,7 +9,7 @@ import logging
from pathlib import Path from pathlib import Path
import sys import sys
from types import ModuleType from types import ModuleType
from typing import Any, Callable, Optional from typing import Any
from esphome.const import SOURCE_FILE_EXTENSIONS from esphome.const import SOURCE_FILE_EXTENSIONS
from esphome.core import CORE from esphome.core import CORE
@ -53,7 +54,7 @@ class ComponentManifest:
return getattr(self.module, "IS_PLATFORM_COMPONENT", False) return getattr(self.module, "IS_PLATFORM_COMPONENT", False)
@property @property
def config_schema(self) -> Optional[Any]: def config_schema(self) -> Any | None:
return getattr(self.module, "CONFIG_SCHEMA", None) return getattr(self.module, "CONFIG_SCHEMA", None)
@property @property
@ -65,7 +66,7 @@ class ComponentManifest:
return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False) return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False)
@property @property
def to_code(self) -> Optional[Callable[[Any], None]]: def to_code(self) -> Callable[[Any], None] | None:
return getattr(self.module, "to_code", None) return getattr(self.module, "to_code", None)
@property @property
@ -88,7 +89,7 @@ class ComponentManifest:
return getattr(self.module, "CODEOWNERS", []) return getattr(self.module, "CODEOWNERS", [])
@property @property
def final_validate_schema(self) -> Optional[Callable[[ConfigType], None]]: def final_validate_schema(self) -> Callable[[ConfigType], None] | None:
"""Components can declare a `FINAL_VALIDATE_SCHEMA` cv.Schema that gets called """Components can declare a `FINAL_VALIDATE_SCHEMA` cv.Schema that gets called
after the main validation. In that function checks across components can be made. after the main validation. In that function checks across components can be made.
@ -121,7 +122,7 @@ class ComponentManifest:
class ComponentMetaFinder(importlib.abc.MetaPathFinder): class ComponentMetaFinder(importlib.abc.MetaPathFinder):
def __init__( def __init__(
self, components_path: Path, allowed_components: Optional[list[str]] = None self, components_path: Path, allowed_components: list[str] | None = None
) -> None: ) -> None:
self._allowed_components = allowed_components self._allowed_components = allowed_components
self._finders = [] self._finders = []
@ -132,7 +133,7 @@ class ComponentMetaFinder(importlib.abc.MetaPathFinder):
continue continue
self._finders.append(finder) self._finders.append(finder)
def find_spec(self, fullname: str, path: Optional[list[str]], target=None): def find_spec(self, fullname: str, path: list[str] | None, target=None):
if not fullname.startswith("esphome.components."): if not fullname.startswith("esphome.components."):
return None return None
parts = fullname.split(".") parts = fullname.split(".")
@ -159,7 +160,7 @@ def clear_component_meta_finders():
def install_meta_finder( def install_meta_finder(
components_path: Path, allowed_components: Optional[list[str]] = None components_path: Path, allowed_components: list[str] | None = None
): ):
sys.meta_path.insert(0, ComponentMetaFinder(components_path, allowed_components)) sys.meta_path.insert(0, ComponentMetaFinder(components_path, allowed_components))

View File

@ -5,7 +5,6 @@ import os
from pathlib import Path from pathlib import Path
import re import re
import subprocess import subprocess
from typing import Union
from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE
from esphome.core import CORE, EsphomeError from esphome.core import CORE, EsphomeError
@ -73,7 +72,7 @@ FILTER_PLATFORMIO_LINES = [
] ]
def run_platformio_cli(*args, **kwargs) -> Union[str, int]: def run_platformio_cli(*args, **kwargs) -> str | int:
os.environ["PLATFORMIO_FORCE_COLOR"] = "true" os.environ["PLATFORMIO_FORCE_COLOR"] = "true"
os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path()) os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path())
os.environ.setdefault( os.environ.setdefault(
@ -93,7 +92,7 @@ def run_platformio_cli(*args, **kwargs) -> Union[str, int]:
return run_external_command(platformio.__main__.main, *cmd, **kwargs) return run_external_command(platformio.__main__.main, *cmd, **kwargs)
def run_platformio_cli_run(config, verbose, *args, **kwargs) -> Union[str, int]: def run_platformio_cli_run(config, verbose, *args, **kwargs) -> str | int:
command = ["run", "-d", CORE.build_path] command = ["run", "-d", CORE.build_path]
if verbose: if verbose:
command += ["-v"] command += ["-v"]

View File

@ -6,7 +6,6 @@ from pathlib import Path
import re import re
import subprocess import subprocess
import sys import sys
from typing import Union
from esphome import const from esphome import const
@ -162,7 +161,7 @@ class RedirectText:
def run_external_command( def run_external_command(
func, *cmd, capture_stdout: bool = False, filter_lines: str = None func, *cmd, capture_stdout: bool = False, filter_lines: str = None
) -> Union[int, str]: ) -> int | str:
""" """
Run a function from an external package that acts like a main method. Run a function from an external package that acts like a main method.

View File

@ -3,7 +3,6 @@ import logging
import os import os
from pathlib import Path from pathlib import Path
import re import re
from typing import Union
from esphome import loader from esphome import loader
from esphome.config import iter_component_configs, iter_components from esphome.config import iter_component_configs, iter_components
@ -132,7 +131,7 @@ def update_storage_json():
new.save(path) new.save(path)
def format_ini(data: dict[str, Union[str, list[str]]]) -> str: def format_ini(data: dict[str, str | list[str]]) -> str:
content = "" content = ""
for key, value in sorted(data.items()): for key, value in sorted(data.items()):
if isinstance(value, list): if isinstance(value, list):
@ -212,9 +211,7 @@ def write_platformio_project():
write_platformio_ini(content) write_platformio_ini(content)
DEFINES_H_FORMAT = ( DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
ESPHOME_H_FORMAT
) = """\
#pragma once #pragma once
#include "esphome/core/macros.h" #include "esphome/core/macros.h"
{} {}

View File

@ -1,9 +1,9 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Callable
from zeroconf import IPVersion, ServiceInfo, ServiceStateChange, Zeroconf from zeroconf import IPVersion, ServiceInfo, ServiceStateChange, Zeroconf
from zeroconf.asyncio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf from zeroconf.asyncio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf

View File

@ -108,6 +108,7 @@ expected-line-ending-format = "LF"
[tool.ruff] [tool.ruff]
required-version = ">=0.5.0" required-version = ">=0.5.0"
target-version = "py311"
[tool.ruff.lint] [tool.ruff.lint]
select = [ select = [