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: if os.path.exists(path) and not allow_overwrite:
raise SaveError("Profile %s already exists and not allowed to overwrite" % profile["id"]) raise SaveError("Profile %s already exists and not allowed to overwrite" % profile["id"])
from octoprint.util import atomic_write
import yaml 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) yaml.safe_dump(profile, f, default_flow_style=False, indent=" ", allow_unicode=True)
except Exception as e: except Exception as e:
raise SaveError("Cannot save profile %s: %s" % (profile["id"], str(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): def _remove_from_path(self, path):
try: try:

View file

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

View file

@ -17,6 +17,7 @@ import shutil
import threading import threading
from functools import wraps from functools import wraps
import warnings import warnings
import contextlib
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -513,6 +514,15 @@ def address_for_client(host, port):
except: except:
continue 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): class RepeatedTimer(threading.Thread):
""" """
This class represents an action that should be run repeatedly in an interval. It is similar to python's This class represents an action that should be run repeatedly in an interval. It is similar to python's