mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
update minimal python version to 3.10 (#8850)
This commit is contained in:
parent
026f47bfb3
commit
aeb4e63950
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.6.0
|
||||
with:
|
||||
python-version: "3.9"
|
||||
python-version: "3.10"
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@ -20,8 +20,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.9"
|
||||
PYUPGRADE_TARGET: "--py39-plus"
|
||||
DEFAULT_PYTHON: "3.10"
|
||||
PYUPGRADE_TARGET: "--py310-plus"
|
||||
|
||||
concurrency:
|
||||
# yamllint disable-line rule:line-length
|
||||
@ -173,7 +173,6 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
@ -192,16 +191,12 @@ jobs:
|
||||
os: windows-latest
|
||||
- python-version: "3.10"
|
||||
os: windows-latest
|
||||
- python-version: "3.9"
|
||||
os: windows-latest
|
||||
- python-version: "3.13"
|
||||
os: macOS-latest
|
||||
- python-version: "3.12"
|
||||
os: macOS-latest
|
||||
- python-version: "3.10"
|
||||
os: macOS-latest
|
||||
- python-version: "3.9"
|
||||
os: macOS-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs:
|
||||
- common
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -96,7 +96,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.6.0
|
||||
with:
|
||||
python-version: "3.9"
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
@ -28,10 +28,10 @@ repos:
|
||||
- --branch=release
|
||||
- --branch=beta
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.15.2
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py39-plus]
|
||||
args: [--py310-plus]
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.37.1
|
||||
hooks:
|
||||
|
@ -2,7 +2,6 @@ import base64
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
from ruamel.yaml import YAML
|
||||
@ -84,7 +83,7 @@ async def to_code(config):
|
||||
def import_config(
|
||||
path: str,
|
||||
name: str,
|
||||
friendly_name: Optional[str],
|
||||
friendly_name: str | None,
|
||||
project_name: str,
|
||||
import_url: str,
|
||||
network: str = CONF_WIFI,
|
||||
|
@ -3,7 +3,6 @@ import itertools
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
from esphome import git
|
||||
import esphome.codegen as cg
|
||||
@ -189,7 +188,7 @@ class RawSdkconfigValue:
|
||||
value: str
|
||||
|
||||
|
||||
SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue]
|
||||
SdkconfigValueType = bool | int | HexInt | str | RawSdkconfigValue
|
||||
|
||||
|
||||
def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
|
||||
@ -206,8 +205,8 @@ def add_idf_component(
|
||||
ref: str = None,
|
||||
path: str = None,
|
||||
refresh: TimePeriod = None,
|
||||
components: Optional[list[str]] = None,
|
||||
submodules: Optional[list[str]] = None,
|
||||
components: list[str] | None = None,
|
||||
submodules: list[str] | None = None,
|
||||
):
|
||||
"""Add an esp-idf component to the project."""
|
||||
if not CORE.using_esp_idf:
|
||||
|
@ -1,6 +1,7 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
|
@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import MutableMapping
|
||||
from collections.abc import Callable, MutableMapping
|
||||
import logging
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
|
@ -1,5 +1,5 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Union
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import image
|
||||
from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw
|
||||
@ -361,7 +359,7 @@ lv_image_list = LValidator(
|
||||
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):
|
||||
value = int(value * 100)
|
||||
return literal(f"lv_pct({value})")
|
||||
|
@ -1,5 +1,4 @@
|
||||
import abc
|
||||
from typing import Union
|
||||
|
||||
from esphome import codegen as cg
|
||||
from esphome.config import Config
|
||||
@ -75,7 +74,7 @@ class CodeContext(abc.ABC):
|
||||
code_context = None
|
||||
|
||||
@abc.abstractmethod
|
||||
def add(self, expression: Union[Expression, Statement]):
|
||||
def add(self, expression: Expression | Statement):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@ -89,13 +88,13 @@ class CodeContext(abc.ABC):
|
||||
CodeContext.append(RawStatement("}"))
|
||||
|
||||
@staticmethod
|
||||
def append(expression: Union[Expression, Statement]):
|
||||
def append(expression: Expression | Statement):
|
||||
if CodeContext.code_context is not None:
|
||||
CodeContext.code_context.add(expression)
|
||||
return expression
|
||||
|
||||
def __init__(self):
|
||||
self.previous: Union[CodeContext | None] = None
|
||||
self.previous: CodeContext | None = None
|
||||
self.indent_level = 0
|
||||
|
||||
async def __aenter__(self):
|
||||
@ -121,7 +120,7 @@ class MainContext(CodeContext):
|
||||
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))
|
||||
|
||||
|
||||
@ -144,7 +143,7 @@ class LambdaContext(CodeContext):
|
||||
self.capture = capture
|
||||
self.where = where
|
||||
|
||||
def add(self, expression: Union[Expression, Statement]):
|
||||
def add(self, expression: Expression | Statement):
|
||||
self.code_list.append(self.indented_statement(expression))
|
||||
return expression
|
||||
|
||||
@ -186,7 +185,7 @@ class LvContext(LambdaContext):
|
||||
async def __aexit__(self, 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)
|
||||
return expression
|
||||
|
||||
@ -303,7 +302,7 @@ lvgl_static = MockObj("LvglComponent", "::")
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import sys
|
||||
from typing import Any, Union
|
||||
from typing import Any
|
||||
|
||||
from esphome import codegen as cg, config_validation as cv
|
||||
from esphome.config_validation import Invalid
|
||||
@ -262,7 +262,7 @@ async def wait_for_widgets():
|
||||
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:
|
||||
return []
|
||||
if not isinstance(config, list):
|
||||
|
@ -1,5 +1,5 @@
|
||||
from collections.abc import Awaitable
|
||||
from typing import Any, Callable, Optional
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
@ -103,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]):
|
||||
messages: dict[str, tuple[bool, Optional[int]]] = {}
|
||||
messages: dict[str, tuple[bool, int | None]] = {}
|
||||
for key in keys:
|
||||
messages[schemas[key].message] = (
|
||||
schemas[key].keep_updated,
|
||||
|
@ -2,7 +2,7 @@
|
||||
# inputs of the OpenTherm component.
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional, TypeVar
|
||||
from typing import Any, TypeVar
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
@ -61,11 +61,11 @@ TSchema = TypeVar("TSchema", bound=EntitySchema)
|
||||
class SensorSchema(EntitySchema):
|
||||
accuracy_decimals: int
|
||||
state_class: str
|
||||
unit_of_measurement: Optional[str] = None
|
||||
icon: Optional[str] = None
|
||||
device_class: Optional[str] = None
|
||||
unit_of_measurement: str | None = None
|
||||
icon: str | None = None
|
||||
device_class: str | None = None
|
||||
disabled_by_default: bool = False
|
||||
order: Optional[int] = None
|
||||
order: int | None = None
|
||||
|
||||
|
||||
SENSORS: dict[str, SensorSchema] = {
|
||||
@ -461,9 +461,9 @@ SENSORS: dict[str, SensorSchema] = {
|
||||
|
||||
@dataclass
|
||||
class BinarySensorSchema(EntitySchema):
|
||||
icon: Optional[str] = None
|
||||
device_class: Optional[str] = None
|
||||
order: Optional[int] = None
|
||||
icon: str | None = None
|
||||
device_class: str | None = None
|
||||
order: int | None = None
|
||||
|
||||
|
||||
BINARY_SENSORS: dict[str, BinarySensorSchema] = {
|
||||
@ -654,7 +654,7 @@ BINARY_SENSORS: dict[str, BinarySensorSchema] = {
|
||||
|
||||
@dataclass
|
||||
class SwitchSchema(EntitySchema):
|
||||
default_mode: Optional[str] = None
|
||||
default_mode: str | None = None
|
||||
|
||||
|
||||
SWITCHES: dict[str, SwitchSchema] = {
|
||||
@ -721,9 +721,9 @@ class InputSchema(EntitySchema):
|
||||
unit_of_measurement: str
|
||||
step: float
|
||||
range: tuple[int, int]
|
||||
icon: Optional[str] = None
|
||||
auto_max_value: Optional[AutoConfigure] = None
|
||||
auto_min_value: Optional[AutoConfigure] = None
|
||||
icon: str | None = None
|
||||
auto_max_value: AutoConfigure | None = None
|
||||
auto_min_value: AutoConfigure | None = None
|
||||
|
||||
|
||||
INPUTS: dict[str, InputSchema] = {
|
||||
@ -834,7 +834,7 @@ class SettingSchema(EntitySchema):
|
||||
backing_type: str
|
||||
validation_schema: cv.Schema
|
||||
default_value: Any
|
||||
order: Optional[int] = None
|
||||
order: int | None = None
|
||||
|
||||
|
||||
SETTINGS: dict[str, SettingSchema] = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Callable
|
||||
from collections.abc import Callable
|
||||
|
||||
from voluptuous import Schema
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Optional
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import mqtt, web_server
|
||||
@ -92,9 +90,9 @@ async def setup_text_core_(
|
||||
var,
|
||||
config,
|
||||
*,
|
||||
min_length: Optional[int],
|
||||
max_length: Optional[int],
|
||||
pattern: Optional[str],
|
||||
min_length: int | None,
|
||||
max_length: int | None,
|
||||
pattern: str | None,
|
||||
):
|
||||
await setup_entity(var, config)
|
||||
|
||||
@ -121,9 +119,9 @@ async def register_text(
|
||||
var,
|
||||
config,
|
||||
*,
|
||||
min_length: Optional[int] = 0,
|
||||
max_length: Optional[int] = 255,
|
||||
pattern: Optional[str] = None,
|
||||
min_length: int | None = 0,
|
||||
max_length: int | None = 255,
|
||||
pattern: str | None = None,
|
||||
):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
@ -136,9 +134,9 @@ async def register_text(
|
||||
async def new_text(
|
||||
config,
|
||||
*,
|
||||
min_length: Optional[int] = 0,
|
||||
max_length: Optional[int] = 255,
|
||||
pattern: Optional[str] = None,
|
||||
min_length: int | None = 0,
|
||||
max_length: int | None = 255,
|
||||
pattern: str | None = None,
|
||||
):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await register_text(
|
||||
|
@ -1,6 +1,5 @@
|
||||
from importlib import resources
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import tzlocal
|
||||
|
||||
@ -40,7 +39,7 @@ SyncTrigger = time_ns.class_("SyncTrigger", automation.Trigger.template(), cg.Co
|
||||
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
|
||||
try:
|
||||
package_loc, resource = iana_key.rsplit("/", 1)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
@ -322,12 +321,12 @@ def final_validate_device_schema(
|
||||
name: str,
|
||||
*,
|
||||
uart_bus: str = CONF_UART_ID,
|
||||
baud_rate: Optional[int] = None,
|
||||
baud_rate: int | None = None,
|
||||
require_tx: bool = False,
|
||||
require_rx: bool = False,
|
||||
data_bits: Optional[int] = None,
|
||||
parity: Optional[str] = None,
|
||||
stop_bits: Optional[int] = None,
|
||||
data_bits: int | None = None,
|
||||
parity: str | None = None,
|
||||
stop_bits: int | None = None,
|
||||
):
|
||||
def validate_baud_rate(value):
|
||||
if value != baud_rate:
|
||||
|
@ -7,7 +7,7 @@ import functools
|
||||
import heapq
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, Union
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@ -63,7 +63,7 @@ def iter_component_configs(config):
|
||||
yield p_name, platform, p_config
|
||||
|
||||
|
||||
ConfigPath = list[Union[str, int]]
|
||||
ConfigPath = list[str | int]
|
||||
path_context = contextvars.ContextVar("Config path")
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ import logging
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from esphome.const import (
|
||||
CONF_COMMENT,
|
||||
@ -326,7 +326,7 @@ class ID:
|
||||
else:
|
||||
self.is_manual = is_manual
|
||||
self.is_declaration = is_declaration
|
||||
self.type: Optional[MockObjClass] = type
|
||||
self.type: MockObjClass | None = type
|
||||
|
||||
def resolve(self, registered_ids):
|
||||
from esphome.config_validation import RESERVED_IDS
|
||||
@ -476,20 +476,20 @@ class EsphomeCore:
|
||||
# True if command is run from vscode api
|
||||
self.vscode = False
|
||||
# The name of the node
|
||||
self.name: Optional[str] = None
|
||||
self.name: str | None = None
|
||||
# The friendly name of the node
|
||||
self.friendly_name: Optional[str] = None
|
||||
self.friendly_name: str | None = None
|
||||
# The area / zone of the node
|
||||
self.area: Optional[str] = None
|
||||
self.area: str | None = None
|
||||
# Additional data components can store temporary data in
|
||||
# The first key to this dict should always be the integration name
|
||||
self.data = {}
|
||||
# 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
|
||||
self.build_path: Optional[str] = None
|
||||
self.build_path: str | None = None
|
||||
# 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)
|
||||
# This is a priority queue (with heapq)
|
||||
# Each item is a tuple of form: (-priority, unique number, task)
|
||||
@ -509,7 +509,7 @@ class EsphomeCore:
|
||||
# A set of defines to set for the compile process in esphome/core/defines.h
|
||||
self.defines: set[Define] = set()
|
||||
# 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
|
||||
self.loaded_integrations = set()
|
||||
# A set of component IDs to track what Component subclasses are declared
|
||||
@ -546,7 +546,7 @@ class EsphomeCore:
|
||||
PIN_SCHEMA_REGISTRY.reset()
|
||||
|
||||
@property
|
||||
def address(self) -> Optional[str]:
|
||||
def address(self) -> str | None:
|
||||
if self.config is None:
|
||||
raise ValueError("Config has not been loaded yet")
|
||||
|
||||
@ -559,7 +559,7 @@ class EsphomeCore:
|
||||
return None
|
||||
|
||||
@property
|
||||
def web_port(self) -> Optional[int]:
|
||||
def web_port(self) -> int | None:
|
||||
if self.config is None:
|
||||
raise ValueError("Config has not been loaded yet")
|
||||
|
||||
@ -572,7 +572,7 @@ class EsphomeCore:
|
||||
return None
|
||||
|
||||
@property
|
||||
def comment(self) -> Optional[str]:
|
||||
def comment(self) -> str | None:
|
||||
if self.config is None:
|
||||
raise ValueError("Config has not been loaded yet")
|
||||
|
||||
@ -773,7 +773,7 @@ class EsphomeCore:
|
||||
_LOGGER.debug("Adding define: %s", 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
|
||||
old_val = self.platformio_options.get(key)
|
||||
if isinstance(old_val, list):
|
||||
|
@ -42,14 +42,13 @@ Here everything is combined in `yield` expressions. You await other coroutines u
|
||||
the last `yield` expression defines what is returned.
|
||||
"""
|
||||
|
||||
import collections
|
||||
from collections.abc import Awaitable, Generator, Iterator
|
||||
from collections.abc import Awaitable, Callable, Generator, Iterator
|
||||
import functools
|
||||
import heapq
|
||||
import inspect
|
||||
import logging
|
||||
import types
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -126,7 +125,7 @@ def _flatten_generator(gen: Generator[Any, Any, Any]):
|
||||
ret = to_send if e.value is None else e.value
|
||||
return ret
|
||||
|
||||
if isinstance(val, collections.abc.Awaitable):
|
||||
if isinstance(val, Awaitable):
|
||||
# yielded object that is awaitable (like `yield some_new_style_method()`)
|
||||
# yield from __await__() like actual coroutines would.
|
||||
to_send = yield from val.__await__()
|
||||
|
@ -1,9 +1,9 @@
|
||||
import abc
|
||||
from collections.abc import Sequence
|
||||
from collections.abc import Callable, Sequence
|
||||
import inspect
|
||||
import math
|
||||
import re
|
||||
from typing import Any, Callable, Optional, Union
|
||||
from typing import Any
|
||||
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
@ -35,19 +35,19 @@ class Expression(abc.ABC):
|
||||
"""
|
||||
|
||||
|
||||
SafeExpType = Union[
|
||||
Expression,
|
||||
bool,
|
||||
str,
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
TimePeriod,
|
||||
type[bool],
|
||||
type[int],
|
||||
type[float],
|
||||
Sequence[Any],
|
||||
]
|
||||
SafeExpType = (
|
||||
Expression
|
||||
| bool
|
||||
| str
|
||||
| str
|
||||
| int
|
||||
| float
|
||||
| TimePeriod
|
||||
| type[bool]
|
||||
| type[int]
|
||||
| type[float]
|
||||
| Sequence[Any]
|
||||
)
|
||||
|
||||
|
||||
class RawExpression(Expression):
|
||||
@ -90,7 +90,7 @@ class VariableDeclarationExpression(Expression):
|
||||
class ExpressionList(Expression):
|
||||
__slots__ = ("args",)
|
||||
|
||||
def __init__(self, *args: Optional[SafeExpType]):
|
||||
def __init__(self, *args: SafeExpType | None):
|
||||
# Remove every None on end
|
||||
args = list(args)
|
||||
while args and args[-1] is None:
|
||||
@ -139,7 +139,7 @@ class CallExpression(Expression):
|
||||
class StructInitializer(Expression):
|
||||
__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
|
||||
# TODO: args is always a Tuple, is this check required?
|
||||
if not isinstance(args, OrderedDict):
|
||||
@ -197,9 +197,7 @@ class ParameterExpression(Expression):
|
||||
class ParameterListExpression(Expression):
|
||||
__slots__ = ("parameters",)
|
||||
|
||||
def __init__(
|
||||
self, *parameters: Union[ParameterExpression, tuple[SafeExpType, str]]
|
||||
):
|
||||
def __init__(self, *parameters: ParameterExpression | tuple[SafeExpType, str]):
|
||||
self.parameters = []
|
||||
for parameter in parameters:
|
||||
if not isinstance(parameter, ParameterExpression):
|
||||
@ -362,7 +360,7 @@ def safe_exp(obj: SafeExpType) -> Expression:
|
||||
return IntLiteral(int(obj.total_seconds))
|
||||
if isinstance(obj, TimePeriodMinutes):
|
||||
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])
|
||||
if obj is bool:
|
||||
return bool_
|
||||
@ -461,7 +459,7 @@ def static_const_array(id_, rhs) -> "MockObj":
|
||||
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."""
|
||||
if isinstance(expression, Statement):
|
||||
return expression
|
||||
@ -579,7 +577,7 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable:
|
||||
return Pvariable(id_, rhs)
|
||||
|
||||
|
||||
def add(expression: Union[Expression, Statement]):
|
||||
def add(expression: Expression | Statement):
|
||||
"""Add an expression to the codegen section.
|
||||
|
||||
After this is called, the given given expression will
|
||||
@ -588,12 +586,12 @@ def add(expression: Union[Expression, Statement]):
|
||||
CORE.add(expression)
|
||||
|
||||
|
||||
def add_global(expression: Union[SafeExpType, Statement], prepend: bool = False):
|
||||
def add_global(expression: SafeExpType | Statement, prepend: bool = False):
|
||||
"""Add an expression to the codegen global storage (above setup())."""
|
||||
CORE.add_global(expression, prepend)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
: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)))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@ -654,7 +652,7 @@ async def process_lambda(
|
||||
parameters: list[tuple[SafeExpType, str]],
|
||||
capture: str = "=",
|
||||
return_type: SafeExpType = None,
|
||||
) -> Union[LambdaExpression, None]:
|
||||
) -> LambdaExpression | None:
|
||||
"""Process the given lambda value into a LambdaExpression.
|
||||
|
||||
This is a coroutine because lambdas can depend on other IDs,
|
||||
@ -711,8 +709,8 @@ def is_template(value):
|
||||
async def templatable(
|
||||
value: Any,
|
||||
args: list[tuple[SafeExpType, str]],
|
||||
output_type: Optional[SafeExpType],
|
||||
to_exp: Union[Callable, dict] = None,
|
||||
output_type: SafeExpType | None,
|
||||
to_exp: Callable | dict = None,
|
||||
):
|
||||
"""Generate code for a templatable config option.
|
||||
|
||||
@ -821,7 +819,7 @@ class MockObj(Expression):
|
||||
assert self.op == "::"
|
||||
return MockObj(f"using namespace {self.base}")
|
||||
|
||||
def __getitem__(self, item: Union[str, Expression]) -> "MockObj":
|
||||
def __getitem__(self, item: str | Expression) -> "MockObj":
|
||||
next_op = "."
|
||||
if isinstance(item, str) and item.startswith("P"):
|
||||
item = item[1:]
|
||||
|
@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Coroutine
|
||||
from collections.abc import Callable, Coroutine
|
||||
import contextlib
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
@ -9,7 +9,7 @@ import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import threading
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
from esphome.storage_json import ignored_devices_storage_path
|
||||
|
||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Callable, Iterable
|
||||
import datetime
|
||||
import functools
|
||||
import gzip
|
||||
@ -17,7 +17,7 @@ import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, TypeVar
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import tornado
|
||||
|
@ -1,3 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
@ -5,7 +6,6 @@ import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Callable, Optional
|
||||
import urllib.parse
|
||||
|
||||
import esphome.config_validation as cv
|
||||
@ -45,12 +45,12 @@ def clone_or_update(
|
||||
*,
|
||||
url: str,
|
||||
ref: str = None,
|
||||
refresh: Optional[TimePeriodSeconds],
|
||||
refresh: TimePeriodSeconds | None,
|
||||
domain: str,
|
||||
username: str = None,
|
||||
password: str = None,
|
||||
submodules: Optional[list[str]] = None,
|
||||
) -> tuple[Path, Optional[Callable[[], None]]]:
|
||||
submodules: list[str] | None = None,
|
||||
) -> tuple[Path, Callable[[], None] | None]:
|
||||
key = f"{url}@{ref}"
|
||||
|
||||
if username is not None and password is not None:
|
||||
|
@ -7,7 +7,6 @@ from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -219,8 +218,8 @@ def sort_ip_addresses(address_list: list[str]) -> list[str]:
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
Union[str, None],
|
||||
Union[tuple[str, int], tuple[str, int, int, int]],
|
||||
str | None,
|
||||
tuple[str, int] | tuple[str, int, int, int],
|
||||
]
|
||||
] = []
|
||||
for addr in address_list:
|
||||
@ -282,7 +281,7 @@ def read_file(path):
|
||||
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.
|
||||
|
||||
Automatically creates all parent directories.
|
||||
@ -315,7 +314,7 @@ def _write_file(path: Union[Path, str], text: Union[str, bytes]):
|
||||
_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:
|
||||
_write_file(path, text)
|
||||
except OSError as err:
|
||||
@ -324,7 +323,7 @@ def write_file(path: Union[Path, str], text: str):
|
||||
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.
|
||||
|
||||
Returns true if the file was changed.
|
||||
|
@ -1,3 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
from contextlib import AbstractContextManager
|
||||
from dataclasses import dataclass
|
||||
import importlib
|
||||
@ -8,7 +9,7 @@ import logging
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from types import ModuleType
|
||||
from typing import Any, Callable, Optional
|
||||
from typing import Any
|
||||
|
||||
from esphome.const import SOURCE_FILE_EXTENSIONS
|
||||
from esphome.core import CORE
|
||||
@ -57,7 +58,7 @@ class ComponentManifest:
|
||||
return getattr(self.module, "IS_TARGET_PLATFORM", False)
|
||||
|
||||
@property
|
||||
def config_schema(self) -> Optional[Any]:
|
||||
def config_schema(self) -> Any | None:
|
||||
return getattr(self.module, "CONFIG_SCHEMA", None)
|
||||
|
||||
@property
|
||||
@ -69,7 +70,7 @@ class ComponentManifest:
|
||||
return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False)
|
||||
|
||||
@property
|
||||
def to_code(self) -> Optional[Callable[[Any], None]]:
|
||||
def to_code(self) -> Callable[[Any], None] | None:
|
||||
return getattr(self.module, "to_code", None)
|
||||
|
||||
@property
|
||||
@ -96,7 +97,7 @@ class ComponentManifest:
|
||||
return getattr(self.module, "INSTANCE_TYPE", None)
|
||||
|
||||
@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
|
||||
after the main validation. In that function checks across components can be made.
|
||||
|
||||
@ -129,7 +130,7 @@ class ComponentManifest:
|
||||
|
||||
class ComponentMetaFinder(importlib.abc.MetaPathFinder):
|
||||
def __init__(
|
||||
self, components_path: Path, allowed_components: Optional[list[str]] = None
|
||||
self, components_path: Path, allowed_components: list[str] | None = None
|
||||
) -> None:
|
||||
self._allowed_components = allowed_components
|
||||
self._finders = []
|
||||
@ -140,7 +141,7 @@ class ComponentMetaFinder(importlib.abc.MetaPathFinder):
|
||||
continue
|
||||
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."):
|
||||
return None
|
||||
parts = fullname.split(".")
|
||||
@ -167,7 +168,7 @@ def clear_component_meta_finders():
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
@ -5,7 +5,6 @@ import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Union
|
||||
|
||||
from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE
|
||||
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_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path())
|
||||
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)
|
||||
|
||||
|
||||
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]
|
||||
if verbose:
|
||||
command += ["-v"]
|
||||
|
@ -1,19 +1,18 @@
|
||||
"""This helper module tracks commonly used types in the esphome python codebase."""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from esphome.core import ID, EsphomeCore, Lambda
|
||||
|
||||
ConfigFragmentType = Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
None,
|
||||
dict[Union[str, int], "ConfigFragmentType"],
|
||||
list["ConfigFragmentType"],
|
||||
ID,
|
||||
Lambda,
|
||||
]
|
||||
ConfigFragmentType = (
|
||||
str
|
||||
| int
|
||||
| float
|
||||
| None
|
||||
| dict[str | int, "ConfigFragmentType"]
|
||||
| list["ConfigFragmentType"]
|
||||
| ID
|
||||
| Lambda
|
||||
)
|
||||
|
||||
ConfigType = dict[str, ConfigFragmentType]
|
||||
CoreType = EsphomeCore
|
||||
ConfigPathType = Union[str, int]
|
||||
ConfigPathType = str | int
|
||||
|
@ -6,7 +6,6 @@ from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
from esphome import const
|
||||
|
||||
@ -162,7 +161,7 @@ class RedirectText:
|
||||
|
||||
def run_external_command(
|
||||
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.
|
||||
|
||||
|
@ -3,7 +3,6 @@ import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Union
|
||||
|
||||
from esphome import loader
|
||||
from esphome.config import iter_component_configs, iter_components
|
||||
@ -132,7 +131,7 @@ def update_storage_json():
|
||||
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 = ""
|
||||
for key, value in sorted(data.items()):
|
||||
if isinstance(value, list):
|
||||
|
@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
import fnmatch
|
||||
import functools
|
||||
import inspect
|
||||
@ -8,7 +9,7 @@ from ipaddress import _BaseAddress
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
import yaml
|
||||
|
@ -1,9 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
from zeroconf import (
|
||||
AddressResolver,
|
||||
|
@ -20,7 +20,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Home Automation",
|
||||
]
|
||||
requires-python = ">=3.9.0"
|
||||
requires-python = ">=3.10.0"
|
||||
|
||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||
|
||||
@ -62,7 +62,7 @@ addopts = [
|
||||
]
|
||||
|
||||
[tool.pylint.MAIN]
|
||||
py-version = "3.9"
|
||||
py-version = "3.10"
|
||||
ignore = [
|
||||
"api_pb2.py",
|
||||
]
|
||||
@ -106,7 +106,7 @@ expected-line-ending-format = "LF"
|
||||
|
||||
[tool.ruff]
|
||||
required-version = ">=0.5.0"
|
||||
target-version = "py39"
|
||||
target-version = "py310"
|
||||
exclude = ['generated']
|
||||
|
||||
[tool.ruff.lint]
|
||||
|
@ -137,7 +137,7 @@ def main():
|
||||
print()
|
||||
print("Running pyupgrade...")
|
||||
print()
|
||||
PYUPGRADE_TARGET = "--py39-plus"
|
||||
PYUPGRADE_TARGET = "--py310-plus"
|
||||
for files in filesets:
|
||||
cmd = ["pyupgrade", PYUPGRADE_TARGET] + files
|
||||
log = get_err(*cmd)
|
||||
|
Loading…
x
Reference in New Issue
Block a user