atomic_write context to save configs first to temp file, then move

Should prevent corruption of files
This commit is contained in:
Gina Häußge 2015-08-21 19:52:31 +02:00
parent 8dfb0cadd7
commit c6d02903fa
3 changed files with 27 additions and 9 deletions

View file

@ -329,12 +329,14 @@ class PrinterProfileManager(object):
if os.path.exists(path) and not allow_overwrite:
raise SaveError("Profile %s already exists and not allowed to overwrite" % profile["id"])
from octoprint.util import atomic_write
import yaml
with open(path, "wb") as f:
try:
try:
with atomic_write(path, "wb") as f:
yaml.safe_dump(profile, f, default_flow_style=False, indent=" ", allow_unicode=True)
except Exception as e:
raise SaveError("Cannot save profile %s: %s" % (profile["id"], str(e)))
except Exception as e:
self._logger.exception("Error while trying to save profile %s" % profile["id"])
raise SaveError("Cannot save profile %s: %s" % (profile["id"], str(e)))
def _remove_from_path(self, path):
try:

View file

@ -798,11 +798,17 @@ class Settings(object):
if not self._dirty and not force:
return False
with open(self._configfile, "wb") as configFile:
yaml.safe_dump(self._config, configFile, default_flow_style=False, indent=" ", allow_unicode=True)
self._dirty = False
self.load()
return True
from octoprint.util import atomic_write
try:
with atomic_write(self._configfile, "wb", prefix="octoprint-config-", suffix=".yaml") as configFile:
yaml.safe_dump(self._config, configFile, default_flow_style=False, indent=" ", allow_unicode=True)
self._dirty = False
except:
self._logger.exception("Error while saving config.yaml!")
raise
else:
self.load()
return True
@property
def last_modified(self):

View file

@ -17,6 +17,7 @@ import shutil
import threading
from functools import wraps
import warnings
import contextlib
logger = logging.getLogger(__name__)
@ -513,6 +514,15 @@ def address_for_client(host, port):
except:
continue
@contextlib.contextmanager
def atomic_write(filename, mode="w+b", prefix="tmp", suffix=""):
temp_config = tempfile.NamedTemporaryFile(mode=mode, prefix=prefix, suffix=suffix, delete=False)
yield temp_config
temp_config.close()
shutil.move(temp_config.name, filename)
class RepeatedTimer(threading.Thread):
"""
This class represents an action that should be run repeatedly in an interval. It is similar to python's