Ctrl K
ComunidadGuías…

atomic_io.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import os
import stat
import tempfile


def atomic_write_text(path, content, encoding='utf-8', default_mode=0o644):
    directory = os.path.dirname(path) or '.'
    os.makedirs(directory, exist_ok=True)
    mode = (
        stat.S_IMODE(os.stat(path).st_mode)
        if os.path.exists(path)
        else default_mode
    )
    temporary_path = None

    try:
        with tempfile.NamedTemporaryFile(
            'w',
            dir=directory,
            encoding=encoding,
            delete=False,
        ) as temporary:
            temporary_path = temporary.name
            temporary.write(content)
            temporary.flush()
            os.fsync(temporary.fileno())

        os.chmod(temporary_path, mode)
        os.replace(temporary_path, path)
        temporary_path = None

        directory_fd = os.open(directory, os.O_RDONLY)
        try:
            os.fsync(directory_fd)
        finally:
            os.close(directory_fd)
    finally:
        if temporary_path and os.path.exists(temporary_path):
            os.unlink(temporary_path)


def atomic_write_many_text(files, encoding='utf-8'):
    originals = {}
    written = []
    for path in files:
        if os.path.exists(path):
            with open(path, 'r', encoding=encoding) as existing:
                originals[path] = (
                    existing.read(),
                    stat.S_IMODE(os.stat(path).st_mode),
                )
        else:
            originals[path] = None

    try:
        for path, content in files.items():
            atomic_write_text(path, content, encoding=encoding)
            written.append(path)
    except Exception:
        rollback_errors = []
        for path in reversed(written):
            try:
                original = originals[path]
                if original is None:
                    if os.path.exists(path):
                        os.unlink(path)
                else:
                    original_content, original_mode = original
                    atomic_write_text(
                        path,
                        original_content,
                        encoding=encoding,
                        default_mode=original_mode,
                    )
                    os.chmod(path, original_mode)
            except Exception as rollback_error:
                rollback_errors.append('{}: {}'.format(path, rollback_error))
        if rollback_errors:
            raise RuntimeError(
                'atomic write failed and rollback was incomplete: {}'.format(
                    '; '.join(rollback_errors)
                )
            )
        raise