[core] Enable ruff PIE (flake8-pie) lint family (#16658)

This commit is contained in:
J. Nick Koston
2026-05-26 06:46:44 -05:00
committed by GitHub
parent 8b62cfded7
commit ceb9d406e1
15 changed files with 25 additions and 33 deletions

View File

@@ -100,7 +100,7 @@ def position(min=-MAX_POSITION, max=MAX_POSITION):
if isinstance(value, str) and value.endswith("%"): if isinstance(value, str) and value.endswith("%"):
value = percent_to_position(value) value = percent_to_position(value)
if isinstance(value, str) and (value.endswith("°") or value.endswith("deg")): if isinstance(value, str) and value.endswith(("°", "deg")):
return angle_to_position( return angle_to_position(
value, value,
min=round(min * POSITION_TO_ANGLE), min=round(min * POSITION_TO_ANGLE),

View File

@@ -72,7 +72,7 @@ def _file_schema(value: ConfigType | str) -> ConfigType:
def _validate_file_shorthand(value: str) -> ConfigType: def _validate_file_shorthand(value: str) -> ConfigType:
value = cv.string_strict(value) value = cv.string_strict(value)
if value.startswith("http://") or value.startswith("https://"): if value.startswith(("http://", "https://")):
return _file_schema( return _file_schema(
{ {
CONF_TYPE: TYPE_WEB, CONF_TYPE: TYPE_WEB,

View File

@@ -401,7 +401,7 @@ def validate_file_shorthand(value):
data[CONF_WEIGHT] = weight[1:] data[CONF_WEIGHT] = weight[1:]
return font_file_schema(data) return font_file_schema(data)
if value.startswith("http://") or value.startswith("https://"): if value.startswith(("http://", "https://")):
return font_file_schema( return font_file_schema(
{ {
CONF_TYPE: TYPE_WEB, CONF_TYPE: TYPE_WEB,

View File

@@ -65,7 +65,7 @@ CONF_JSON = "json"
def validate_url(value): def validate_url(value):
value = cv.url(value) value = cv.url(value)
if value.startswith("http://") or value.startswith("https://"): if value.startswith(("http://", "https://")):
return value return value
raise cv.Invalid("URL must start with 'http://' or 'https://'") raise cv.Invalid("URL must start with 'http://' or 'https://'")

View File

@@ -408,7 +408,7 @@ def validate_file_shorthand(value):
raise cv.Invalid(f"Could not parse mdi icon name from '{value}'.") raise cv.Invalid(f"Could not parse mdi icon name from '{value}'.")
return download_gh_svg(parts[1], parts[0]) return download_gh_svg(parts[1], parts[0])
if value.startswith("http://") or value.startswith("https://"): if value.startswith(("http://", "https://")):
return download_image(value) return download_image(value)
value = cv.file_(value) value = cv.file_(value)

View File

@@ -112,7 +112,7 @@ def expand_file_to_files(config: dict):
def validate_yaml_filename(value): def validate_yaml_filename(value):
value = cv.string(value) value = cv.string(value)
if not (value.endswith(".yaml") or value.endswith(".yml")): if not value.endswith((".yaml", ".yml")):
raise cv.Invalid("Only YAML (.yaml / .yml) files are supported.") raise cv.Invalid("Only YAML (.yaml / .yml) files are supported.")
return value return value

View File

@@ -418,11 +418,11 @@ async def setup_time_core_(time_var, config):
for conf in config.get(CONF_ON_TIME, []): for conf in config.get(CONF_ON_TIME, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], time_var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], time_var)
seconds = conf.get(CONF_SECONDS, list(range(0, 61))) seconds = conf.get(CONF_SECONDS, list(range(61)))
cg.add(trigger.add_seconds(seconds)) cg.add(trigger.add_seconds(seconds))
minutes = conf.get(CONF_MINUTES, list(range(0, 60))) minutes = conf.get(CONF_MINUTES, list(range(60)))
cg.add(trigger.add_minutes(minutes)) cg.add(trigger.add_minutes(minutes))
hours = conf.get(CONF_HOURS, list(range(0, 24))) hours = conf.get(CONF_HOURS, list(range(24)))
cg.add(trigger.add_hours(hours)) cg.add(trigger.add_hours(hours))
days_of_month = conf.get(CONF_DAYS_OF_MONTH, list(range(1, 32))) days_of_month = conf.get(CONF_DAYS_OF_MONTH, list(range(1, 32)))
cg.add(trigger.add_days_of_month(days_of_month)) cg.add(trigger.add_days_of_month(days_of_month))

View File

@@ -28,10 +28,12 @@ class AnsiFore(Enum):
class AnsiStyle(Enum): class AnsiStyle(Enum):
# BOLD/BRIGHT and THIN/DIM are intentional ANSI synonyms; Enum treats the
# second name in each pair as an alias of the first.
BRIGHT = "\033[1m" BRIGHT = "\033[1m"
BOLD = "\033[1m" BOLD = "\033[1m" # noqa: PIE796
DIM = "\033[2m" DIM = "\033[2m"
THIN = "\033[2m" THIN = "\033[2m" # noqa: PIE796
NORMAL = "\033[22m" NORMAL = "\033[22m"
RESET_ALL = "\033[0m" RESET_ALL = "\033[0m"

View File

@@ -57,7 +57,7 @@ def get_port_type(port: str) -> PortType:
""" """
if port == "BOOTSEL": if port == "BOOTSEL":
return PortType.BOOTSEL return PortType.BOOTSEL
if port.startswith("/") or port.startswith("COM"): if port.startswith(("/", "COM")):
return PortType.SERIAL return PortType.SERIAL
if port == "MQTT": if port == "MQTT":
return PortType.MQTT return PortType.MQTT

View File

@@ -126,6 +126,7 @@ select = [
"LOG", # flake8-logging "LOG", # flake8-logging
"NPY", # numpy-specific rules "NPY", # numpy-specific rules
"PERF", # performance "PERF", # performance
"PIE", # flake8-pie
"PL", # pylint "PL", # pylint
"PTH", # flake8-use-pathlib "PTH", # flake8-use-pathlib
"PYI", # flake8-pyi "PYI", # flake8-pyi

View File

@@ -84,12 +84,7 @@ def indent_list(text: str, padding: str = " ") -> list[str]:
"""Indent each line of the given text with the specified padding.""" """Indent each line of the given text with the specified padding."""
lines = [] lines = []
for line in text.splitlines(): for line in text.splitlines():
if ( if line == "" or line.startswith(("#ifdef", "#if ", "#endif")):
line == ""
or line.startswith("#ifdef")
or line.startswith("#if ")
or line.startswith("#endif")
):
p = "" p = ""
else: else:
p = padding p = padding

View File

@@ -483,9 +483,7 @@ def should_run_device_builder(branch: str | None = None) -> bool:
True if the device-builder downstream tests should run, False otherwise. True if the device-builder downstream tests should run, False otherwise.
""" """
target_branch = get_target_branch() target_branch = get_target_branch()
if target_branch and ( if target_branch and (target_branch.startswith(("release", "beta"))):
target_branch.startswith("release") or target_branch.startswith("beta")
):
return False return False
for file in changed_files(branch): for file in changed_files(branch):
@@ -955,9 +953,7 @@ def detect_memory_impact_config(
# all components at once would produce nonsensical memory impact results. # all components at once would produce nonsensical memory impact results.
# Memory impact analysis is most useful for focused PRs targeting dev. # Memory impact analysis is most useful for focused PRs targeting dev.
target_branch = get_target_branch() target_branch = get_target_branch()
if target_branch and ( if target_branch and (target_branch.startswith(("release", "beta"))):
target_branch.startswith("release") or target_branch.startswith("beta")
):
print( print(
f"Memory impact: Skipping analysis for target branch {target_branch} " f"Memory impact: Skipping analysis for target branch {target_branch} "
f"(would try to build all components at once, giving nonsensical results)", f"(would try to build all components at once, giving nonsensical results)",
@@ -1311,7 +1307,7 @@ def main() -> None:
# (no isolation, all components are groupable) # (no isolation, all components are groupable)
target_branch = get_target_branch() target_branch = get_target_branch()
is_release_branch = target_branch and ( is_release_branch = target_branch and (
target_branch.startswith("release") or target_branch.startswith("beta") target_branch.startswith(("release", "beta"))
) )
if is_release_branch: if is_release_branch:

View File

@@ -103,9 +103,7 @@ def get_component_from_path(file_path: str) -> str | None:
Returns: Returns:
Component name if path is in components or tests directory, None otherwise Component name if path is in components or tests directory, None otherwise
""" """
if file_path.startswith(ESPHOME_COMPONENTS_PATH) or file_path.startswith( if file_path.startswith((ESPHOME_COMPONENTS_PATH, ESPHOME_TESTS_COMPONENTS_PATH)):
ESPHOME_TESTS_COMPONENTS_PATH
):
parts = file_path.split("/") parts = file_path.split("/")
if len(parts) >= 3 and parts[2]: if len(parts) >= 3 and parts[2]:
# Verify that parts[2] is actually a component directory, not a file # Verify that parts[2] is actually a component directory, not a file
@@ -160,7 +158,7 @@ def is_validate_only_file(test_file: Path) -> bool:
``esphome config`` only and skipped during compile. ``esphome config`` only and skipped during compile.
""" """
name = test_file.name name = test_file.name
return name.startswith("validate.") or name.startswith("validate-") return name.startswith(("validate.", "validate-"))
@dataclass(frozen=True) @dataclass(frozen=True)

View File

@@ -43,7 +43,7 @@ async def test_gpio_expander_cache(
# ensure logs are in the expected order # ensure logs are in the expected order
log_order = [ log_order = [
(digital_read_hw_pattern, 0), (digital_read_hw_pattern, 0),
[(digital_read_cache_pattern, i) for i in range(0, 8)], [(digital_read_cache_pattern, i) for i in range(8)],
(digital_read_hw_pattern, 8), (digital_read_hw_pattern, 8),
[(digital_read_cache_pattern, i) for i in range(8, 16)], [(digital_read_cache_pattern, i) for i in range(8, 16)],
(digital_read_hw_pattern, 16), (digital_read_hw_pattern, 16),
@@ -68,7 +68,7 @@ async def test_gpio_expander_cache(
# uint16_t component tests (single bank of 16 pins) # uint16_t component tests (single bank of 16 pins)
(uint16_read_hw_pattern, 0), # First pin triggers hw read (uint16_read_hw_pattern, 0), # First pin triggers hw read
[ [
(uint16_read_cache_pattern, i) for i in range(0, 16) (uint16_read_cache_pattern, i) for i in range(16)
], # All 16 pins return via cache ], # All 16 pins return via cache
# After cache reset # After cache reset
(uint16_read_hw_pattern, 5), # First read after reset triggers hw (uint16_read_hw_pattern, 5), # First read after reset triggers hw

View File

@@ -70,11 +70,11 @@ def test_numeric_offset_slash() -> None:
def test_star() -> None: def test_star() -> None:
assert _parse_cron_part("*", 0, 59, {}) == set(range(0, 60)) assert _parse_cron_part("*", 0, 59, {}) == set(range(60))
def test_question() -> None: def test_question() -> None:
assert _parse_cron_part("?", 0, 59, {}) == set(range(0, 60)) assert _parse_cron_part("?", 0, 59, {}) == set(range(60))
def test_range() -> None: def test_range() -> None: