mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 00:31:58 +00:00
tweaks
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
@@ -5,6 +6,7 @@ from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
from types import TracebackType
|
||||
|
||||
from esphome import loader
|
||||
from esphome.config import iter_component_configs, iter_components
|
||||
@@ -303,18 +305,21 @@ def clean_cmake_cache():
|
||||
pioenvs_cmake_path.unlink()
|
||||
|
||||
|
||||
def _rmtree_error_handler(func, path, exc_info):
|
||||
def _rmtree_error_handler(
|
||||
func: Callable[[str], object],
|
||||
path: str,
|
||||
exc_info: tuple[type[BaseException], BaseException, TracebackType | None],
|
||||
) -> None:
|
||||
"""Error handler for shutil.rmtree to handle read-only files on Windows.
|
||||
|
||||
On Windows, git pack files and other files may be marked read-only,
|
||||
causing shutil.rmtree to fail with "Access is denied". This handler
|
||||
removes the read-only flag and retries the deletion.
|
||||
"""
|
||||
if not os.access(path, os.W_OK):
|
||||
os.chmod(path, stat.S_IWUSR | stat.S_IRUSR)
|
||||
func(path)
|
||||
else:
|
||||
if os.access(path, os.W_OK):
|
||||
raise exc_info[1].with_traceback(exc_info[2])
|
||||
os.chmod(path, stat.S_IWUSR | stat.S_IRUSR)
|
||||
func(path)
|
||||
|
||||
|
||||
def clean_build(clear_pio_cache: bool = True):
|
||||
|
||||
@@ -15,6 +15,7 @@ from esphome.writer import (
|
||||
CPP_INCLUDE_BEGIN,
|
||||
CPP_INCLUDE_END,
|
||||
GITIGNORE_CONTENT,
|
||||
_rmtree_error_handler,
|
||||
clean_build,
|
||||
clean_cmake_cache,
|
||||
storage_should_clean,
|
||||
@@ -1137,3 +1138,20 @@ def test_clean_all_handles_readonly_files(
|
||||
# Verify directory was removed despite read-only files
|
||||
assert not subdir.exists()
|
||||
assert build_dir.exists() # .esphome dir itself is preserved
|
||||
|
||||
|
||||
def test_rmtree_error_handler_reraises_for_writable_files() -> None:
|
||||
"""Test _rmtree_error_handler re-raises exception for writable files."""
|
||||
# Create a mock exception
|
||||
original_error = PermissionError("Some other permission error")
|
||||
exc_info = (type(original_error), original_error, original_error.__traceback__)
|
||||
|
||||
# Patch os.access to return True (file is writable)
|
||||
with (
|
||||
patch("esphome.writer.os.access", return_value=True),
|
||||
pytest.raises(PermissionError) as exc_info_caught,
|
||||
):
|
||||
_rmtree_error_handler(lambda p: None, "/some/path", exc_info)
|
||||
|
||||
# Verify the original exception was re-raised
|
||||
assert exc_info_caught.value is original_error
|
||||
|
||||
Reference in New Issue
Block a user