All of the functions in this module work atomically—either they
succeed, or they raise an OSError
(or pass through some other
exception) and leave the original file (if any) untouched.
The name, of course, is a racist joke about overweight Irish people. Or maybe it's just sure for "file, atomic".
filename
For write*
functions, the filename is the name of the file to
write. Any existing file will be overwritten on success. If there
is no existing file, there is no problem.
For transform*
functions, the filename is the name of both the
input file and the output file; any existing data will be
replaced with the transformed data. If there is no existing file,
it's a FileNotFoundError
(or OSError
with ENOENT
, for older
versions of Python), as usual for an input file.
For append*
functions, the filename is again the name of both
the input file and the output file; any existing data will still
be present, and the data data will follow it. If there is no
existing file, there is no problem; a new file will be created
with just the new data.
binary
Most functions take a binary=False
flag. This determines whether
the input and temporary files are read and written in binary mode
or text mode. This is particularly important in Python 3.x, where
text mode means Unicode str
and binary means bytes
.
Some functions specify binary=None
instead. In that case, a
None
value means fatomic
will guess the file mode based on
the type of the data: text for str
, binary for bytes
or
bytearray
, text for anything else. (Note that bytes
and str
are the same type in 2.x.)
func
The transform*
functions take a function that will be run on
the whole file, or each line, or each chunk. Whether that means
str
or bytes
depends on the binary
flag. (Of course in 2.x
those are the same type.)
with fatomic.open('foo', 'a') as f:
f.write('Hello, ')
with fatomic.open('foo', 'a') as f:
f.write('world\n')
fatomic.transform('foo', lambda line: line.replace('Hello', 'Hi'))
The first time you run that, you should get a new file named "foo" with the single line "Hi, world". Running it repeatedly should keep appending lines to it. Hitting ^C or pulling the power cord or whatever may leave you with a trailing "Hello, " or a trailing "Hello, world", but can never leave you with any lost lines before that.
replace(src, dst)
Like os.replace
in Python 3.3+, but works in earlier
versions. (Of course it doesn't support src_dir_fd
and dst_dir_fd
.)
open(filename, mode, *args, **kwargs)
Returns a file-like object that writes to a temporary file, then overwrites filename atomically when closed.
filename
is as in the builtin open
.
mode
is as in the builtin open
, but it may not be r
, r+
,
or w+
, because that would make no sense. In the case of a
, the
file starts with a copy of the original file.
All other arguments are identical to the builtin open
.
In addition to the usual file-like-object methods, there will be
a discard
method that tells it not to overwrite the original
file at exit.
write(filename, lines, binary=False)
Write all of lines
, which can be an iterator, to filename
.
Just like the writelines
method on a file object, this does not
add newlines.
writeall(filename, contents, binary=None)
Write all of contents
to filename
.
writechunks(filename, chunks, binary=False)
Write all of chunks
, which can be an iterator, to filename
.
Note that this is identical to writelines
, and is only provided
for API consistency.
transform(filename, func, binary=False)
Write func(line)
for each line
in the file (without reading
the entire file into memory all at once).
transformall(filename, func, binary=False)
Write func(contents)
for the entire contents of the file (which
obviously are read completely into memory).
transformchunks(filename, func, chunksize=None, binary=False)
Write func(chunk)
for each chunk
of up to chunksize
length
(without reading the entire file into memory all at once). The
transformed chunks do not have to be the same length as the
original chunks.
If chunksize
is None
, a reasonable default will be used.
append(filename, lines, binary=False)
Append all of lines
, which can be an iterator, to filename
.
Just like the writelines
method on a file object, this does not
add newlines.
appendall(filename, contents, binary=None)
Append all of contents
to filename
.
appendchunks(filename, chunks, binary=False)
Append all of chunks
, which can be an iterator, to filename
.
Note that this is identical to appendlines
, and is only
provided for API consistency.