From c8d3fc57c2b67f9b0f94ebb7ae4d05215d1492bf Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 7 Aug 2015 21:57:19 -0400 Subject: [PATCH 01/22] First version of directory browser. --- qutebrowser/browser/dirbrowser.py | 55 +++++++++++++++++++++++++++++++ qutebrowser/browser/webpage.py | 7 ++++ qutebrowser/html/dirbrowser.html | 43 ++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 qutebrowser/browser/dirbrowser.py create mode 100644 qutebrowser/html/dirbrowser.html diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py new file mode 100644 index 000000000..18b4e0bc9 --- /dev/null +++ b/qutebrowser/browser/dirbrowser.py @@ -0,0 +1,55 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015 Antoni Boucher (antoyo) +# +# 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 . + +"""The directory browser page.""" + +import os + +from qutebrowser.utils import jinja + + +def dirbrowser(url): + """Get the directory browser web page. + + Args: + url: The directory path. + + Return: + The HTML of the web page. + """ + title = "Browse directory: {}".format(url) + template = jinja.env.get_template('dirbrowser.html') + # pylint: disable=no-member + # https://bitbucket.org/logilab/pylint/issue/490/ + + def is_file(file): + return os.path.isfile(os.path.join(url, file)) + + def is_dir(file): + return os.path.isdir(os.path.join(url, file)) + + parent = os.path.dirname(url) + all_files = os.listdir(url) + files = sorted([(file, os.path.join(url, file)) for file in all_files if + is_file(file)]) + directories = sorted([(file, os.path.join(url, file)) for file in + all_files if is_dir(file)]) + html = template.render(title=title, url=url, icon='', parent=parent, + files=files, directories=directories) + return html diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 16460eaad..db8cb0871 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -31,6 +31,7 @@ from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config from qutebrowser.browser import http, tabhistory +from qutebrowser.browser.dirbrowser import dirbrowser from qutebrowser.browser.network import networkmanager from qutebrowser.utils import (message, usertypes, log, jinja, qtutils, utils, objreg, debug) @@ -158,6 +159,12 @@ class BrowserPage(QWebPage): if QUrl(elem.attribute('src')) == info.url: elem.setAttribute('style', 'display: none') return False + elif (error_str.endswith('Path is a directory') and + info.url.scheme() == 'file'): + html = dirbrowser(info.url.url()[7:]) + errpage.content = html.encode('utf-8') + errpage.encoding = 'utf-8' + return True else: self._ignore_load_started = True self.error_occurred = True diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html new file mode 100644 index 000000000..19659caf5 --- /dev/null +++ b/qutebrowser/html/dirbrowser.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block style %} +{{ super() }} +#dirbrowserContainer { + background: #fff; + min-width: 35em; + max-width: 35em; + position: absolute; + top: 2em; + left: 1em; + padding: 10px; + border: 2px solid #eee; + -webkit-border-radius: 5px; +} + +#dirbrowserTitleText { + font-size: 118%; + font-weight: bold; +} +{% endblock %} + +{% block content %} +
+
+

Browse directory: {{url}}

+
+ +
    + {% if parent %} +
  • ..
  • + {% endif %} + + {% for item in directories %} +
  • {{item.0}}
  • + {% endfor %} + + {% for item in files %} +
  • {{item.0}}
  • + {% endfor %} +
+
+ +{% endblock %} From 4c1ed3539084d9fc7e2aafbc7ac9745de78ac99c Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 7 Aug 2015 22:45:42 -0400 Subject: [PATCH 02/22] Removed .. when in root folder. --- qutebrowser/browser/dirbrowser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index 18b4e0bc9..c079a5cfb 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -44,7 +44,10 @@ def dirbrowser(url): def is_dir(file): return os.path.isdir(os.path.join(url, file)) - parent = os.path.dirname(url) + if os.path.dirname(url) == url: + parent = None + else: + parent = os.path.dirname(url) all_files = os.listdir(url) files = sorted([(file, os.path.join(url, file)) for file in all_files if is_file(file)]) From fedf9d9c725e4a5daa90a417bb20bd576bdd1bf2 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 7 Aug 2015 22:45:54 -0400 Subject: [PATCH 03/22] Replaced tuple by dict. --- qutebrowser/browser/dirbrowser.py | 10 ++++++---- qutebrowser/html/dirbrowser.html | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index c079a5cfb..1b7a2bd92 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -49,10 +49,12 @@ def dirbrowser(url): else: parent = os.path.dirname(url) all_files = os.listdir(url) - files = sorted([(file, os.path.join(url, file)) for file in all_files if - is_file(file)]) - directories = sorted([(file, os.path.join(url, file)) for file in - all_files if is_dir(file)]) + files = sorted([{'name': file, 'absname': os.path.join(url, file)} + for file in all_files if is_file(file)], + key=lambda v: v['name'].lower()) + directories = sorted([{'name': file, 'absname': os.path.join(url, file)} + for file in all_files if is_dir(file)], + key=lambda v: v['name'].lower()) html = template.render(title=title, url=url, icon='', parent=parent, files=files, directories=directories) return html diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index 19659caf5..d5fefc489 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -31,11 +31,11 @@ {% endif %} {% for item in directories %} -
  • {{item.0}}
  • +
  • {{item.name}}
  • {% endfor %} {% for item in files %} -
  • {{item.0}}
  • +
  • {{item.name}}
  • {% endfor %} From 2be0743378538f2449a56aa84c818f5d05501727 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 7 Aug 2015 23:28:24 -0400 Subject: [PATCH 04/22] Added images. --- qutebrowser/browser/dirbrowser.py | 10 +++++++++- qutebrowser/html/dirbrowser.html | 24 ++++++++++++++++++++++++ qutebrowser/img/file.png | Bin 0 -> 1098 bytes qutebrowser/img/folder.png | Bin 0 -> 834 bytes 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 qutebrowser/img/file.png create mode 100644 qutebrowser/img/folder.png diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index 1b7a2bd92..dfacb4785 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -20,7 +20,9 @@ """The directory browser page.""" import os +import pkg_resources +import qutebrowser from qutebrowser.utils import jinja @@ -38,6 +40,11 @@ def dirbrowser(url): # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ + folder = pkg_resources.resource_filename(qutebrowser.__name__, + 'img/folder.png') + file = pkg_resources.resource_filename(qutebrowser.__name__, + 'img/file.png') + def is_file(file): return os.path.isfile(os.path.join(url, file)) @@ -56,5 +63,6 @@ def dirbrowser(url): for file in all_files if is_dir(file)], key=lambda v: v['name'].lower()) html = template.render(title=title, url=url, icon='', parent=parent, - files=files, directories=directories) + files=files, directories=directories, folder=folder, + file=file) return html diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index d5fefc489..ca9d4e658 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -17,6 +17,26 @@ font-size: 118%; font-weight: bold; } + +ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +ul > li { + background-repeat: no-repeat; + line-height: 22px; + padding-left: 25px; +} + +ul > li { + background-image: url('{{folder}}'); +} + +ul.files > li { + background-image: url('{{file}}'); +} {% endblock %} {% block content %} @@ -29,11 +49,15 @@ {% if parent %}
  • ..
  • {% endif %} + + +
      {% for item in files %}
    • {{item.name}}
    • {% endfor %} diff --git a/qutebrowser/img/file.png b/qutebrowser/img/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a258c7c2e6d10486091f91eba27acb199b268063 GIT binary patch literal 1098 zcmV-Q1hxB#P)fFDZ*Bm0X>@2HVIW9V zM@1lLWo&RDW^!+BAWC6tbz+882eSYG15-&vK~y-6m6Y9U990;HpLaH!%x*T-wwtJl zQ5%c(1FT;lenfLEN}LOlqeotk*=0!fj|NlZ6@k2luuCi6IyU zIs0-YWTn2s{QNvqQ{VF9_&C68zADk~_zQx1#d+5W&YD+9UuTgFEF_;4m|SMe z4t4MSF2({96j)haq19?}JEK_aVf4Pyww!m{!OkNHfe6DR_aGwOmFZ^ombTF{W4C+8 zBEoX5P83DAX_A)2Z3nyDb{n~OIA*2WhUwX0v(r!pwQ=FIO0-?4i;> zK!3SHxztZ7EK>?ggncE#zA#rM!Z4}X*-ON&__q&FejEVwNTM@%diu=N=H}K=SPGvr z)q-W4KiB#8+0000NbVXQnL3MO!Z*l-k zVQ_CCLt$)TZDDR?a%E}Xks2xh001d;MObu1WpiV4X>fFDZ*BlWVRL0VTf3r`W!8&ziYLVZigz`&WT$3gGUIv*)`G zcB=By&xS@oSRQm^#*rr0m8M}G2I!~`wid5k$RpC+E$WpESc?bXh=?*)GWl!<>nLy% z>t4lT32Rvt5#<1>%u?aSoAH-%5Cnu#w65g`K5xgz;)Pt{8IV$d`{ecPi0_BJGt-~< zHWS5CDf;?!^7~`J6(9wqf&G7;%~T$k0r~m)} literal 0 HcmV?d00001 From ec5049f8017dc424b9eaeb002bb076806d13fd03 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 13:32:04 -0400 Subject: [PATCH 05/22] Renamed url to urlstring. --- qutebrowser/browser/dirbrowser.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index dfacb4785..709b7e2d1 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -26,16 +26,16 @@ import qutebrowser from qutebrowser.utils import jinja -def dirbrowser(url): +def dirbrowser(urlstring): """Get the directory browser web page. Args: - url: The directory path. + urlstring: The directory path. Return: The HTML of the web page. """ - title = "Browse directory: {}".format(url) + title = "Browse directory: {}".format(urlstring) template = jinja.env.get_template('dirbrowser.html') # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ @@ -46,23 +46,23 @@ def dirbrowser(url): 'img/file.png') def is_file(file): - return os.path.isfile(os.path.join(url, file)) + return os.path.isfile(os.path.join(urlstring, file)) def is_dir(file): - return os.path.isdir(os.path.join(url, file)) + return os.path.isdir(os.path.join(urlstring, file)) - if os.path.dirname(url) == url: + if os.path.dirname(urlstring) == urlstring: parent = None else: - parent = os.path.dirname(url) - all_files = os.listdir(url) - files = sorted([{'name': file, 'absname': os.path.join(url, file)} + parent = os.path.dirname(urlstring) + all_files = os.listdir(urlstring) + files = sorted([{'name': file, 'absname': os.path.join(urlstring, file)} for file in all_files if is_file(file)], key=lambda v: v['name'].lower()) - directories = sorted([{'name': file, 'absname': os.path.join(url, file)} + directories = sorted([{'name': file, 'absname': os.path.join(urlstring, file)} for file in all_files if is_dir(file)], key=lambda v: v['name'].lower()) - html = template.render(title=title, url=url, icon='', parent=parent, + html = template.render(title=title, url=urlstring, icon='', parent=parent, files=files, directories=directories, folder=folder, file=file) return html From b8809f879ddbb103ab8d849f168cd2a7957a3e3a Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 13:47:47 -0400 Subject: [PATCH 06/22] Added resource_filename function and tests. --- qutebrowser/browser/dirbrowser.py | 12 +++++------- qutebrowser/utils/utils.py | 14 ++++++++++++++ tests/utils/test_utils.py | 20 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index 709b7e2d1..6145e4819 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -20,10 +20,9 @@ """The directory browser page.""" import os -import pkg_resources -import qutebrowser from qutebrowser.utils import jinja +from qutebrowser.utils.utils import resource_filename def dirbrowser(urlstring): @@ -40,10 +39,8 @@ def dirbrowser(urlstring): # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ - folder = pkg_resources.resource_filename(qutebrowser.__name__, - 'img/folder.png') - file = pkg_resources.resource_filename(qutebrowser.__name__, - 'img/file.png') + folder = resource_filename('img/folder.png') + file = resource_filename('img/file.png') def is_file(file): return os.path.isfile(os.path.join(urlstring, file)) @@ -59,7 +56,8 @@ def dirbrowser(urlstring): files = sorted([{'name': file, 'absname': os.path.join(urlstring, file)} for file in all_files if is_file(file)], key=lambda v: v['name'].lower()) - directories = sorted([{'name': file, 'absname': os.path.join(urlstring, file)} + directories = sorted([{'name': file, 'absname': os.path.join(urlstring, + file)} for file in all_files if is_dir(file)], key=lambda v: v['name'].lower()) html = template.render(title=title, url=urlstring, icon='', parent=parent, diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 373e38cbd..32542f8ef 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -89,6 +89,20 @@ def read_file(filename, binary=False): return data +def resource_filename(filename): + """Get the absolute filename of a file contained with qutebrowser. + + Args: + filename: The filename. + + Return: + The absolute filename. + """ + if hasattr(sys, 'frozen'): + return os.path.join(os.path.dirname(sys.executable), filename) + return pkg_resources.resource_filename(qutebrowser.__name__, filename) + + def actute_warning(): """Display a warning about the dead_actute issue if needed.""" # WORKAROUND (remove this when we bump the requirements to 5.3.0) diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index 9c0e2e6c4..db85dcf59 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -117,6 +117,26 @@ class TestReadFile: assert content.splitlines()[0] == b"Hello World!" +class TestResourceFilename: + + """Test resource_filename.""" + + @pytest.fixture(autouse=True, params=[True, False]) + def freezer(self, request, monkeypatch): + if request.param and not getattr(sys, 'frozen', False): + monkeypatch.setattr(sys, 'frozen', True, raising=False) + monkeypatch.setattr('sys.executable', qutebrowser.__file__) + elif not request.param and getattr(sys, 'frozen', False): + # Want to test unfrozen tests, but we are frozen + pytest.skip("Can't run with sys.frozen = True!") + + def test_readfile(self): + """Read a test file.""" + filename = utils.resource_filename(os.path.join('utils', 'testfile')) + expected = os.path.abspath('./qutebrowser/utils/testfile') + assert expected == filename + + class Patcher: """Helper for TestActuteWarning. From e6521b047d74504e04f85ee1e6a930fddf5058d5 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 14:10:27 -0400 Subject: [PATCH 07/22] Added get_file_list function and tests. --- qutebrowser/browser/dirbrowser.py | 33 +++++++++++-------- tests/browser/test_dirbrowser.py | 55 +++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 tests/browser/test_dirbrowser.py diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index 6145e4819..e82eba958 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -25,6 +25,24 @@ from qutebrowser.utils import jinja from qutebrowser.utils.utils import resource_filename +def get_file_list(basedir, all_files, filterfunc): + """Get a list of files filtered by a filter function and sorted by name. + + Args: + basedir: The parent directory of all files. + all_files: The list of files to filter and sort. + filterfunc: The filter function. + + Return: + A list of dicts. Each dict contains the name and absname keys. + """ + items = [{'name': filename, 'absname': + os.path.abspath(os.path.join(basedir, filename))} + for filename in all_files + if filterfunc(os.path.join(basedir, filename))] + return sorted(items, key=lambda v: v['name'].lower()) + + def dirbrowser(urlstring): """Get the directory browser web page. @@ -42,24 +60,13 @@ def dirbrowser(urlstring): folder = resource_filename('img/folder.png') file = resource_filename('img/file.png') - def is_file(file): - return os.path.isfile(os.path.join(urlstring, file)) - - def is_dir(file): - return os.path.isdir(os.path.join(urlstring, file)) - if os.path.dirname(urlstring) == urlstring: parent = None else: parent = os.path.dirname(urlstring) all_files = os.listdir(urlstring) - files = sorted([{'name': file, 'absname': os.path.join(urlstring, file)} - for file in all_files if is_file(file)], - key=lambda v: v['name'].lower()) - directories = sorted([{'name': file, 'absname': os.path.join(urlstring, - file)} - for file in all_files if is_dir(file)], - key=lambda v: v['name'].lower()) + files = get_file_list(urlstring, all_files, os.path.isfile) + directories = get_file_list(urlstring, all_files, os.path.isdir) html = template.render(title=title, url=urlstring, icon='', parent=parent, files=files, directories=directories, folder=folder, file=file) diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_dirbrowser.py new file mode 100644 index 000000000..cca017338 --- /dev/null +++ b/tests/browser/test_dirbrowser.py @@ -0,0 +1,55 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015 Antoni Boucher (antoyo) +# +# 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 qutebrowser.browser.dirbrowser.""" + +import os + +from qutebrowser.browser.dirbrowser import get_file_list + + +class TestFileList: + + """Test file list.""" + + def test_get_file_list(self): + """Test get_file_list.""" + basedir = os.path.abspath('./qutebrowser/utils') + all_files = os.listdir(basedir) + result = get_file_list(basedir, all_files, os.path.isfile) + assert {'name': 'testfile', 'absname': os.path.join(basedir, + 'testfile')} in result + + """Test get_file_list with os.path.isdir filter function.""" + basedir = os.path.abspath('./qutebrowser/utils') + all_files = os.listdir(basedir) + result = get_file_list(basedir, all_files, os.path.isdir) + print(result) + assert {'name': 'testfile', 'absname': os.path.join(basedir, + 'testfile')} not in result + + basedir = os.path.abspath('./qutebrowser') + all_files = os.listdir(basedir) + result = get_file_list(basedir, all_files, os.path.isfile) + assert ({'name': 'utils', 'absname': os.path.join(basedir, 'utils')} + not in result) + + result = get_file_list(basedir, all_files, os.path.isdir) + assert ({'name': 'utils', 'absname': os.path.join(basedir, 'utils')} + in result) From 2969599390593561a6819025aa1e957688daf440 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 14:13:09 -0400 Subject: [PATCH 08/22] Use toLocalFile function instead of slicing. --- qutebrowser/browser/webpage.py | 2 +- tests/browser/test_dirbrowser.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index db8cb0871..f5d6fe675 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -161,7 +161,7 @@ class BrowserPage(QWebPage): return False elif (error_str.endswith('Path is a directory') and info.url.scheme() == 'file'): - html = dirbrowser(info.url.url()[7:]) + html = dirbrowser(info.url.toLocalFile()) errpage.content = html.encode('utf-8') errpage.encoding = 'utf-8' return True diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_dirbrowser.py index cca017338..ebfee4bc0 100644 --- a/tests/browser/test_dirbrowser.py +++ b/tests/browser/test_dirbrowser.py @@ -36,7 +36,6 @@ class TestFileList: assert {'name': 'testfile', 'absname': os.path.join(basedir, 'testfile')} in result - """Test get_file_list with os.path.isdir filter function.""" basedir = os.path.abspath('./qutebrowser/utils') all_files = os.listdir(basedir) result = get_file_list(basedir, all_files, os.path.isdir) From 7e20d77bdff08e0a00556462250f9512da40d1c0 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 14:40:37 -0400 Subject: [PATCH 09/22] Switch to SVG file and tango theme. --- MANIFEST.in | 1 + qutebrowser/browser/dirbrowser.py | 4 +- qutebrowser/html/dirbrowser.html | 1 + qutebrowser/img/file.png | Bin 1098 -> 0 bytes qutebrowser/img/file.svg | 548 ++++++++++++++++++++++++++++++ qutebrowser/img/folder.png | Bin 834 -> 0 bytes qutebrowser/img/folder.svg | 424 +++++++++++++++++++++++ 7 files changed, 976 insertions(+), 2 deletions(-) delete mode 100644 qutebrowser/img/file.png create mode 100644 qutebrowser/img/file.svg delete mode 100644 qutebrowser/img/folder.png create mode 100644 qutebrowser/img/folder.svg diff --git a/MANIFEST.in b/MANIFEST.in index 62d5e78b6..80f440197 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ global-exclude __pycache__ *.pyc *.pyo recursive-include qutebrowser *.py recursive-include qutebrowser/html *.html +recursive-include qutebrowser/img *.svg recursive-include qutebrowser/test *.py recursive-include qutebrowser/javascript *.js graft icons diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py index e82eba958..61c3e4bd6 100644 --- a/qutebrowser/browser/dirbrowser.py +++ b/qutebrowser/browser/dirbrowser.py @@ -57,8 +57,8 @@ def dirbrowser(urlstring): # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ - folder = resource_filename('img/folder.png') - file = resource_filename('img/file.png') + folder = resource_filename('img/folder.svg') + file = resource_filename('img/file.svg') if os.path.dirname(urlstring) == urlstring: parent = None diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index ca9d4e658..a312ff913 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -26,6 +26,7 @@ ul { ul > li { background-repeat: no-repeat; + background-size: 22px; line-height: 22px; padding-left: 25px; } diff --git a/qutebrowser/img/file.png b/qutebrowser/img/file.png deleted file mode 100644 index a258c7c2e6d10486091f91eba27acb199b268063..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1098 zcmV-Q1hxB#P)fFDZ*Bm0X>@2HVIW9V zM@1lLWo&RDW^!+BAWC6tbz+882eSYG15-&vK~y-6m6Y9U990;HpLaH!%x*T-wwtJl zQ5%c(1FT;lenfLEN}LOlqeotk*=0!fj|NlZ6@k2luuCi6IyU zIs0-YWTn2s{QNvqQ{VF9_&C68zADk~_zQx1#d+5W&YD+9UuTgFEF_;4m|SMe z4t4MSF2({96j)haq19?}JEK_aVf4Pyww!m{!OkNHfe6DR_aGwOmFZ^ombTF{W4C+8 zBEoX5P83DAX_A)2Z3nyDb{n~OIA*2WhUwX0v(r!pwQ=FIO0-?4i;> zK!3SHxztZ7EK>?ggncE#zA#rM!Z4}X*-ON&__q&FejEVwNTM@%diu=N=H}K=SPGvr z)q-W4KiB#8+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Generic Text + + + text + plaintext + regular + document + + + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qutebrowser/img/folder.png b/qutebrowser/img/folder.png deleted file mode 100644 index 07784f88ed66cbfb0de560ae10dd3341db76ca5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 834 zcmV-I1HJr-P)0000NbVXQnL3MO!Z*l-k zVQ_CCLt$)TZDDR?a%E}Xks2xh001d;MObu1WpiV4X>fFDZ*BlWVRL0VTf3r`W!8&ziYLVZigz`&WT$3gGUIv*)`G zcB=By&xS@oSRQm^#*rr0m8M}G2I!~`wid5k$RpC+E$WpESc?bXh=?*)GWl!<>nLy% z>t4lT32Rvt5#<1>%u?aSoAH-%5Cnu#w65g`K5xgz;)Pt{8IV$d`{ecPi0_BJGt-~< zHWS5CDf;?!^7~`J6(9wqf&G7;%~T$k0r~m)} diff --git a/qutebrowser/img/folder.svg b/qutebrowser/img/folder.svg new file mode 100644 index 000000000..79b25c305 --- /dev/null +++ b/qutebrowser/img/folder.svg @@ -0,0 +1,424 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Folder Icon + + + + Jakub Steiner + + + + http://jimmac.musichall.cz + + + folder + directory + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 14ae308279214cee62e06a3355bf94f2d677e074 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 15:16:48 -0400 Subject: [PATCH 10/22] Added a file:// scheme. --- qutebrowser/browser/dirbrowser.py | 73 ------------ qutebrowser/browser/network/filescheme.py | 110 ++++++++++++++++++ qutebrowser/browser/network/networkmanager.py | 2 + qutebrowser/browser/webpage.py | 7 -- tests/browser/test_dirbrowser.py | 2 +- 5 files changed, 113 insertions(+), 81 deletions(-) delete mode 100644 qutebrowser/browser/dirbrowser.py create mode 100644 qutebrowser/browser/network/filescheme.py diff --git a/qutebrowser/browser/dirbrowser.py b/qutebrowser/browser/dirbrowser.py deleted file mode 100644 index 61c3e4bd6..000000000 --- a/qutebrowser/browser/dirbrowser.py +++ /dev/null @@ -1,73 +0,0 @@ -# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: - -# Copyright 2015 Antoni Boucher (antoyo) -# -# 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 . - -"""The directory browser page.""" - -import os - -from qutebrowser.utils import jinja -from qutebrowser.utils.utils import resource_filename - - -def get_file_list(basedir, all_files, filterfunc): - """Get a list of files filtered by a filter function and sorted by name. - - Args: - basedir: The parent directory of all files. - all_files: The list of files to filter and sort. - filterfunc: The filter function. - - Return: - A list of dicts. Each dict contains the name and absname keys. - """ - items = [{'name': filename, 'absname': - os.path.abspath(os.path.join(basedir, filename))} - for filename in all_files - if filterfunc(os.path.join(basedir, filename))] - return sorted(items, key=lambda v: v['name'].lower()) - - -def dirbrowser(urlstring): - """Get the directory browser web page. - - Args: - urlstring: The directory path. - - Return: - The HTML of the web page. - """ - title = "Browse directory: {}".format(urlstring) - template = jinja.env.get_template('dirbrowser.html') - # pylint: disable=no-member - # https://bitbucket.org/logilab/pylint/issue/490/ - - folder = resource_filename('img/folder.svg') - file = resource_filename('img/file.svg') - - if os.path.dirname(urlstring) == urlstring: - parent = None - else: - parent = os.path.dirname(urlstring) - all_files = os.listdir(urlstring) - files = get_file_list(urlstring, all_files, os.path.isfile) - directories = get_file_list(urlstring, all_files, os.path.isdir) - html = template.render(title=title, url=urlstring, icon='', parent=parent, - files=files, directories=directories, folder=folder, - file=file) - return html diff --git a/qutebrowser/browser/network/filescheme.py b/qutebrowser/browser/network/filescheme.py new file mode 100644 index 000000000..c86b283c0 --- /dev/null +++ b/qutebrowser/browser/network/filescheme.py @@ -0,0 +1,110 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . +# +# pylint complains when using .render() on jinja templates, so we make it shut +# up for this whole module. + +# pylint: disable=no-member +# https://bitbucket.org/logilab/pylint/issue/490/ + +"""Handler functions for different qute:... pages. + +Module attributes: + pyeval_output: The output of the last :pyeval command. +""" + +import os + +from qutebrowser.browser.network import schemehandler, networkreply +from qutebrowser.utils import jinja +from qutebrowser.utils.utils import resource_filename + + +def get_file_list(basedir, all_files, filterfunc): + """Get a list of files filtered by a filter function and sorted by name. + + Args: + basedir: The parent directory of all files. + all_files: The list of files to filter and sort. + filterfunc: The filter function. + + Return: + A list of dicts. Each dict contains the name and absname keys. + """ + items = [{'name': filename, 'absname': + os.path.abspath(os.path.join(basedir, filename))} + for filename in all_files + if filterfunc(os.path.join(basedir, filename))] + return sorted(items, key=lambda v: v['name'].lower()) + + +def dirbrowser(urlstring): + """Get the directory browser web page. + + Args: + urlstring: The directory path. + + Return: + The HTML of the web page. + """ + if os.path.isdir(urlstring): + title = "Browse directory: {}".format(urlstring) + template = jinja.env.get_template('dirbrowser.html') + # pylint: disable=no-member + # https://bitbucket.org/logilab/pylint/issue/490/ + + folder = resource_filename('img/folder.svg') + file = resource_filename('img/file.svg') + + if os.path.dirname(urlstring) == urlstring: + parent = None + else: + parent = os.path.dirname(urlstring) + all_files = os.listdir(urlstring) + files = get_file_list(urlstring, all_files, os.path.isfile) + directories = get_file_list(urlstring, all_files, os.path.isdir) + html = template.render(title=title, url=urlstring, icon='', + parent=parent, files=files, + directories=directories, folder=folder, + file=file) + html = html.encode('UTF-8', errors='xmlcharrefreplace') + else: + with open(urlstring, 'rb') as f: + html = f.read() + return html + + +class FileSchemeHandler(schemehandler.SchemeHandler): + + """Scheme handler for file: URLs.""" + + def createRequest(self, _op, request, _outgoing_data): + """Create a new request. + + Args: + request: const QNetworkRequest & req + _op: Operation op + _outgoing_data: QIODevice * outgoingData + + Return: + A QNetworkReply. + """ + data = dirbrowser(request.url().toLocalFile()) + return networkreply.FixedDataNetworkReply( + request, data, 'text/html', self.parent()) diff --git a/qutebrowser/browser/network/networkmanager.py b/qutebrowser/browser/network/networkmanager.py index 1e0049684..e2119c460 100644 --- a/qutebrowser/browser/network/networkmanager.py +++ b/qutebrowser/browser/network/networkmanager.py @@ -31,6 +31,7 @@ from qutebrowser.utils import (message, log, usertypes, utils, objreg, qtutils, urlutils) from qutebrowser.browser import cookies from qutebrowser.browser.network import qutescheme, networkreply +from qutebrowser.browser.network import filescheme HOSTBLOCK_ERROR_STRING = '%HOSTBLOCK%' @@ -97,6 +98,7 @@ class NetworkManager(QNetworkAccessManager): self._requests = [] self._scheme_handlers = { 'qute': qutescheme.QuteSchemeHandler(win_id), + 'file': filescheme.FileSchemeHandler(win_id), } self._set_cookiejar() self._set_cache() diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index f5d6fe675..16460eaad 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -31,7 +31,6 @@ from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config from qutebrowser.browser import http, tabhistory -from qutebrowser.browser.dirbrowser import dirbrowser from qutebrowser.browser.network import networkmanager from qutebrowser.utils import (message, usertypes, log, jinja, qtutils, utils, objreg, debug) @@ -159,12 +158,6 @@ class BrowserPage(QWebPage): if QUrl(elem.attribute('src')) == info.url: elem.setAttribute('style', 'display: none') return False - elif (error_str.endswith('Path is a directory') and - info.url.scheme() == 'file'): - html = dirbrowser(info.url.toLocalFile()) - errpage.content = html.encode('utf-8') - errpage.encoding = 'utf-8' - return True else: self._ignore_load_started = True self.error_occurred = True diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_dirbrowser.py index ebfee4bc0..80a04499a 100644 --- a/tests/browser/test_dirbrowser.py +++ b/tests/browser/test_dirbrowser.py @@ -21,7 +21,7 @@ import os -from qutebrowser.browser.dirbrowser import get_file_list +from qutebrowser.browser.network.filescheme import get_file_list class TestFileList: From a6010e3ead84ef1b8b4384899869deac07380e08 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 15:40:18 -0400 Subject: [PATCH 11/22] Renamed test function. --- tests/utils/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index db85dcf59..b99adb103 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -130,7 +130,7 @@ class TestResourceFilename: # Want to test unfrozen tests, but we are frozen pytest.skip("Can't run with sys.frozen = True!") - def test_readfile(self): + def test_resource_filename(self): """Read a test file.""" filename = utils.resource_filename(os.path.join('utils', 'testfile')) expected = os.path.abspath('./qutebrowser/utils/testfile') From e5779d07751c377f2702604bc052688bd41de7f8 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 19:32:47 -0400 Subject: [PATCH 12/22] Fixed tests. --- tests/browser/test_dirbrowser.py | 45 ++++++++++++++------------------ tests/utils/test_utils.py | 25 ++++++++++-------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_dirbrowser.py index 80a04499a..022c5604b 100644 --- a/tests/browser/test_dirbrowser.py +++ b/tests/browser/test_dirbrowser.py @@ -17,38 +17,33 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -"""Tests for qutebrowser.browser.dirbrowser.""" +"""Tests for qutebrowser.browser.network.filescheme.""" import os +import pytest + from qutebrowser.browser.network.filescheme import get_file_list -class TestFileList: +@pytest.mark.parametrize('create_file, create_dir, filterfunc, expected', [ + (True, False, os.path.isfile, True), + (True, False, os.path.isdir, False), - """Test file list.""" + (False, True, os.path.isfile, False), + (False, True, os.path.isdir, True), - def test_get_file_list(self): - """Test get_file_list.""" - basedir = os.path.abspath('./qutebrowser/utils') - all_files = os.listdir(basedir) - result = get_file_list(basedir, all_files, os.path.isfile) - assert {'name': 'testfile', 'absname': os.path.join(basedir, - 'testfile')} in result + (False, False, os.path.isfile, False), + (False, False, os.path.isdir, False), +]) +def test_get_file_list(tmpdir, create_file, create_dir, filterfunc, expected): + """Test get_file_list.""" + path = tmpdir / 'foo' + if create_file or create_dir: + path.ensure(dir=create_dir) - basedir = os.path.abspath('./qutebrowser/utils') - all_files = os.listdir(basedir) - result = get_file_list(basedir, all_files, os.path.isdir) - print(result) - assert {'name': 'testfile', 'absname': os.path.join(basedir, - 'testfile')} not in result + all_files = os.listdir(str(tmpdir)) - basedir = os.path.abspath('./qutebrowser') - all_files = os.listdir(basedir) - result = get_file_list(basedir, all_files, os.path.isfile) - assert ({'name': 'utils', 'absname': os.path.join(basedir, 'utils')} - not in result) - - result = get_file_list(basedir, all_files, os.path.isdir) - assert ({'name': 'utils', 'absname': os.path.join(basedir, 'utils')} - in result) + result = get_file_list(str(tmpdir), all_files, filterfunc) + item = {'name': 'foo', 'absname': str(path)} + assert (item in result) == expected diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index b99adb103..cbcfd7f5d 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -92,19 +92,21 @@ class TestEliding: assert utils.elide(text, length) == expected +@pytest.fixture(params=[True, False]) +def freezer(request, monkeypatch): + if request.param and not getattr(sys, 'frozen', False): + monkeypatch.setattr(sys, 'frozen', True, raising=False) + monkeypatch.setattr('sys.executable', qutebrowser.__file__) + elif not request.param and getattr(sys, 'frozen', False): + # Want to test unfrozen tests, but we are frozen + pytest.skip("Can't run with sys.frozen = True!") + + +@pytest.mark.usefixtures('freezer') class TestReadFile: """Test read_file.""" - @pytest.fixture(autouse=True, params=[True, False]) - def freezer(self, request, monkeypatch): - if request.param and not getattr(sys, 'frozen', False): - monkeypatch.setattr(sys, 'frozen', True, raising=False) - monkeypatch.setattr('sys.executable', qutebrowser.__file__) - elif not request.param and getattr(sys, 'frozen', False): - # Want to test unfrozen tests, but we are frozen - pytest.skip("Can't run with sys.frozen = True!") - def test_readfile(self): """Read a test file.""" content = utils.read_file(os.path.join('utils', 'testfile')) @@ -117,6 +119,7 @@ class TestReadFile: assert content.splitlines()[0] == b"Hello World!" +@pytest.mark.usefixtures('freezer') class TestResourceFilename: """Test resource_filename.""" @@ -133,8 +136,8 @@ class TestResourceFilename: def test_resource_filename(self): """Read a test file.""" filename = utils.resource_filename(os.path.join('utils', 'testfile')) - expected = os.path.abspath('./qutebrowser/utils/testfile') - assert expected == filename + with open(filename, 'r', encoding='utf-8') as f: + assert f.read().splitlines()[0] == "Hello World!" class Patcher: From 0896d7a8b3251e2aea6cd5c288e6cd30dd97599c Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 19:45:00 -0400 Subject: [PATCH 13/22] Fixed file scheme handler. --- qutebrowser/browser/network/filescheme.py | 56 +++++++++---------- qutebrowser/browser/network/networkmanager.py | 4 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/qutebrowser/browser/network/filescheme.py b/qutebrowser/browser/network/filescheme.py index c86b283c0..bf4133cb0 100644 --- a/qutebrowser/browser/network/filescheme.py +++ b/qutebrowser/browser/network/filescheme.py @@ -47,10 +47,11 @@ def get_file_list(basedir, all_files, filterfunc): Return: A list of dicts. Each dict contains the name and absname keys. """ - items = [{'name': filename, 'absname': - os.path.abspath(os.path.join(basedir, filename))} - for filename in all_files - if filterfunc(os.path.join(basedir, filename))] + items = [] + for filename in all_files: + absname = os.path.join(basedir, filename) + if filterfunc(absname): + items.append({'name': filename, 'absname': absname}) return sorted(items, key=lambda v: v['name'].lower()) @@ -63,31 +64,26 @@ def dirbrowser(urlstring): Return: The HTML of the web page. """ - if os.path.isdir(urlstring): - title = "Browse directory: {}".format(urlstring) - template = jinja.env.get_template('dirbrowser.html') - # pylint: disable=no-member - # https://bitbucket.org/logilab/pylint/issue/490/ + title = "Browse directory: {}".format(urlstring) + template = jinja.env.get_template('dirbrowser.html') + # pylint: disable=no-member + # https://bitbucket.org/logilab/pylint/issue/490/ - folder = resource_filename('img/folder.svg') - file = resource_filename('img/file.svg') + folder = resource_filename('img/folder.svg') + file = resource_filename('img/file.svg') - if os.path.dirname(urlstring) == urlstring: - parent = None - else: - parent = os.path.dirname(urlstring) - all_files = os.listdir(urlstring) - files = get_file_list(urlstring, all_files, os.path.isfile) - directories = get_file_list(urlstring, all_files, os.path.isdir) - html = template.render(title=title, url=urlstring, icon='', - parent=parent, files=files, - directories=directories, folder=folder, - file=file) - html = html.encode('UTF-8', errors='xmlcharrefreplace') + if os.path.dirname(urlstring) == urlstring: + parent = None else: - with open(urlstring, 'rb') as f: - html = f.read() - return html + parent = os.path.dirname(urlstring) + all_files = os.listdir(urlstring) + files = get_file_list(urlstring, all_files, os.path.isfile) + directories = get_file_list(urlstring, all_files, os.path.isdir) + html = template.render(title=title, url=urlstring, icon='', + parent=parent, files=files, + directories=directories, folder=folder, + file=file) + return html.encode('UTF-8', errors='xmlcharrefreplace') class FileSchemeHandler(schemehandler.SchemeHandler): @@ -105,6 +101,8 @@ class FileSchemeHandler(schemehandler.SchemeHandler): Return: A QNetworkReply. """ - data = dirbrowser(request.url().toLocalFile()) - return networkreply.FixedDataNetworkReply( - request, data, 'text/html', self.parent()) + urlstring = request.url().toLocalFile() + if os.path.isdir(urlstring): + data = dirbrowser(urlstring) + return networkreply.FixedDataNetworkReply( + request, data, 'text/html', self.parent()) diff --git a/qutebrowser/browser/network/networkmanager.py b/qutebrowser/browser/network/networkmanager.py index e2119c460..7359a2d43 100644 --- a/qutebrowser/browser/network/networkmanager.py +++ b/qutebrowser/browser/network/networkmanager.py @@ -320,8 +320,10 @@ class NetworkManager(QNetworkAccessManager): """ scheme = req.url().scheme() if scheme in self._scheme_handlers: - return self._scheme_handlers[scheme].createRequest( + result = self._scheme_handlers[scheme].createRequest( op, req, outgoing_data) + if result: + return result host_blocker = objreg.get('host-blocker') if (op == QNetworkAccessManager.GetOperation and From 4d2a55190f7a5e90f9559b64a5ac546853f86b9b Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sat, 8 Aug 2015 19:52:13 -0400 Subject: [PATCH 14/22] Removed useless method. --- tests/utils/test_utils.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index cbcfd7f5d..c22f4835a 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -124,15 +124,6 @@ class TestResourceFilename: """Test resource_filename.""" - @pytest.fixture(autouse=True, params=[True, False]) - def freezer(self, request, monkeypatch): - if request.param and not getattr(sys, 'frozen', False): - monkeypatch.setattr(sys, 'frozen', True, raising=False) - monkeypatch.setattr('sys.executable', qutebrowser.__file__) - elif not request.param and getattr(sys, 'frozen', False): - # Want to test unfrozen tests, but we are frozen - pytest.skip("Can't run with sys.frozen = True!") - def test_resource_filename(self): """Read a test file.""" filename = utils.resource_filename(os.path.join('utils', 'testfile')) From 052d18147e8a1691043da6586a86a7e4855b12da Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 9 Aug 2015 11:29:18 -0400 Subject: [PATCH 15/22] Added permission check. --- qutebrowser/browser/network/filescheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/network/filescheme.py b/qutebrowser/browser/network/filescheme.py index bf4133cb0..3ec6bf7ec 100644 --- a/qutebrowser/browser/network/filescheme.py +++ b/qutebrowser/browser/network/filescheme.py @@ -102,7 +102,7 @@ class FileSchemeHandler(schemehandler.SchemeHandler): A QNetworkReply. """ urlstring = request.url().toLocalFile() - if os.path.isdir(urlstring): + if os.path.isdir(urlstring) and os.access(urlstring, os.R_OK): data = dirbrowser(urlstring) return networkreply.FixedDataNetworkReply( request, data, 'text/html', self.parent()) From 814841200a4ec49d655ac6d9bbc611670b76041e Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 12 Aug 2015 17:24:01 -0400 Subject: [PATCH 16/22] Fixes issues. --- qutebrowser/browser/network/filescheme.py | 52 ++++++++++++------- qutebrowser/browser/network/networkmanager.py | 37 +++++++------ qutebrowser/html/dirbrowser.html | 4 +- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/qutebrowser/browser/network/filescheme.py b/qutebrowser/browser/network/filescheme.py index 3ec6bf7ec..e38bf770d 100644 --- a/qutebrowser/browser/network/filescheme.py +++ b/qutebrowser/browser/network/filescheme.py @@ -1,6 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2015 Florian Bruhin (The Compiler) +# Copyright 2015 Antoni Boucher (antoyo) # # This file is part of qutebrowser. # @@ -20,14 +21,7 @@ # pylint complains when using .render() on jinja templates, so we make it shut # up for this whole module. -# pylint: disable=no-member -# https://bitbucket.org/logilab/pylint/issue/490/ - -"""Handler functions for different qute:... pages. - -Module attributes: - pyeval_output: The output of the last :pyeval command. -""" +"""Handler functions for file:... pages.""" import os @@ -55,7 +49,19 @@ def get_file_list(basedir, all_files, filterfunc): return sorted(items, key=lambda v: v['name'].lower()) -def dirbrowser(urlstring): +def is_root(directory): + """Check if the directory is the root directory. + + Args: + directory: The directory to check. + + Return: + Whether the directory is a root directory or not. + """ + return os.path.dirname(directory) == directory + + +def dirbrowser_html(urlstring): """Get the directory browser web page. Args: @@ -69,20 +75,30 @@ def dirbrowser(urlstring): # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ - folder = resource_filename('img/folder.svg') - file = resource_filename('img/file.svg') + folder_icon = resource_filename('img/folder.svg') + file_icon = resource_filename('img/file.svg') - if os.path.dirname(urlstring) == urlstring: + if is_root(urlstring): parent = None else: parent = os.path.dirname(urlstring) - all_files = os.listdir(urlstring) + + try: + all_files = os.listdir(urlstring) + except (PermissionError, OSError) as e: + html = jinja.env.get_template('error.html').render( + title="Error while reading directory", + url='file://%s' % urlstring, + error=str(e), + icon='') + return html.encode('UTF-8', errors='xmlcharrefreplace') + files = get_file_list(urlstring, all_files, os.path.isfile) directories = get_file_list(urlstring, all_files, os.path.isdir) html = template.render(title=title, url=urlstring, icon='', parent=parent, files=files, - directories=directories, folder=folder, - file=file) + directories=directories, folder_icon=folder_icon, + file_icon=file_icon) return html.encode('UTF-8', errors='xmlcharrefreplace') @@ -99,10 +115,10 @@ class FileSchemeHandler(schemehandler.SchemeHandler): _outgoing_data: QIODevice * outgoingData Return: - A QNetworkReply. + A QNetworkReply for directories, None for files. """ urlstring = request.url().toLocalFile() - if os.path.isdir(urlstring) and os.access(urlstring, os.R_OK): - data = dirbrowser(urlstring) + if os.path.isdir(urlstring): + data = dirbrowser_html(urlstring) return networkreply.FixedDataNetworkReply( request, data, 'text/html', self.parent()) diff --git a/qutebrowser/browser/network/networkmanager.py b/qutebrowser/browser/network/networkmanager.py index ff08b015a..a2815d4ab 100644 --- a/qutebrowser/browser/network/networkmanager.py +++ b/qutebrowser/browser/network/networkmanager.py @@ -297,6 +297,25 @@ class NetworkManager(QNetworkAccessManager): download.destroyed.connect(self.on_adopted_download_destroyed) download.do_retry.connect(self.adopt_download) + def setReferer(self, req, current_url): + """Set the referer header.""" + referer_header_conf = config.get('network', 'referer-header') + + try: + if referer_header_conf == 'never': + # Note: using ''.encode('ascii') sends a header with no value, + # instead of no header at all + req.setRawHeader('Referer'.encode('ascii'), QByteArray()) + elif (referer_header_conf == 'same-domain' and + not urlutils.same_domain(req.url(), current_url)): + req.setRawHeader('Referer'.encode('ascii'), QByteArray()) + # If refer_header_conf is set to 'always', we leave the header + # alone as QtWebKit did set it. + except urlutils.InvalidUrlError: + # req.url() or current_url can be invalid - this happens on + # https://www.playstation.com/ for example. + pass + # WORKAROUND for: # http://www.riverbankcomputing.com/pipermail/pyqt/2014-September/034806.html # @@ -322,7 +341,7 @@ class NetworkManager(QNetworkAccessManager): if scheme in self._scheme_handlers: result = self._scheme_handlers[scheme].createRequest( op, req, outgoing_data) - if result: + if result is not None: return result host_blocker = objreg.get('host-blocker') @@ -348,22 +367,8 @@ class NetworkManager(QNetworkAccessManager): webview = objreg.get('webview', scope='tab', window=self._win_id, tab=self._tab_id) current_url = webview.url() - referer_header_conf = config.get('network', 'referer-header') - try: - if referer_header_conf == 'never': - # Note: using ''.encode('ascii') sends a header with no value, - # instead of no header at all - req.setRawHeader('Referer'.encode('ascii'), QByteArray()) - elif (referer_header_conf == 'same-domain' and - not urlutils.same_domain(req.url(), current_url)): - req.setRawHeader('Referer'.encode('ascii'), QByteArray()) - # If refer_header_conf is set to 'always', we leave the header - # alone as QtWebKit did set it. - except urlutils.InvalidUrlError: - # req.url() or current_url can be invalid - this happens on - # https://www.playstation.com/ for example. - pass + self.setReferer(req, current_url) accept_language = config.get('network', 'accept-language') if accept_language is not None: diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index a312ff913..fec109364 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -32,11 +32,11 @@ ul > li { } ul > li { - background-image: url('{{folder}}'); + background-image: url('{{folder_icon}}'); } ul.files > li { - background-image: url('{{file}}'); + background-image: url('{{file_icon}}'); } {% endblock %} From 8450093de0430c408a7b6157d8b98a409a04b6f8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Aug 2015 18:54:58 +0200 Subject: [PATCH 17/22] Install BeautifulSoup4 for tests. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 679dba9e7..9f2e7092a 100644 --- a/tox.ini +++ b/tox.ini @@ -25,6 +25,7 @@ deps = coverage==3.7.1 pytest-cov==2.0.0 cov-core==1.15.0 + beautifulsoup4==4.4.0 commands = {envpython} scripts/link_pyqt.py --tox {envdir} {envpython} -m py.test --strict -rfEsw --cov qutebrowser --cov-report xml --cov-report= {posargs:tests} From 7b3de27b44d1a943db2c5ca87a831507e70da301 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Aug 2015 21:55:09 +0200 Subject: [PATCH 18/22] Add class="parent" in dirbrowser.html. --- qutebrowser/html/dirbrowser.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index fec109364..56e030e05 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -46,7 +46,7 @@ ul.files > li {

      Browse directory: {{url}}

      -
        +
          {% if parent %}
        • ..
        • {% endif %} From b3df642b21212e43ce98a0166d6fc91016109985 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Aug 2015 21:55:25 +0200 Subject: [PATCH 19/22] Write more tests for dirbrowser. --- tests/browser/test_dirbrowser.py | 156 ++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_dirbrowser.py index 022c5604b..3307b8cd2 100644 --- a/tests/browser/test_dirbrowser.py +++ b/tests/browser/test_dirbrowser.py @@ -20,10 +20,14 @@ """Tests for qutebrowser.browser.network.filescheme.""" import os +import collections import pytest +import bs4 +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkRequest -from qutebrowser.browser.network.filescheme import get_file_list +from qutebrowser.browser.network import filescheme @pytest.mark.parametrize('create_file, create_dir, filterfunc, expected', [ @@ -44,6 +48,154 @@ def test_get_file_list(tmpdir, create_file, create_dir, filterfunc, expected): all_files = os.listdir(str(tmpdir)) - result = get_file_list(str(tmpdir), all_files, filterfunc) + result = filescheme.get_file_list(str(tmpdir), all_files, filterfunc) item = {'name': 'foo', 'absname': str(path)} assert (item in result) == expected + + +class TestIsRoot: + + @pytest.mark.windows + @pytest.mark.parametrize('directory, is_root', [ + ('C:\\foo', False), + ('C:\\', True) + ]) + def test_windows(self, directory, is_root): + assert filescheme.is_root(directory) == is_root + + @pytest.mark.posix + @pytest.mark.parametrize('directory, is_root', [ + ('/foo', False), + ('/', True) + ]) + def test_posix(self, directory, is_root): + assert filescheme.is_root(directory) == is_root + + +class TestDirbrowserHtml: + + Parsed = collections.namedtuple('Parsed', 'parent, folders, files') + Item = collections.namedtuple('Item', 'link, text') + + @pytest.fixture + def parser(self): + """Provide a function to get a parsed dirbrowser document.""" + def parse(path): + html = filescheme.dirbrowser_html(path) + soup = bs4.BeautifulSoup(html, 'html.parser') + print(soup.prettify()) + container = soup('div', id='dirbrowserContainer')[0] + + parent_elem = container('ul', class_='parent')[0].li + if parent_elem is None: + parent = None + else: + parent = parent_elem.a.string + + folders = [] + files = [] + + for li in container('ul', class_='folders')[0]('li'): + item = self.Item(link=li.a['href'], text=str(li.a.string)) + folders.append(item) + + for li in container('ul', class_='files')[0]('li'): + item = self.Item(link=li.a['href'], text=str(li.a.string)) + files.append(item) + + return self.Parsed(parent=parent, folders=folders, files=files) + + return parse + + def test_basic(self): + html = filescheme.dirbrowser_html(os.getcwd()) + soup = bs4.BeautifulSoup(html, 'html.parser') + print(soup.prettify()) + container = soup.div + assert container['id'] == 'dirbrowserContainer' + title_elem = container('div', id='dirbrowserTitle')[0] + title_text = title_elem('p', id='dirbrowserTitleText')[0].text + assert title_text == 'Browse directory: {}'.format(os.getcwd()) + + def test_empty(self, tmpdir, parser): + parsed = parser(str(tmpdir)) + assert parsed.parent + assert not parsed.folders + assert not parsed.files + + def test_files(self, tmpdir, parser): + foo_file = tmpdir / 'foo' + bar_file = tmpdir / 'bar' + foo_file.ensure() + bar_file.ensure() + + parsed = parser(str(tmpdir)) + assert parsed.parent + assert not parsed.folders + foo_item = self.Item('file://' + str(foo_file), foo_file.relto(tmpdir)) + bar_item = self.Item('file://' + str(bar_file), bar_file.relto(tmpdir)) + assert parsed.files == [bar_item, foo_item] + + def test_dirs(self, tmpdir, parser): + foo_dir = tmpdir / 'foo' + bar_dir = tmpdir / 'bar' + foo_dir.ensure(dir=True) + bar_dir.ensure(dir=True) + + parsed = parser(str(tmpdir)) + assert parsed.parent + assert not parsed.files + foo_item = self.Item('file://' + str(foo_dir), foo_dir.relto(tmpdir)) + bar_item = self.Item('file://' + str(bar_dir), bar_dir.relto(tmpdir)) + assert parsed.folders == [bar_item, foo_item] + + def test_mixed(self, tmpdir, parser): + foo_file = tmpdir / 'foo' + bar_dir = tmpdir / 'bar' + foo_file.ensure() + bar_dir.ensure(dir=True) + + parsed = parser(str(tmpdir)) + foo_item = self.Item('file://' + str(foo_file), foo_file.relto(tmpdir)) + bar_item = self.Item('file://' + str(bar_dir), bar_dir.relto(tmpdir)) + assert parsed.parent + assert parsed.files == [foo_item] + assert parsed.folders == [bar_item] + + def test_root_dir(self, tmpdir, parser): + if os.name == 'nt': + root_dir = 'C:\\' + else: + root_dir = '/' + parsed = parser(root_dir) + assert not parsed.parent + + def test_oserror(self, mocker): + m = mocker.patch('qutebrowser.browser.network.filescheme.os.listdir') + m.side_effect = OSError('Error message') + html = filescheme.dirbrowser_html('') + soup = bs4.BeautifulSoup(html, 'html.parser') + print(soup.prettify()) + error_title = soup('p', id='errorTitleText')[0].string + error_msg = soup('p', id='errorMessageText')[0].string + assert error_title == 'Unable to load page' + assert error_msg == 'Error message' + + +class TestFileSchemeHandler: + + def test_dir(self, tmpdir): + url = QUrl.fromLocalFile(str(tmpdir)) + req = QNetworkRequest(url) + handler = filescheme.FileSchemeHandler(win_id=0) + reply = handler.createRequest(None, req, None) + assert reply.readAll() == filescheme.dirbrowser_html(str(tmpdir)) + + def test_file(self, tmpdir): + filename = tmpdir / 'foo' + filename.ensure() + url = QUrl.fromLocalFile(str(filename)) + req = QNetworkRequest(url) + handler = filescheme.FileSchemeHandler(win_id=0) + reply = handler.createRequest(None, req, None) + assert reply is None From bbb581eaf8857989814ab3614168a259c1733d5a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 13 Aug 2015 21:55:36 +0200 Subject: [PATCH 20/22] 100% coverage for browser.network.filescheme. --- scripts/dev/check_coverage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py index d2b158bd7..9dd6dc137 100644 --- a/scripts/dev/check_coverage.py +++ b/scripts/dev/check_coverage.py @@ -40,6 +40,7 @@ PERFECT_FILES = [ 'qutebrowser/browser/rfc6266.py', 'qutebrowser/browser/webelem.py', 'qutebrowser/browser/network/schemehandler.py', + 'qutebrowser/browser/network/filescheme.py', 'qutebrowser/misc/readline.py', 'qutebrowser/misc/split.py', From abeb7e3390e412ef4edfcc268f30cba18856766e Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Thu, 13 Aug 2015 19:54:23 -0400 Subject: [PATCH 21/22] Fixed issues. --- qutebrowser/browser/network/filescheme.py | 28 +++++++++---------- qutebrowser/html/dirbrowser.html | 9 +++--- ...{test_dirbrowser.py => test_filescheme.py} | 6 ++-- tests/utils/test_utils.py | 14 ++++------ 4 files changed, 26 insertions(+), 31 deletions(-) rename tests/browser/{test_dirbrowser.py => test_filescheme.py} (97%) diff --git a/qutebrowser/browser/network/filescheme.py b/qutebrowser/browser/network/filescheme.py index e38bf770d..7bb2e24c4 100644 --- a/qutebrowser/browser/network/filescheme.py +++ b/qutebrowser/browser/network/filescheme.py @@ -61,16 +61,16 @@ def is_root(directory): return os.path.dirname(directory) == directory -def dirbrowser_html(urlstring): +def dirbrowser_html(path): """Get the directory browser web page. Args: - urlstring: The directory path. + path: The directory path. Return: The HTML of the web page. """ - title = "Browse directory: {}".format(urlstring) + title = "Browse directory: {}".format(path) template = jinja.env.get_template('dirbrowser.html') # pylint: disable=no-member # https://bitbucket.org/logilab/pylint/issue/490/ @@ -78,24 +78,24 @@ def dirbrowser_html(urlstring): folder_icon = resource_filename('img/folder.svg') file_icon = resource_filename('img/file.svg') - if is_root(urlstring): + if is_root(path): parent = None else: - parent = os.path.dirname(urlstring) + parent = os.path.dirname(path) try: - all_files = os.listdir(urlstring) - except (PermissionError, OSError) as e: + all_files = os.listdir(path) + except OSError as e: html = jinja.env.get_template('error.html').render( title="Error while reading directory", - url='file://%s' % urlstring, + url='file://%s' % path, error=str(e), icon='') return html.encode('UTF-8', errors='xmlcharrefreplace') - files = get_file_list(urlstring, all_files, os.path.isfile) - directories = get_file_list(urlstring, all_files, os.path.isdir) - html = template.render(title=title, url=urlstring, icon='', + files = get_file_list(path, all_files, os.path.isfile) + directories = get_file_list(path, all_files, os.path.isdir) + html = template.render(title=title, url=path, icon='', parent=parent, files=files, directories=directories, folder_icon=folder_icon, file_icon=file_icon) @@ -117,8 +117,8 @@ class FileSchemeHandler(schemehandler.SchemeHandler): Return: A QNetworkReply for directories, None for files. """ - urlstring = request.url().toLocalFile() - if os.path.isdir(urlstring): - data = dirbrowser_html(urlstring) + path = request.url().toLocalFile() + if os.path.isdir(path): + data = dirbrowser_html(path) return networkreply.FixedDataNetworkReply( request, data, 'text/html', self.parent()) diff --git a/qutebrowser/html/dirbrowser.html b/qutebrowser/html/dirbrowser.html index 56e030e05..716785698 100644 --- a/qutebrowser/html/dirbrowser.html +++ b/qutebrowser/html/dirbrowser.html @@ -46,11 +46,11 @@ ul.files > li {

          Browse directory: {{url}}

          -
            - {% if parent %} + {% if parent %} +
            • ..
            • - {% endif %} -
            +
          + {% endif %}
            {% for item in directories %} @@ -64,5 +64,4 @@ ul.files > li { {% endfor %}
          - {% endblock %} diff --git a/tests/browser/test_dirbrowser.py b/tests/browser/test_filescheme.py similarity index 97% rename from tests/browser/test_dirbrowser.py rename to tests/browser/test_filescheme.py index 3307b8cd2..44e0bca68 100644 --- a/tests/browser/test_dirbrowser.py +++ b/tests/browser/test_filescheme.py @@ -86,11 +86,11 @@ class TestDirbrowserHtml: print(soup.prettify()) container = soup('div', id='dirbrowserContainer')[0] - parent_elem = container('ul', class_='parent')[0].li - if parent_elem is None: + parent_elem = container('ul', class_='parent') + if len(parent_elem) == 0: parent = None else: - parent = parent_elem.a.string + parent = parent_elem[0].li.a.string folders = [] files = [] diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index c22f4835a..02bed2f64 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -120,15 +120,11 @@ class TestReadFile: @pytest.mark.usefixtures('freezer') -class TestResourceFilename: - - """Test resource_filename.""" - - def test_resource_filename(self): - """Read a test file.""" - filename = utils.resource_filename(os.path.join('utils', 'testfile')) - with open(filename, 'r', encoding='utf-8') as f: - assert f.read().splitlines()[0] == "Hello World!" +def test_resource_filename(): + """Read a test file.""" + filename = utils.resource_filename(os.path.join('utils', 'testfile')) + with open(filename, 'r', encoding='utf-8') as f: + assert f.read().splitlines()[0] == "Hello World!" class Patcher: From 149ca68853826b9b4b5376e78c9942f783993c10 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 14 Aug 2015 08:24:13 -0400 Subject: [PATCH 22/22] Renamed setReferer to set_referer. --- qutebrowser/browser/network/networkmanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/network/networkmanager.py b/qutebrowser/browser/network/networkmanager.py index a2815d4ab..267d9ff6a 100644 --- a/qutebrowser/browser/network/networkmanager.py +++ b/qutebrowser/browser/network/networkmanager.py @@ -297,7 +297,7 @@ class NetworkManager(QNetworkAccessManager): download.destroyed.connect(self.on_adopted_download_destroyed) download.do_retry.connect(self.adopt_download) - def setReferer(self, req, current_url): + def set_referer(self, req, current_url): """Set the referer header.""" referer_header_conf = config.get('network', 'referer-header') @@ -368,7 +368,7 @@ class NetworkManager(QNetworkAccessManager): tab=self._tab_id) current_url = webview.url() - self.setReferer(req, current_url) + self.set_referer(req, current_url) accept_language = config.get('network', 'accept-language') if accept_language is not None: