mirror of
https://github.com/esphome/esphome.git
synced 2025-09-10 15:22:24 +01:00
dashboard: Ensure disk I/O happens in the executor (#5789)
* Ensure I/O executor * safe file writer * fixes * more io * more io
This commit is contained in:
@@ -38,6 +38,7 @@ from esphome.yaml_util import FastestAvailableSafeLoader
|
||||
|
||||
from .core import DASHBOARD
|
||||
from .entries import EntryState, entry_state_to_bool
|
||||
from .util.file import write_file
|
||||
from .util.subprocess import async_run_system_command
|
||||
from .util.text import friendly_name_slugify
|
||||
|
||||
@@ -524,9 +525,19 @@ class DownloadListRequestHandler(BaseHandler):
|
||||
|
||||
|
||||
class DownloadBinaryRequestHandler(BaseHandler):
|
||||
def _load_file(self, path: str, compressed: bool) -> bytes:
|
||||
"""Load a file from disk and compress it if requested."""
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
if compressed:
|
||||
return gzip.compress(data, 9)
|
||||
return data
|
||||
|
||||
@authenticated
|
||||
@bind_config
|
||||
async def get(self, configuration=None):
|
||||
async def get(self, configuration: str | None = None):
|
||||
"""Download a binary file."""
|
||||
loop = asyncio.get_running_loop()
|
||||
compressed = self.get_argument("compressed", "0") == "1"
|
||||
|
||||
storage_path = ext_storage_path(configuration)
|
||||
@@ -583,11 +594,8 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
if compressed:
|
||||
data = gzip.compress(data, 9)
|
||||
self.write(data)
|
||||
data = await loop.run_in_executor(None, self._load_file, path, compressed)
|
||||
self.write(data)
|
||||
|
||||
self.finish()
|
||||
|
||||
@@ -746,19 +754,35 @@ class InfoRequestHandler(BaseHandler):
|
||||
class EditRequestHandler(BaseHandler):
|
||||
@authenticated
|
||||
@bind_config
|
||||
def get(self, configuration=None):
|
||||
async def get(self, configuration: str | None = None):
|
||||
"""Get the content of a file."""
|
||||
loop = asyncio.get_running_loop()
|
||||
filename = settings.rel_path(configuration)
|
||||
content = ""
|
||||
if os.path.isfile(filename):
|
||||
with open(file=filename, encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
content = await loop.run_in_executor(None, self._read_file, filename)
|
||||
self.write(content)
|
||||
|
||||
def _read_file(self, filename: str) -> bytes:
|
||||
"""Read a file and return the content as bytes."""
|
||||
with open(file=filename, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
|
||||
def _write_file(self, filename: str, content: bytes) -> None:
|
||||
"""Write a file with the given content."""
|
||||
write_file(filename, content)
|
||||
|
||||
@authenticated
|
||||
@bind_config
|
||||
def post(self, configuration=None):
|
||||
with open(file=settings.rel_path(configuration), mode="wb") as f:
|
||||
f.write(self.request.body)
|
||||
async def post(self, configuration: str | None = None):
|
||||
"""Write the content of a file."""
|
||||
loop = asyncio.get_running_loop()
|
||||
config_file = settings.rel_path(configuration)
|
||||
await loop.run_in_executor(
|
||||
None, self._write_file, config_file, self.request.body
|
||||
)
|
||||
# Ensure the StorageJSON is updated as well
|
||||
await async_run_system_command(
|
||||
[*DASHBOARD_COMMAND, "compile", "--only-generate", config_file]
|
||||
)
|
||||
self.set_status(200)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user