From cf32aac111c76c9233ff5b606efb1c47fa6f9d28 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 9 Nov 2016 08:15:31 +0100 Subject: [PATCH] Move usertypes.DownloadTarget to downloads module --- qutebrowser/browser/adblock.py | 3 +- qutebrowser/browser/commands.py | 4 +- qutebrowser/browser/downloads.py | 79 ++++++++++++++++++- qutebrowser/browser/qtnetworkdownloads.py | 6 +- .../browser/webengine/webenginedownloads.py | 2 +- qutebrowser/browser/webkit/mhtml.py | 2 +- qutebrowser/mainwindow/prompt.py | 5 +- qutebrowser/utils/usertypes.py | 71 ----------------- tests/unit/browser/webkit/test_downloads.py | 33 ++++++++ .../utils/usertypes/test_downloadtarget.py | 59 -------------- 10 files changed, 120 insertions(+), 144 deletions(-) delete mode 100644 tests/unit/utils/usertypes/test_downloadtarget.py diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index 9a65231bb..6197847d9 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -26,6 +26,7 @@ import posixpath import zipfile import fnmatch +from qutebrowser.browser import downloads from qutebrowser.config import config from qutebrowser.utils import objreg, standarddir, log, message, usertypes from qutebrowser.commands import cmdutils, cmdexc @@ -208,7 +209,7 @@ class HostBlocker: else: fobj = io.BytesIO() fobj.name = 'adblock: ' + url.host() - target = usertypes.FileObjDownloadTarget(fobj) + target = downloads.FileObjDownloadTarget(fobj) download = download_manager.get(url, target=target, auto_remove=True) self._in_progress.append(download) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 042e75a51..b3f9d67c8 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1328,7 +1328,7 @@ class CommandDispatcher: if dest is None: target = None else: - target = usertypes.FileDownloadTarget(dest) + target = downloads.FileDownloadTarget(dest) download_manager.get(url, target=target) elif mhtml_: self._download_mhtml(dest) @@ -1336,7 +1336,7 @@ class CommandDispatcher: if dest is None: target = None else: - target = usertypes.FileDownloadTarget(dest) + target = downloads.FileDownloadTarget(dest) download_manager.get(self._current_url(), target=target) def _download_mhtml(self, dest=None): diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index be4d3285b..12e766ecb 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -164,6 +164,77 @@ def get_filename_question(*, suggested_filename, url, parent=None): return q +class NoFilenameError(Exception): + + """Raised when we can't find out a filename in DownloadTarget.""" + + +# Where a download should be saved +class _DownloadTarget: + + """Abstract base class for different download targets.""" + + def __init__(self): + raise NotImplementedError + + def suggested_filename(self): + """Get the suggested filename for this download target.""" + raise NotImplementedError + + +class FileDownloadTarget(_DownloadTarget): + + """Save the download to the given file. + + Attributes: + filename: Filename where the download should be saved. + """ + + def __init__(self, filename): + # pylint: disable=super-init-not-called + self.filename = filename + + def suggested_filename(self): + return os.path.basename(self.filename) + + +class FileObjDownloadTarget(_DownloadTarget): + + """Save the download to the given file-like object. + + Attributes: + fileobj: File-like object where the download should be written to. + """ + + def __init__(self, fileobj): + # pylint: disable=super-init-not-called + self.fileobj = fileobj + + def suggested_filename(self): + try: + return self.fileobj.name + except AttributeError: + raise NoFilenameError + + +class OpenFileDownloadTarget(_DownloadTarget): + + """Save the download in a temp dir and directly open it. + + Attributes: + cmdline: The command to use as string. A `{}` is expanded to the + filename. None means to use the system's default application. + If no `{}` is found, the filename is appended to the cmdline. + """ + + def __init__(self, cmdline=None): + # pylint: disable=super-init-not-called + self.cmdline = cmdline + + def suggested_filename(self): + raise NoFilenameError + + class DownloadItemStats(QObject): """Statistics (bytes done, total bytes, time, etc.) about a download. @@ -565,13 +636,13 @@ class AbstractDownloadItem(QObject): """Set the target for a given download. Args: - target: The usertypes.DownloadTarget for this download. + target: The DownloadTarget for this download. """ - if isinstance(target, usertypes.FileObjDownloadTarget): + if isinstance(target, FileObjDownloadTarget): self._set_fileobj(target.fileobj, autoclose=False) - elif isinstance(target, usertypes.FileDownloadTarget): + elif isinstance(target, FileDownloadTarget): self._set_filename(target.filename) - elif isinstance(target, usertypes.OpenFileDownloadTarget): + elif isinstance(target, OpenFileDownloadTarget): try: fobj = temp_download_manager.get_tmpfile(self.basename) except OSError as exc: diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py index 5161b3309..c72e60303 100644 --- a/qutebrowser/browser/qtnetworkdownloads.py +++ b/qutebrowser/browser/qtnetworkdownloads.py @@ -372,7 +372,7 @@ class DownloadManager(downloads.AbstractDownloadManager): Args: request: The QNetworkRequest to download. - target: Where to save the download as usertypes.DownloadTarget. + target: Where to save the download as downloads.DownloadTarget. **kwargs: Passed to _fetch_request. Return: @@ -430,7 +430,7 @@ class DownloadManager(downloads.AbstractDownloadManager): Args: reply: The QNetworkReply to download. - target: Where to save the download as usertypes.DownloadTarget. + target: Where to save the download as downloads.DownloadTarget. auto_remove: Whether to remove the download even if ui -> remove-finished-downloads is set to -1. @@ -456,7 +456,7 @@ class DownloadManager(downloads.AbstractDownloadManager): filename = downloads.immediate_download_path(prompt_download_directory) if filename is not None: # User doesn't want to be asked, so just use the download_dir - target = usertypes.FileDownloadTarget(filename) + target = downloads.FileDownloadTarget(filename) download.set_target(target) return download diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 5b4e7f498..5286a6298 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -143,7 +143,7 @@ class DownloadManager(downloads.AbstractDownloadManager): filename = downloads.immediate_download_path() if filename is not None: # User doesn't want to be asked, so just use the download_dir - target = usertypes.FileDownloadTarget(filename) + target = downloads.FileDownloadTarget(filename) download.set_target(target) return diff --git a/qutebrowser/browser/webkit/mhtml.py b/qutebrowser/browser/webkit/mhtml.py index e89e85ef6..237898809 100644 --- a/qutebrowser/browser/webkit/mhtml.py +++ b/qutebrowser/browser/webkit/mhtml.py @@ -345,7 +345,7 @@ class _Downloader: download_manager = objreg.get('qtnetwork-download-manager', scope='window', window=self._win_id) - target = usertypes.FileObjDownloadTarget(_NoCloseBytesIO()) + target = downloads.FileObjDownloadTarget(_NoCloseBytesIO()) item = download_manager.get(url, target=target, auto_remove=True) self.pending_downloads.add((url, item)) diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 7435a46a7..f5ed10009 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -29,6 +29,7 @@ from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex, from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit, QLabel, QFileSystemModel, QTreeView, QSizePolicy) +from qutebrowser.browser import downloads from qutebrowser.config import style from qutebrowser.utils import usertypes, log, utils, qtutils, objreg, message from qutebrowser.keyinput import modeman @@ -687,11 +688,11 @@ class DownloadFilenamePrompt(FilenamePrompt): def accept(self, value=None): text = value if value is not None else self._lineedit.text() - self.question.answer = usertypes.FileDownloadTarget(text) + self.question.answer = downloads.FileDownloadTarget(text) return True def download_open(self, cmdline): - self.question.answer = usertypes.OpenFileDownloadTarget(cmdline) + self.question.answer = downloads.OpenFileDownloadTarget(cmdline) self.question.done() message.global_bridge.prompt_done.emit(self.KEY_MODE) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 052db5720..8cac08fda 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -269,77 +269,6 @@ JsWorld = enum('JsWorld', ['main', 'application', 'user', 'jseval']) MessageLevel = enum('MessageLevel', ['error', 'warning', 'info']) -class NoFilenameError(Exception): - - """Raised when we can't find out a filename in DownloadTarget.""" - - -# Where a download should be saved -class DownloadTarget: - - """Abstract base class for different download targets.""" - - def __init__(self): - raise NotImplementedError - - def suggested_filename(self): - """Get the suggested filename for this download target.""" - raise NotImplementedError - - -class FileDownloadTarget(DownloadTarget): - - """Save the download to the given file. - - Attributes: - filename: Filename where the download should be saved. - """ - - def __init__(self, filename): - # pylint: disable=super-init-not-called - self.filename = filename - - def suggested_filename(self): - return os.path.basename(self.filename) - - -class FileObjDownloadTarget(DownloadTarget): - - """Save the download to the given file-like object. - - Attributes: - fileobj: File-like object where the download should be written to. - """ - - def __init__(self, fileobj): - # pylint: disable=super-init-not-called - self.fileobj = fileobj - - def suggested_filename(self): - try: - return self.fileobj.name - except AttributeError: - raise NoFilenameError - - -class OpenFileDownloadTarget(DownloadTarget): - - """Save the download in a temp dir and directly open it. - - Attributes: - cmdline: The command to use as string. A `{}` is expanded to the - filename. None means to use the system's default application. - If no `{}` is found, the filename is appended to the cmdline. - """ - - def __init__(self, cmdline=None): - # pylint: disable=super-init-not-called - self.cmdline = cmdline - - def suggested_filename(self): - raise NoFilenameError - - class Question(QObject): """A question asked to the user, e.g. via the status bar. diff --git a/tests/unit/browser/webkit/test_downloads.py b/tests/unit/browser/webkit/test_downloads.py index 498375700..acee916b2 100644 --- a/tests/unit/browser/webkit/test_downloads.py +++ b/tests/unit/browser/webkit/test_downloads.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest from qutebrowser.browser import downloads, qtnetworkdownloads @@ -27,3 +28,35 @@ def test_download_model(qapp, qtmodeltester, config_stub, cookiejar_and_cache): manager = qtnetworkdownloads.DownloadManager(win_id=0) model = downloads.DownloadModel(manager) qtmodeltester.check(model) + + +class TestDownloadTarget: + + def test_base(self): + with pytest.raises(NotImplementedError): + downloads._DownloadTarget() + + def test_filename(self): + target = downloads.FileDownloadTarget("/foo/bar") + assert target.filename == "/foo/bar" + + def test_fileobj(self): + fobj = object() + target = downloads.FileObjDownloadTarget(fobj) + assert target.fileobj is fobj + + def test_openfile(self): + target = downloads.OpenFileDownloadTarget() + assert target.cmdline is None + + def test_openfile_custom_command(self): + target = downloads.OpenFileDownloadTarget('echo') + assert target.cmdline == 'echo' + + @pytest.mark.parametrize('obj', [ + downloads.FileDownloadTarget('foobar'), + downloads.FileObjDownloadTarget(None), + downloads.OpenFileDownloadTarget(), + ]) + def test_class_hierarchy(self, obj): + assert isinstance(obj, downloads._DownloadTarget) diff --git a/tests/unit/utils/usertypes/test_downloadtarget.py b/tests/unit/utils/usertypes/test_downloadtarget.py deleted file mode 100644 index 4da2e7a81..000000000 --- a/tests/unit/utils/usertypes/test_downloadtarget.py +++ /dev/null @@ -1,59 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2016 Daniel Schadt -# -# This file is part of qutebrowser. -# -# qutebrowser is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# qutebrowser is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with qutebrowser. If not, see . - -"""Tests for the DownloadTarget class.""" - -from qutebrowser.utils import usertypes - -import pytest - - -def test_base(): - with pytest.raises(NotImplementedError): - usertypes.DownloadTarget() - - -def test_filename(): - target = usertypes.FileDownloadTarget("/foo/bar") - assert target.filename == "/foo/bar" - - -def test_fileobj(): - fobj = object() - target = usertypes.FileObjDownloadTarget(fobj) - assert target.fileobj is fobj - - -def test_openfile(): - target = usertypes.OpenFileDownloadTarget() - assert target.cmdline is None - - -def test_openfile_custom_command(): - target = usertypes.OpenFileDownloadTarget('echo') - assert target.cmdline == 'echo' - - -@pytest.mark.parametrize('obj', [ - usertypes.FileDownloadTarget('foobar'), - usertypes.FileObjDownloadTarget(None), - usertypes.OpenFileDownloadTarget(), -]) -def test_class_hierarchy(obj): - assert isinstance(obj, usertypes.DownloadTarget)