1
mirror of https://github.com/home-assistant/core synced 2024-10-01 05:30:36 +02:00
ha-core/homeassistant/util/file.py

83 lines
2.3 KiB
Python
Raw Normal View History

2021-11-11 07:19:56 +01:00
"""File utility functions."""
from __future__ import annotations
import logging
import os
import tempfile
from atomicwrites import AtomicWriter
2021-11-11 07:19:56 +01:00
from homeassistant.exceptions import HomeAssistantError
_LOGGER = logging.getLogger(__name__)
class WriteError(HomeAssistantError):
"""Error writing the data."""
def write_utf8_file_atomic(
filename: str,
utf8_data: str,
private: bool = False,
) -> None:
"""Write a file and rename it into place using atomicwrites.
Writes all or nothing.
This function uses fsync under the hood. It should
only be used to write mission critical files as
fsync can block for a few seconds or longer is the
disk is busy.
Using this function frequently will significantly
negatively impact performance.
"""
try:
with AtomicWriter(filename, overwrite=True).open() as fdesc:
if not private:
os.fchmod(fdesc.fileno(), 0o644)
fdesc.write(utf8_data)
except OSError as error:
_LOGGER.exception("Saving file failed: %s", filename)
raise WriteError(error) from error
2021-11-11 07:19:56 +01:00
def write_utf8_file(
filename: str,
utf8_data: str,
private: bool = False,
) -> None:
"""Write a file and rename it into place.
Writes all or nothing.
"""
tmp_filename = ""
try:
# Modern versions of Python tempfile create this file with mode 0o600
with tempfile.NamedTemporaryFile(
mode="w", encoding="utf-8", dir=os.path.dirname(filename), delete=False
2021-11-11 07:19:56 +01:00
) as fdesc:
fdesc.write(utf8_data)
tmp_filename = fdesc.name
if not private:
os.fchmod(fdesc.fileno(), 0o644)
2021-11-11 07:19:56 +01:00
os.replace(tmp_filename, filename)
except OSError as error:
_LOGGER.exception("Saving file failed: %s", filename)
raise WriteError(error) from error
finally:
if os.path.exists(tmp_filename):
try:
os.remove(tmp_filename)
except OSError as err:
# If we are cleaning up then something else went wrong, so
# we should suppress likely follow-on errors in the cleanup
_LOGGER.error(
"File replacement cleanup failed for %s while saving %s: %s",
tmp_filename,
filename,
err,
)