AUTHORS:
Write to a given file using a temporary file and then rename it to the target file. This renaming should be atomic on modern operating systems. Therefore, this class can be used to avoid race conditions when a file might be read while it is being written. It also avoids having partially written files due to exceptions or crashes.
This is to be used in a with statement, where a temporary file is created when entering the with and is moved in place of the target file when exiting the with (if no exceptions occured).
INPUT:
EXAMPLES:
sage: from sage.misc.temporary_file import atomic_write
sage: target_file = tmp_filename()
sage: open(target_file, "w").write("Old contents")
sage: with atomic_write(target_file) as f:
....: f.write("New contents")
....: f.flush()
....: open(target_file, "r").read()
'Old contents'
sage: open(target_file, "r").read()
'New contents'
The name of the temporary file can be accessed using f.name. It is not a problem to close and re-open the temporary file:
sage: from sage.misc.temporary_file import atomic_write
sage: target_file = tmp_filename()
sage: open(target_file, "w").write("Old contents")
sage: with atomic_write(target_file) as f:
....: f.close()
....: open(f.name, "w").write("Newer contents")
sage: open(target_file, "r").read()
'Newer contents'
If an exception occurs while writing the file, the target file is not touched:
sage: with atomic_write(target_file) as f:
....: f.write("Newest contents")
....: raise RuntimeError
Traceback (most recent call last):
...
RuntimeError
sage: open(target_file, "r").read()
'Newer contents'
Some examples of using the append option. Note that the file is never opened in “append” mode, it is possible to overwrite existing data:
sage: target_file = tmp_filename()
sage: with atomic_write(target_file, append=True) as f:
....: f.write("Hello")
sage: with atomic_write(target_file, append=True) as f:
....: f.write(" World")
sage: open(target_file, "r").read()
'Hello World'
sage: with atomic_write(target_file, append=True) as f:
....: f.seek(0)
....: f.write("HELLO")
sage: open(target_file, "r").read()
'HELLO World'
If the target file is a symbolic link, the link is kept and the target of the link is written to:
sage: link_to_target = os.path.join(tmp_dir(), "templink")
sage: os.symlink(target_file, link_to_target)
sage: with atomic_write(link_to_target) as f:
....: f.write("Newest contents")
sage: open(target_file, "r").read()
'Newest contents'
We check the permission bits of the new file. Note that the old permissions do not matter:
sage: os.chmod(target_file, 0o600)
sage: _ = os.umask(0o022)
sage: with atomic_write(target_file) as f:
....: pass
sage: oct(os.stat(target_file).st_mode & 0o777)
'644'
sage: _ = os.umask(0o077)
sage: with atomic_write(target_file, mode=0o777) as f:
....: pass
sage: oct(os.stat(target_file).st_mode & 0o777)
'700'
Test writing twice to the same target file. The outermost with “wins”:
sage: open(target_file, "w").write(">>> ")
sage: with atomic_write(target_file, append=True) as f, ....: atomic_write(target_file, append=True) as g:
....: f.write("AAA"); f.close()
....: g.write("BBB"); g.close()
sage: open(target_file, "r").read()
'>>> AAA'
Remove the directory SAGE_TMP.
TESTS:
This is automatically run when Sage exits, test this by running a separate session of Sage:
sage: from sage.tests.cmdline import test_executable
sage: child_SAGE_TMP, err, ret = test_executable(["sage", "-c", "print SAGE_TMP"])
sage: err, ret
('', 0)
sage: os.path.exists(child_SAGE_TMP) # indirect doctest
False
The parent directory should exist:
sage: parent_SAGE_TMP = os.path.normpath(child_SAGE_TMP + '/..')
sage: os.path.isdir(parent_SAGE_TMP)
True
When run from the Sage notebook, return the next available canonical filename for a plot/graphics file in the current working directory. Otherwise, return a temporary file inside SAGE_TMP.
INPUT:
OUTPUT:
The path of the temporary file created. In the notebook, this is a filename without path in the current directory. Otherwise, this an absolute path.
EXAMPLES:
sage: from sage.misc.temporary_file import graphics_filename
sage: print graphics_filename() # random, typical filename for sagenb
sage0.png
TESTS:
When doctesting, this returns instead a random temporary file. We check that it’s a file inside SAGE_TMP and that the extension is correct:
sage: fn = graphics_filename(ext=".jpeg")
sage: fn.startswith(str(SAGE_TMP))
True
sage: fn.endswith('.jpeg')
True
Historically, it was also possible to omit the dot. This has been changed in trac ticket #16640 but it will still work for now:
sage: fn = graphics_filename("jpeg")
doctest:...: DeprecationWarning: extension must now include the dot
See http://trac.sagemath.org/16640 for details.
sage: fn.endswith('.jpeg')
True
Create and return a temporary directory in $HOME/.sage/temp/hostname/pid/
The temporary directory is deleted automatically when Sage exits.
INPUT:
OUTPUT:
The absolute path of the temporary directory created, with a trailing slash (or whatever the path separator is on your OS).
EXAMPLES:
sage: d = tmp_dir('dir_testing_', '.extension')
sage: d # random output
'/home/username/.sage/temp/hostname/7961/dir_testing_XgRu4p.extension/'
sage: os.chdir(d)
sage: _ = open('file_inside_d', 'w')
Temporary directories are unaccessible by other users:
sage: os.stat(d).st_mode & 0o077
0
Create and return a temporary file in $HOME/.sage/temp/hostname/pid/
The temporary file is deleted automatically when Sage exits.
Warning
If you need a particular file extension always use tmp_filename(ext=".foo"), this will ensure that the file does not yet exist. If you were to use tmp_filename()+".foo", then you might overwrite an existing file!
INPUT:
OUTPUT:
The absolute path of the temporary file created.
EXAMPLES:
sage: fn = tmp_filename('just_for_testing_', '.extension')
sage: fn # random
'/home/username/.sage/temp/hostname/8044/just_for_testing_tVVHsn.extension'
sage: _ = open(fn, 'w')
Temporary files are unaccessible by other users:
sage: os.stat(fn).st_mode & 0o077
0