Hướng dẫn python atomic operations - hoạt động nguyên tử python

Tôi đang sử dụng Python để viết các đoạn văn bản vào các tệp trong một thao tác duy nhất:

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

Nếu tập lệnh bị gián đoạn để ghi tệp không hoàn thành, tôi muốn không có tệp thay vì một tệp hoàn thành một phần. Điều này có thể được thực hiện?

Hướng dẫn python atomic operations - hoạt động nguyên tử python

Martineau

Huy hiệu vàng 115K2525 gold badges160 silver badges283 bronze badges

hỏi ngày 25 tháng 2 năm 2010 lúc 12:21Feb 25, 2010 at 12:21

1

Viết dữ liệu vào một tệp tạm thời và khi dữ liệu đã được viết thành công, hãy đổi tên tệp thành tệp đích chính xác, ví dụ

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)

Theo Doc http://docs.python.org/l Library/os.html#os.rename

Nếu thành công, việc đổi tên sẽ là một hoạt động nguyên tử (đây là một yêu cầu POSIX). Trên Windows, nếu DST đã tồn tại, Oserror sẽ được nêu ra ngay cả khi đó là một tập tin; Có thể không có cách nào để thực hiện đổi tên nguyên tử khi DST đặt tên cho một tệp hiện có

cũng

Hoạt động có thể thất bại trên một số hương vị Unix nếu SRC và DST nằm trên các hệ thống tập tin khác nhau.

Ghi chú:

  • Nó có thể không hoạt động nguyên tử nếu các vị trí SRC và số phận không nằm trên cùng một hệ thống tập tin

  • 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)
    
    4 Bước có thể bị bỏ qua nếu hiệu suất/khả năng đáp ứng quan trọng hơn tính toàn vẹn của dữ liệu trong các trường hợp như mất điện, sự cố hệ thống, v.v.

Đã trả lời ngày 25 tháng 2 năm 2010 lúc 12:39Feb 25, 2010 at 12:39

Anurag uniyalanurag uniyalAnurag Uniyal

83K39 Huy hiệu vàng169 Huy hiệu bạc216 Huy hiệu Đồng39 gold badges169 silver badges216 bronze badges

15

Một đoạn trích đơn giản thực hiện viết nguyên tử bằng cách sử dụng Python

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)
5.

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

hoặc thậm chí đọc và ghi vào và từ cùng một tệp:

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

Sử dụng hai người quản lý bối cảnh đơn giản

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.pop('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)

Đã trả lời ngày 7 tháng 4 năm 2015 lúc 12:23Apr 7, 2015 at 12:23

Nils Wernernils WernerNils Werner

32,9K7 Huy hiệu vàng72 Huy hiệu bạc94 Huy hiệu đồng7 gold badges72 silver badges94 bronze badges

8

Vì rất dễ dàng để làm hỏng các chi tiết, tôi khuyên bạn nên sử dụng một thư viện nhỏ cho điều đó. Ưu điểm của một thư viện là nó chăm sóc tất cả các chi tiết lớn này, và đang được một cộng đồng xem xét và cải thiện.

Một thư viện như vậy là

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)
6 bởi Untitaker thậm chí có hỗ trợ Windows thích hợp:

  • https://github.com/untitaker/python-atomicwrites
  • https://pypi.org/project/atomicwrites/

Từ 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.

Cài đặt qua PIP:

pip install atomicwrites

Đã trả lời ngày 28 tháng 6 năm 2016 lúc 11:43Jun 28, 2016 at 11:43

vogvogvog

21.6K11 Huy hiệu vàng57 Huy hiệu bạc73 Huy hiệu đồng11 gold badges57 silver badges73 bronze badges

3

Tôi đã sử dụng mã này để thay thế/ghi một tệp nguyên tử:

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

Usage:

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

Nó dựa trên công thức này.

Đã trả lời ngày 30 tháng 8 năm 2014 lúc 19:59Aug 30, 2014 at 19:59

Jakub Jirutkajakub JirutkaJakub Jirutka

9.6704 Huy hiệu vàng40 Huy hiệu bạc34 Huy hiệu đồng4 gold badges40 silver badges34 bronze badges

2

Chỉ cần liên kết tệp sau khi bạn hoàn thành:

with tempfile.NamedTemporaryFile(mode="w") as f:
    f.write(...)
    os.link(f.name, final_filename)

Nếu bạn muốn có được ưa thích:

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)
0

Đã trả lời ngày 9 tháng 1 lúc 17:40Jan 9 at 17:40

Blaisblaisblais

6407 Huy hiệu bạc9 Huy hiệu Đồng7 silver badges9 bronze badges

Câu trả lời trên trang này khá cũ, hiện có những thư viện làm điều này cho bạn.

Cụ thể

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)
7 là một thư viện được thiết kế để giúp ngăn chặn lỗi của lập trình viên khỏi các tệp bị hỏng, kết nối ổ cắm hoặc các luồng tổng quát. Nó khá linh hoạt và trong số những thứ khác, nó có tùy chọn sử dụng bộ nhớ hoặc tệp tạm thời, bạn thậm chí có thể giữ các tệp tạm thời trong trường hợp lỗi.

Ví dụ của họ chỉ là những gì bạn muốn:

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)
1
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)
2

Đó là trong pypi, chỉ cần cài đặt nó bằng

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)
8 hoặc nhận mới nhất tại https://github.com/rec/safer

Đã trả lời ngày 17 tháng 8 năm 2020 lúc 10:26Aug 17, 2020 at 10:26

EricericEric

93310 Huy hiệu bạc21 Huy hiệu đồng10 silver badges21 bronze badges

Giải pháp nguyên tử cho thư mục Windows để lặp và đổi tên tệp. Được thử nghiệm, nguyên tử để tự động hóa, bạn có thể tăng xác suất để giảm thiểu rủi ro không có sự kiện có cùng tên tệp. Bạn thư viện ngẫu nhiên cho các biểu tượng chữ cái kết hợp Sử dụng phương thức ngẫu nhiên.choice, cho Digit str (Random.random.range (50.99999999,2). Bạn có thể thay đổi phạm vi chữ số như bạn muốn.

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)
3