Jak sprawić, by tworzenie plików stało się operacją atomową?

Używam Pythona do zapisu fragmentów tekstu do plików w jednej operacji:

open(file, 'w').write(text)

Jeśli skrypt zostanie przerwany tak, że zapis pliku nie zakończy się, chcę mieć Nie plik, a nie Plik częściowo kompletny. Czy można to zrobić?

Author: martineau, 2010-02-25

6 answers

Zapisanie danych do pliku tymczasowego i po pomyślnym zapisaniu danych Zmień nazwę pliku na właściwy plik docelowy np.]}

f = open(tmpFile, 'w')
f.write(text)
# make sure that all data is on disk
# see http://stackoverflow.com/questions/7433057/is-rename-without-fsync-safe
f.flush()
os.fsync(f.fileno()) 
f.close()

os.rename(tmpFile, myFile)

Według doc http://docs.python.org/library/os.html#os.rename

Jeśli się powiedzie, zmiana nazwy będzie operacją atomową (jest to Wymagania POSIX). W systemie Windows, jeśli dst już istnieje, OSError zostanie podniesiony nawet jeśli jest to plik; nie może być sposób implementacji atomic rename, gdy dst nazwy an istniejący plik

Również

Operacja może się nie udać na niektórych systemach uniksowych, jeśli src i dst są na różnych systemach plików.

Uwaga:

  • Może to nie być operacja atomowa, jeśli lokalizacje src i dest nie są na tym samym systemie plików

  • os.fsync krok może zostać pominięty, jeśli wydajność/czas reakcji jest ważniejsza niż integralność danych w przypadkach takich jak awaria zasilania, awaria systemu itp.]}

 105
Author: Anurag Uniyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-28 05:43:11

Prosty fragment implementujący atomic writing przy użyciu Pythona tempfile.

with open_atomic('test.txt', 'w') as f:
    f.write("huzza")

Lub nawet odczyt i zapis do i z tego samego pliku:

with open('test.txt', 'r') as src:
    with open_atomic('test.txt', 'w') as dst:
        for line in src:
            dst.write(line)

Używanie dwóch prostych menedżerów kontekstu

import os
import tempfile as tmp
from contextlib import contextmanager

@contextmanager
def tempfile(suffix='', dir=None):
    """ Context for temporary file.

    Will find a free temporary filename upon entering
    and will try to delete the file on leaving, even in case of an exception.

    Parameters
    ----------
    suffix : string
        optional file suffix
    dir : string
        optional directory to save temporary file in
    """

    tf = tmp.NamedTemporaryFile(delete=False, suffix=suffix, dir=dir)
    tf.file.close()
    try:
        yield tf.name
    finally:
        try:
            os.remove(tf.name)
        except OSError as e:
            if e.errno == 2:
                pass
            else:
                raise

@contextmanager
def open_atomic(filepath, *args, **kwargs):
    """ Open temporary file object that atomically moves to destination upon
    exiting.

    Allows reading and writing to and from the same filename.

    The file will not be moved to destination in case of an exception.

    Parameters
    ----------
    filepath : string
        the file path to be opened
    fsync : bool
        whether to force write the file to disk
    *args : mixed
        Any valid arguments for :code:`open`
    **kwargs : mixed
        Any valid keyword arguments for :code:`open`
    """
    fsync = kwargs.get('fsync', False)

    with tempfile(dir=os.path.dirname(os.path.abspath(filepath))) as tmppath:
        with open(tmppath, *args, **kwargs) as file:
            try:
                yield file
            finally:
                if fsync:
                    file.flush()
                    os.fsync(file.fileno())
        os.rename(tmppath, filepath)
 20
Author: Nils Werner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-26 08:56:43

Ponieważ bardzo łatwo jest namieszać w szczegółach, zalecam użycie do tego małej biblioteki. Zaletą biblioteki jest to, że zajmuje się ona wszystkimi szczegółami i jest weryfikowana i ulepszana przez społeczność.

Jedną z takich bibliotek jest python-atomicwrites by untitaker , która ma nawet odpowiednie okna wsparcie:

Z README:

from atomicwrites import atomic_write

with atomic_write('foo.txt', overwrite=True) as f:
    f.write('Hello world.')
    # "foo.txt" doesn't exist yet.

# Now it does.

Instalacja przez PIP:

pip install atomicwrites
 14
Author: vog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-04 00:18:36

Używam tego kodu do atomicznego zastąpienia / zapisu pliku:

import os
from contextlib import contextmanager

@contextmanager
def atomic_write(filepath, binary=False, fsync=False):
    """ Writeable file object that atomically updates a file (using a temporary file).

    :param filepath: the file path to be opened
    :param binary: whether to open the file in a binary mode instead of textual
    :param fsync: whether to force write the file to disk
    """

    tmppath = filepath + '~'
    while os.path.isfile(tmppath):
        tmppath += '~'
    try:
        with open(tmppath, 'wb' if binary else 'w') as file:
            yield file
            if fsync:
                file.flush()
                os.fsync(file.fileno())
        os.rename(tmppath, filepath)
    finally:
        try:
            os.remove(tmppath)
        except (IOError, OSError):
            pass

Użycie:

with atomic_write('path/to/file') as f:
    f.write("allons-y!\n")

Jest oparty na ten przepis .

 5
Author: Jakub Jirutka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-30 20:06:51

Odpowiedzi na tej stronie są dość stare, są teraz biblioteki, które robią to za Ciebie.

W szczególności safer jest biblioteką zaprojektowaną w celu zapobiegania błędom programisty przed uszkodzeniem plików, połączeń gniazd lub uogólnionych strumieni. Jest dość elastyczny i między innymi ma możliwość korzystania z pamięci lub plików tymczasowych, możesz nawet zachować pliki tymczasowe w przypadku awarii.

Ich przykład jest właśnie tym, czego chcesz:

# dangerous
with open(filename, 'w') as fp:
    json.dump(data, fp)
    # If an exception is raised, the file is empty or partly written
# safer
with safer.open(filename, 'w') as fp:
    json.dump(data, fp)
    # If an exception is raised, the file is unchanged.

Jest w PyPI, po prostu go zainstaluj używając pip install --user safer lub Pobierz najnowszą wersję na https://github.com/rec/safer

 0
Author: Eric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-17 10:40:55

Rozwiązanie atomowe dla systemu Windows do zapętlania folderu i zmiany nazw plików. Przetestowany, atomowy w celu automatyzacji, możesz zwiększyć prawdopodobieństwo, aby zminimalizować ryzyko, że nie wystąpi taka sama nazwa pliku. Losowa biblioteka dla kombinacji symboli literowych używa losowych.metoda wyboru, dla cyfry str(random.przypadkowe.zasięg (50,999999999,2). Możesz zmieniać zakres cyfr, jak chcesz.

import os import random

path = "C:\\Users\\ANTRAS\\Desktop\\NUOTRAUKA\\"

def renamefiles():
    files = os.listdir(path)
    i = 1
    for file in files:
        os.rename(os.path.join(path, file), os.path.join(path, 
                  random.choice('ABCDEFGHIJKL') + str(i) + str(random.randrange(31,9999999,2)) + '.jpg'))
        i = i+1

for x in range(30):
    renamefiles()
 -1
Author: Mindaugas Vaitkus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-04 19:46:30