Files
Gymnasium/gym/utils/atomic_write.py

61 lines
1.9 KiB
Python
Raw Normal View History

2016-05-26 20:49:58 -07:00
# Based on http://stackoverflow.com/questions/2333872/atomic-writing-to-file-with-python
import os
from contextlib import contextmanager
2016-05-26 20:49:58 -07:00
# We would ideally atomically replace any existing file with the new
# version. However, on Windows there's no Python-only solution prior
# to Python 3.3. (This library includes a C extension to do so:
# https://pypi.python.org/pypi/pyosreplace/0.1.)
#
# Correspondingly, we make a best effort, but on Python < 3.3 use a
# replace method which could result in the file temporarily
# disappearing.
import sys
2021-07-29 02:26:34 +02:00
2016-05-26 20:49:58 -07:00
if sys.version_info >= (3, 3):
# Python 3.3 and up have a native `replace` method
from os import replace
elif sys.platform.startswith("win"):
2021-07-29 02:26:34 +02:00
2016-05-26 20:49:58 -07:00
def replace(src, dst):
# TODO: on Windows, this will raise if the file is in use,
# which is possible. We'll need to make this more robust over
# time.
try:
os.remove(dst)
except OSError:
pass
2016-05-26 20:49:58 -07:00
os.rename(src, dst)
2021-07-29 02:26:34 +02:00
2016-05-26 20:49:58 -07:00
else:
# POSIX rename() is always atomic
from os import rename as replace
2021-07-29 02:26:34 +02:00
@contextmanager
def atomic_write(filepath, binary=False, fsync=False):
2021-07-29 02:26:34 +02:00
"""Writeable file object that atomically updates a file (using a temporary file). In some cases (namely Python < 3.3 on Windows), this could result in an existing file being temporarily unlinked.
: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
"""
2021-07-29 02:26:34 +02:00
tmppath = filepath + "~"
while os.path.isfile(tmppath):
2021-07-29 02:26:34 +02:00
tmppath += "~"
try:
2021-07-29 02:26:34 +02:00
with open(tmppath, "wb" if binary else "w") as file:
yield file
if fsync:
file.flush()
os.fsync(file.fileno())
2016-05-26 20:49:58 -07:00
replace(tmppath, filepath)
finally:
try:
os.remove(tmppath)
except (IOError, OSError):
pass