From 81b2688c70748845eab89236b4026b40c80b112c Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Wed, 6 Jul 2016 18:41:08 +0200 Subject: [PATCH] downloads: use global TempDownloadManager This way, all temporary downloads will end up in the same directory and everything is cleaned up at program exit, not when the corresponding window is closed. --- qutebrowser/app.py | 6 +- qutebrowser/browser/webkit/downloads.py | 83 +++++++++++++++++-------- qutebrowser/mainwindow/mainwindow.py | 1 - 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index c0662eaa0..6cbe7674f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -47,7 +47,7 @@ from qutebrowser.completion.models import instances as completionmodels from qutebrowser.commands import cmdutils, runners, cmdexc from qutebrowser.config import style, config, websettings, configexc from qutebrowser.browser import urlmarks, adblock -from qutebrowser.browser.webkit import cookies, cache, history +from qutebrowser.browser.webkit import cookies, cache, history, downloads from qutebrowser.browser.webkit.network import (qutescheme, proxy, networkmanager) from qutebrowser.mainwindow import mainwindow @@ -438,6 +438,8 @@ def _init_modules(args, crash_handler): os.environ.pop('QT_WAYLAND_DISABLE_WINDOWDECORATION', None) _maybe_hide_mouse_cursor() objreg.get('config').changed.connect(_maybe_hide_mouse_cursor) + temp_downloads = downloads.TempDownloadManager(qApp) + objreg.register('temporary-downloads', temp_downloads) def _init_late_modules(args): @@ -711,6 +713,8 @@ class Quitter: not restart): atexit.register(shutil.rmtree, self._args.basedir, ignore_errors=True) + # Delete temp download dir + objreg.get('temporary-downloads').cleanup() # If we don't kill our custom handler here we might get segfaults log.destroy.debug("Deactivating message handler...") qInstallMessageHandler(None) diff --git a/qutebrowser/browser/webkit/downloads.py b/qutebrowser/browser/webkit/downloads.py index 86670986b..45cd73baa 100644 --- a/qutebrowser/browser/webkit/downloads.py +++ b/qutebrowser/browser/webkit/downloads.py @@ -738,20 +738,6 @@ class DownloadManager(QAbstractListModel): self._update_timer = usertypes.Timer(self, 'download-update') self._update_timer.timeout.connect(self.update_gui) self._update_timer.setInterval(REFRESH_INTERVAL) - self._tmpdir_obj = None - - def cleanup(self): - """Clean up any temporary files from this manager.""" - if self._tmpdir_obj is not None: - self._tmpdir_obj.cleanup() - - @property - def tmpdir(self): - """Lazily create a temporary directory if one is needed.""" - if self._tmpdir_obj is None: - self._tmpdir_obj = tempfile.TemporaryDirectory( - prefix='qutebrowser-downloads-') - return self._tmpdir_obj def __repr__(self): return utils.get_repr(self, downloads=len(self.downloads)) @@ -966,17 +952,8 @@ class DownloadManager(QAbstractListModel): if filename is not usertypes.OPEN_DOWNLOAD: download.set_filename(filename) return - # Find the next free filename without causing a race condition - index = 0 - while True: - basename = '{}-{}'.format(index, suggested_filename) - path = os.path.join(self.tmpdir.name, basename) - try: - fobj = open(path, 'xb') - except FileExistsError: - index += 1 - else: - break + tmp_manager = objreg.get('temporary-downloads') + fobj = tmp_manager.get_tmpfile(suggested_filename) download.finished.connect(download.open_file) download.autoclose = True download.set_fileobj(fobj) @@ -1291,3 +1268,59 @@ class DownloadManager(QAbstractListModel): The number of unfinished downloads. """ return sum(1 for download in self.downloads if not download.done) + + +class TempDownloadManager(QObject): + + """Manager to handle temporary download files. + + The downloads are downloaded to a temporary location and then openened with + the system standard application. The temporary files are deleted when + qutebrowser is shutdown. + + Attributes: + files: A list of NamedTemporaryFiles of downloaded items. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self.files = [] + self._tmpdir = None + + def cleanup(self): + """Clean up any temporary files.""" + if self._tmpdir is not None: + self._tmpdir.cleanup() + self._tmpdir = None + + def get_tmpdir(self): + """Return the temporary directory that is used for downloads. + + The directory is created lazily on first access. + + Return: + The tempfile.TemporaryDirectory that is used. + """ + if self._tmpdir is None: + self._tmpdir = tempfile.TemporaryDirectory( + prefix='qutebrowser-downloads-') + return self._tmpdir + + def get_tmpfile(self, suggested_name): + """Return a temporary file in the temporary downloads directory. + + The files are kept as long as qutebrowser is running and automatically + cleaned up at program exit. + + Args: + suggested_name: str of the "suggested"/original filename. Used as a + suffix, so any file extenions are preserved. + + Return: + A tempfile.NamedTemporaryFile that should be used to save the file. + """ + tmpdir = self.get_tmpdir() + fobj = tempfile.NamedTemporaryFile(dir=tmpdir.name, delete=False, + suffix=suggested_name) + self.files.append(fobj) + return fobj diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 77b24e116..54a735609 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -452,7 +452,6 @@ class MainWindow(QWidget): self._save_geometry() log.destroy.debug("Closing window {}".format(self.win_id)) self.tabbed_browser.shutdown() - self._get_object('download-manager').cleanup() def closeEvent(self, e): """Override closeEvent to display a confirmation if needed."""