Use QSaveFile for saving files. Fixes #234.
This commit is contained in:
parent
d611a37d7d
commit
bf24578dfd
@ -38,7 +38,7 @@ from PyQt5.QtWidgets import QMessageBox
|
||||
from qutebrowser.config import (configdata, iniparsers, configtypes,
|
||||
textwrapper, keyconfparser)
|
||||
from qutebrowser.commands import cmdexc, cmdutils
|
||||
from qutebrowser.utils import message, objreg, utils, standarddir, log
|
||||
from qutebrowser.utils import message, objreg, utils, standarddir, log, qtutils
|
||||
from qutebrowser.utils.usertypes import Completion
|
||||
|
||||
|
||||
@ -579,7 +579,7 @@ class ConfigManager(QObject):
|
||||
return
|
||||
configfile = os.path.join(self._configdir, self._fname)
|
||||
log.destroy.debug("Saving config to {}".format(configfile))
|
||||
with open(configfile, 'w', encoding='utf-8') as f:
|
||||
with qtutils.savefile_open(configfile) as f:
|
||||
f.write(str(self))
|
||||
|
||||
def dump_userconfig(self):
|
||||
|
@ -23,7 +23,7 @@ import os
|
||||
import os.path
|
||||
import configparser
|
||||
|
||||
from qutebrowser.utils import log, utils
|
||||
from qutebrowser.utils import log, utils, qtutils
|
||||
|
||||
|
||||
class ReadConfigParser(configparser.ConfigParser):
|
||||
@ -67,5 +67,5 @@ class ReadWriteConfigParser(ReadConfigParser):
|
||||
if not os.path.exists(self._configdir):
|
||||
os.makedirs(self._configdir, 0o755)
|
||||
log.destroy.debug("Saving config to {}".format(self._configfile))
|
||||
with open(self._configfile, 'w', encoding='utf-8') as f:
|
||||
with qtutils.savefile_open(self._configfile) as f:
|
||||
self.write(f)
|
||||
|
@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
from qutebrowser.config import configdata, textwrapper
|
||||
from qutebrowser.commands import cmdutils, cmdexc
|
||||
from qutebrowser.utils import log, utils
|
||||
from qutebrowser.utils import log, utils, qtutils
|
||||
|
||||
|
||||
class KeyConfigError(Exception):
|
||||
@ -126,8 +126,9 @@ class KeyConfigParser(QObject):
|
||||
if self._configfile is None:
|
||||
return
|
||||
log.destroy.debug("Saving key config to {}".format(self._configfile))
|
||||
with open(self._configfile, 'w', encoding='utf-8') as f:
|
||||
f.write(str(self))
|
||||
with qtutils.savefile_open(self._configfile, encoding='utf-8') as f:
|
||||
data = str(self)
|
||||
f.write(data)
|
||||
|
||||
@cmdutils.register(instance='key-config')
|
||||
def bind(self, key, *command, mode=None):
|
||||
|
@ -25,7 +25,7 @@ import collections
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
|
||||
from qutebrowser.utils import log, utils, objreg
|
||||
from qutebrowser.utils import log, utils, objreg, qtutils
|
||||
from qutebrowser.config import config
|
||||
|
||||
|
||||
@ -104,12 +104,8 @@ class LineConfigParser(collections.UserList):
|
||||
if not os.path.exists(self._configdir):
|
||||
os.makedirs(self._configdir, 0o755)
|
||||
log.destroy.debug("Saving config to {}".format(self._configfile))
|
||||
if self._binary:
|
||||
with open(self._configfile, 'wb') as f:
|
||||
self.write(f, limit)
|
||||
else:
|
||||
with open(self._configfile, 'w', encoding='utf-8') as f:
|
||||
self.write(f, limit)
|
||||
with qtutils.savefile_open(self._configfile, self._binary) as f:
|
||||
self.write(f, limit)
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def cleanup_file(self, section, option):
|
||||
|
@ -27,14 +27,16 @@ Module attributes:
|
||||
"""
|
||||
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import operator
|
||||
import distutils.version # pylint: disable=no-name-in-module,import-error
|
||||
# https://bitbucket.org/logilab/pylint/issue/73/
|
||||
import contextlib
|
||||
|
||||
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
|
||||
QIODevice)
|
||||
QIODevice, QSaveFile)
|
||||
|
||||
|
||||
MAXVALS = {
|
||||
@ -155,6 +157,132 @@ def deserialize(data, obj):
|
||||
_check_qdatastream(stream)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def savefile_open(filename, binary=False, encoding='utf-8'):
|
||||
"""Context manager to easily use a QSaveFile."""
|
||||
f = QSaveFile(filename)
|
||||
try:
|
||||
if binary:
|
||||
ok = f.open(QIODevice.WriteOnly)
|
||||
new_f = PyQIODevice(f)
|
||||
else:
|
||||
ok = f.open(QIODevice.WriteOnly | QIODevice.Text)
|
||||
new_f = io.TextIOWrapper(PyQIODevice(f), encoding=encoding)
|
||||
if not ok: # pylint: disable=used-before-assignment
|
||||
raise IOError(f.errorString())
|
||||
yield new_f
|
||||
except:
|
||||
f.cancelWriting()
|
||||
raise
|
||||
finally:
|
||||
new_f.flush()
|
||||
ok = f.commit()
|
||||
if not ok:
|
||||
raise IOError(f.errorString())
|
||||
|
||||
|
||||
class PyQIODevice(io.BufferedIOBase):
|
||||
|
||||
"""Wrapper for a QIODevice which provides a python interface.
|
||||
|
||||
Attributes:
|
||||
_dev: The underlying QIODevice.
|
||||
"""
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
def __init__(self, dev):
|
||||
self._dev = dev
|
||||
|
||||
def __len__(self):
|
||||
return self._dev.size()
|
||||
|
||||
def _check_open(self):
|
||||
"""Check if the device is open, raise IOError if not."""
|
||||
if not self._dev.isOpen():
|
||||
raise IOError("IO operation on closed device!")
|
||||
|
||||
def _check_random(self):
|
||||
"""Check if the device supports random access, raise IOError if not."""
|
||||
if not self.seekable():
|
||||
raise IOError("Random access not allowed!")
|
||||
|
||||
def fileno(self):
|
||||
raise io.UnsupportedOperation
|
||||
|
||||
def seek(self, offset, whence=io.SEEK_SET):
|
||||
self._check_open()
|
||||
self._check_random()
|
||||
if whence == io.SEEK_SET:
|
||||
ok = self._dev.seek(offset)
|
||||
elif whence == io.SEEK_CUR:
|
||||
ok = self._dev.seek(self.tell() + offset)
|
||||
elif whence == io.SEEK_END:
|
||||
ok = self._dev.seek(len(self) + offset)
|
||||
else:
|
||||
raise io.UnsupportedOperation("whence = {} is not "
|
||||
"supported!".format(whence))
|
||||
if not ok:
|
||||
raise IOError(self._dev.errorString())
|
||||
|
||||
def truncate(self, size=None): # pylint: disable=unused-argument
|
||||
raise io.UnsupportedOperation
|
||||
|
||||
def close(self):
|
||||
self._dev.close()
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
return not self._dev.isOpen()
|
||||
|
||||
def flush(self):
|
||||
self._check_open()
|
||||
self._dev.waitForBytesWritten(-1)
|
||||
|
||||
def isatty(self):
|
||||
self._check_open()
|
||||
return False
|
||||
|
||||
def readable(self):
|
||||
return self._dev.isReadable()
|
||||
|
||||
def readline(self, size=-1):
|
||||
self._check_open()
|
||||
if size == -1:
|
||||
size = 0
|
||||
return self._dev.readLine(size)
|
||||
|
||||
def seekable(self):
|
||||
return not self._dev.isSequential()
|
||||
|
||||
def tell(self):
|
||||
self._check_open()
|
||||
self._check_random()
|
||||
return self._dev.pos()
|
||||
|
||||
def writable(self):
|
||||
return self._dev.isWritable()
|
||||
|
||||
def readinto(self, b):
|
||||
self._check_open()
|
||||
return self._dev.read(b, len(b))
|
||||
|
||||
def write(self, b):
|
||||
self._check_open()
|
||||
num = self._dev.write(b)
|
||||
if num == -1 or num < len(b):
|
||||
raise IOError(self._dev.errorString())
|
||||
return num
|
||||
|
||||
def read(self, size):
|
||||
self._check_open()
|
||||
buf = bytes()
|
||||
num = self._dev.read(buf, size)
|
||||
if num == -1:
|
||||
raise IOError(self._dev.errorString())
|
||||
return num
|
||||
|
||||
|
||||
class QtValueError(ValueError):
|
||||
|
||||
"""Exception which gets raised by ensure_valid."""
|
||||
|
Loading…
Reference in New Issue
Block a user