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,
|
from qutebrowser.config import (configdata, iniparsers, configtypes,
|
||||||
textwrapper, keyconfparser)
|
textwrapper, keyconfparser)
|
||||||
from qutebrowser.commands import cmdexc, cmdutils
|
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
|
from qutebrowser.utils.usertypes import Completion
|
||||||
|
|
||||||
|
|
||||||
@ -579,7 +579,7 @@ class ConfigManager(QObject):
|
|||||||
return
|
return
|
||||||
configfile = os.path.join(self._configdir, self._fname)
|
configfile = os.path.join(self._configdir, self._fname)
|
||||||
log.destroy.debug("Saving config to {}".format(configfile))
|
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))
|
f.write(str(self))
|
||||||
|
|
||||||
def dump_userconfig(self):
|
def dump_userconfig(self):
|
||||||
|
@ -23,7 +23,7 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
from qutebrowser.utils import log, utils
|
from qutebrowser.utils import log, utils, qtutils
|
||||||
|
|
||||||
|
|
||||||
class ReadConfigParser(configparser.ConfigParser):
|
class ReadConfigParser(configparser.ConfigParser):
|
||||||
@ -67,5 +67,5 @@ class ReadWriteConfigParser(ReadConfigParser):
|
|||||||
if not os.path.exists(self._configdir):
|
if not os.path.exists(self._configdir):
|
||||||
os.makedirs(self._configdir, 0o755)
|
os.makedirs(self._configdir, 0o755)
|
||||||
log.destroy.debug("Saving config to {}".format(self._configfile))
|
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)
|
self.write(f)
|
||||||
|
@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSignal, QObject
|
|||||||
|
|
||||||
from qutebrowser.config import configdata, textwrapper
|
from qutebrowser.config import configdata, textwrapper
|
||||||
from qutebrowser.commands import cmdutils, cmdexc
|
from qutebrowser.commands import cmdutils, cmdexc
|
||||||
from qutebrowser.utils import log, utils
|
from qutebrowser.utils import log, utils, qtutils
|
||||||
|
|
||||||
|
|
||||||
class KeyConfigError(Exception):
|
class KeyConfigError(Exception):
|
||||||
@ -126,8 +126,9 @@ class KeyConfigParser(QObject):
|
|||||||
if self._configfile is None:
|
if self._configfile is None:
|
||||||
return
|
return
|
||||||
log.destroy.debug("Saving key config to {}".format(self._configfile))
|
log.destroy.debug("Saving key config to {}".format(self._configfile))
|
||||||
with open(self._configfile, 'w', encoding='utf-8') as f:
|
with qtutils.savefile_open(self._configfile, encoding='utf-8') as f:
|
||||||
f.write(str(self))
|
data = str(self)
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
@cmdutils.register(instance='key-config')
|
@cmdutils.register(instance='key-config')
|
||||||
def bind(self, key, *command, mode=None):
|
def bind(self, key, *command, mode=None):
|
||||||
|
@ -25,7 +25,7 @@ import collections
|
|||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot
|
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
|
from qutebrowser.config import config
|
||||||
|
|
||||||
|
|
||||||
@ -104,12 +104,8 @@ class LineConfigParser(collections.UserList):
|
|||||||
if not os.path.exists(self._configdir):
|
if not os.path.exists(self._configdir):
|
||||||
os.makedirs(self._configdir, 0o755)
|
os.makedirs(self._configdir, 0o755)
|
||||||
log.destroy.debug("Saving config to {}".format(self._configfile))
|
log.destroy.debug("Saving config to {}".format(self._configfile))
|
||||||
if self._binary:
|
with qtutils.savefile_open(self._configfile, self._binary) as f:
|
||||||
with open(self._configfile, 'wb') as f:
|
self.write(f, limit)
|
||||||
self.write(f, limit)
|
|
||||||
else:
|
|
||||||
with open(self._configfile, 'w', encoding='utf-8') as f:
|
|
||||||
self.write(f, limit)
|
|
||||||
|
|
||||||
@pyqtSlot(str, str)
|
@pyqtSlot(str, str)
|
||||||
def cleanup_file(self, section, option):
|
def cleanup_file(self, section, option):
|
||||||
|
@ -27,14 +27,16 @@ Module attributes:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import operator
|
import operator
|
||||||
import distutils.version # pylint: disable=no-name-in-module,import-error
|
import distutils.version # pylint: disable=no-name-in-module,import-error
|
||||||
# https://bitbucket.org/logilab/pylint/issue/73/
|
# https://bitbucket.org/logilab/pylint/issue/73/
|
||||||
|
import contextlib
|
||||||
|
|
||||||
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
|
from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
|
||||||
QIODevice)
|
QIODevice, QSaveFile)
|
||||||
|
|
||||||
|
|
||||||
MAXVALS = {
|
MAXVALS = {
|
||||||
@ -155,6 +157,132 @@ def deserialize(data, obj):
|
|||||||
_check_qdatastream(stream)
|
_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):
|
class QtValueError(ValueError):
|
||||||
|
|
||||||
"""Exception which gets raised by ensure_valid."""
|
"""Exception which gets raised by ensure_valid."""
|
||||||
|
Loading…
Reference in New Issue
Block a user