Merge remote-tracking branch 'TheCompiler/master' into relax-editor-templating

This commit is contained in:
Oliver Caldwell 2016-01-31 22:43:58 +00:00
commit 54ff2aa46c
26 changed files with 234 additions and 58 deletions

13
.pydocstylerc Normal file
View File

@ -0,0 +1,13 @@
[pydocstyle]
# Disabled checks:
# D102: Missing docstring in public method (will be handled by others)
# D103: Missing docstring in public function (will be handled by others)
# D104: Missing docstring in public package (will be handled by others)
# D105: Missing docstring in magic method (will be handled by others)
# D209: Blank line before closing """ (removed from PEP257)
# D211: No blank lines allowed before class docstring
# (PEP257 got changed, but let's stick to the old standard)
# D402: First line should not be function's signature (false-positives)
ignore = D102,D103,D104,D105,D209,D211,D402
match = (?!resources|test_*).*\.py
inherit = false

View File

@ -16,7 +16,7 @@ env:
- TESTENV=unittests-nodisp
- TESTENV=misc
- TESTENV=vulture
- TESTENV=pep257
- TESTENV=pydocstyle
- TESTENV=flake8
- TESTENV=pyroma
- TESTENV=check-manifest
@ -53,7 +53,7 @@ matrix:
- os: osx
env: TESTENV=vulture
- os: osx
env: TESTENV=pep257
env: TESTENV=pydocstyle
- os: osx
env: TESTENV=flake8
- os: osx

View File

@ -23,6 +23,9 @@ Added
- New `--quiet` argument for the `:debug-pyeval` command to not open a tab with
the results. Note `:debug-pyeval` is still only intended for debugging.
- The completion now matches each entered word separately.
- A new command `:paste-primary` got added to paste the primary selection, and
`<Shift-Insert>` got added as a binding so it pastes primary rather than
clipboard.
Changed
~~~~~~~
@ -36,6 +39,10 @@ Fixed
- Fixed starting with -c "".
- Fixed crash when a tab is closed twice via javascript (e.g. Dropbox
authentication dialogs)
- Fixed crash when a notification/geolocation prompt is answered after closing
the tab it belongs to.
- Fixed crash when downloading a file without any path information (e.g a
magnet link).
v0.5.1
------

View File

@ -143,5 +143,5 @@ My issue is not listed.::
https://github.com/The-Compiler/qutebrowser/issues[the issue tracker] or
using the `:report` command.
If you are reporting a segfault, make sure you read the
https://github.com/The-Compiler/qutebrowser/blob/master/doc/stacktrace.asciidoc[guide]
on how to report them with all needed information.
link:doc/stacktrace.asciidoc[guide] on how to report them with all needed
information.

View File

@ -32,6 +32,7 @@ exclude .eslintignore
exclude doc/help
exclude .appveyor.yml
exclude .travis.yml
exclude .pydocstylerc
exclude misc/appveyor_install.py
global-exclude __pycache__ *.pyc *.pyo

View File

@ -165,6 +165,7 @@ Contributors, sorted by the number of commits in descending order:
* Jonas Schürmann
* Panagiotis Ktistakis
* Jimmy
* Jakub Klinkovský
* skinnay
* error800
* Zach-Button

View File

@ -833,6 +833,7 @@ How many steps to zoom out.
|<<move-to-start-of-next-block,move-to-start-of-next-block>>|Move the cursor or selection to the start of next block.
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|<<paste-primary,paste-primary>>|Paste the primary selection at cursor position.
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|<<prompt-no,prompt-no>>|Answer no to a yes/no prompt.
|<<prompt-yes,prompt-yes>>|Answer yes to a yes/no prompt.
@ -1046,6 +1047,10 @@ Open an external editor with the currently selected form field.
The editor which should be launched can be configured via the `general -> editor` config option.
[[paste-primary]]
=== paste-primary
Paste the primary selection at cursor position.
[[prompt-accept]]
=== prompt-accept
Accept the current prompt.

View File

@ -1307,6 +1307,31 @@ class CommandDispatcher:
except webelem.IsNullError:
raise cmdexc.CommandError("Element vanished while editing!")
@cmdutils.register(instance='command-dispatcher',
modes=[KeyMode.insert], hide=True, scope='window',
needs_js=True)
def paste_primary(self):
"""Paste the primary selection at cursor position."""
frame = self._current_widget().page().currentFrame()
try:
elem = webelem.focus_elem(frame)
except webelem.IsNullError:
raise cmdexc.CommandError("No element focused!")
if not elem.is_editable(strict=True):
raise cmdexc.CommandError("Focused element is not editable!")
clipboard = QApplication.clipboard()
if clipboard.supportsSelection():
sel = clipboard.text(QClipboard.Selection)
log.misc.debug("Pasting primary selection into element {}".format(
elem.debug_text()))
elem.evaluateJavaScript("""
var sel = '{}';
var event = document.createEvent('TextEvent');
event.initTextEvent('textInput', true, true, null, sel);
this.dispatchEvent(event);
""".format(webelem.javascript_escape(sel)))
def _clear_search(self, view, text):
"""Clear search string/highlights for the given view.

View File

@ -774,7 +774,10 @@ class DownloadManager(QAbstractListModel):
# https://bugreports.qt.io/browse/QTBUG-42757
request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
QNetworkRequest.AlwaysNetwork)
suggested_fn = urlutils.filename_from_url(request.url())
if suggested_fn is None:
suggested_fn = 'qutebrowser-download'
# We won't need a question if a filename or fileobj is already given
if fileobj is None and filename is None:

View File

@ -372,6 +372,7 @@ class BrowserPage(QWebPage):
q.answered_no.connect(no_action)
q.cancelled.connect(no_action)
self.shutting_down.connect(q.abort)
q.completed.connect(q.deleteLater)
self.featurePermissionRequestCanceled.connect(functools.partial(

View File

@ -1324,7 +1324,8 @@ KEY_SECTION_DESC = {
"Since normal keypresses are passed through, only special keys are "
"supported in this mode.\n"
"Useful hidden commands to map in this section:\n\n"
" * `open-editor`: Open a texteditor with the focused field."),
" * `open-editor`: Open a texteditor with the focused field.\n"
" * `paste-primary`: Paste primary selection at cursor position."),
'hint': (
"Keybindings for hint mode.\n"
"Since normal keypresses are passed through, only special keys are "
@ -1495,6 +1496,7 @@ KEY_DATA = collections.OrderedDict([
('insert', collections.OrderedDict([
('open-editor', ['<Ctrl-E>']),
('paste-primary', ['<Shift-Ins>']),
])),
('hint', collections.OrderedDict([

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Crash when trying to get filename from custom URL</title>
</head>
<body>
<a href="qute://">download</a>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Paste primary selection</title>
</head>
<body>
<textarea id="qute-textarea"></textarea>
<textarea id="qute-textarea-noedit" readonly></textarea>
</body>
</html>

View File

@ -24,6 +24,16 @@ Feature: Downloading things from a website.
And I wait for the error "Download error: * - server replied: NOT FOUND"
Then no crash should happen
Scenario: Downloading a link without path information (issue 1243)
When I set completion -> download-path-suggestion to filename
And I set storage -> prompt-download-directory to true
And I open data/downloads/issue1243.html
And I run :hint links download
And I run :follow-hint a
And I wait for "Asking question <qutebrowser.utils.usertypes.Question default='qutebrowser-download' mode=<PromptMode.text: 2> text='Save file to:'>, *" in the log
And I run :leave-mode
Then no crash should happen
Scenario: Retrying a failed download
When I run :download http://localhost:(port)/does-not-exist
And I wait for the error "Download error: * - server replied: NOT FOUND"

View File

@ -178,6 +178,15 @@ Feature: Prompts
And I run :leave-mode
Then the javascript message "notification permission aborted" should be logged
Scenario: answering notification after closing tab
When I set content -> notifications to ask
And I open data/prompt/notifications.html in a new tab
And I click the button
And I wait for a prompt
And I run :tab-close
And I wait for "Leaving mode KeyMode.yesno (reason: aborted)" in the log
Then no crash should happen
# Page authentication
Scenario: Successful webpage authentification

View File

@ -38,3 +38,16 @@ def skip_with_broken_clipboard(qtbot, qapp):
if clipboard.text() != "Does this work?":
pytest.skip("Clipboard seems to be broken on this platform.")
@bdd.when(bdd.parsers.parse('I set the text field to "{value}"'))
def set_text_field(quteproc, value):
quteproc.send_cmd(":jseval document.getElementById('qute-textarea').value "
"= '{}';".format(value))
@bdd.then(bdd.parsers.parse('the text field should contain "{value}"'))
def check_text_field(quteproc, value):
quteproc.send_cmd(":jseval console.log('text: ' + "
"document.getElementById('qute-textarea').value);")
quteproc.wait_for_js('text: ' + value)

View File

@ -170,3 +170,67 @@ Feature: Yanking and pasting.
history:
- active: true
url: http://localhost:*/data/hello3.txt
#### :paste-primary
Scenario: Pasting the primary selection into an empty text field
When selection is supported
And I open data/paste_primary.html
And I put "Hello world" into the primary selection
# Click the text field
And I run :hint all
And I run :follow-hint a
And I run :paste-primary
# Compare
Then the text field should contain "Hello world"
Scenario: Pasting the primary selection into a text field at specific position
When selection is supported
And I open data/paste_primary.html
And I set the text field to "one two three four"
And I put " Hello world" into the primary selection
# Click the text field
And I run :hint all
And I run :follow-hint a
# Move to the beginning and two words to the right
And I press the keys "<Home>"
And I press the key "<Ctrl+Right>"
And I press the key "<Ctrl+Right>"
And I run :paste-primary
# Compare
Then the text field should contain "one two Hello world three four"
Scenario: Pasting the primary selection into a text field with undo
When selection is supported
And I open data/paste_primary.html
# Click the text field
And I run :hint all
And I run :follow-hint a
# Paste and undo
And I put "This text should be undone" into the primary selection
And I run :paste-primary
And I press the key "<Ctrl+z>"
# Paste final text
And I put "This text should stay" into the primary selection
And I run :paste-primary
# Compare
Then the text field should contain "This text should stay"
Scenario: Pasting the primary selection without a focused field
When selection is supported
And I open data/paste_primary.html
And I put "test" into the primary selection
And I run :enter-mode insert
And I run :paste-primary
Then the error "No element focused!" should be shown
Scenario: Pasting the primary selection with a read-only field
When selection is supported
And I open data/paste_primary.html
# Click the text field
And I run :hint all
And I run :follow-hint s
And I put "test" into the primary selection
And I run :enter-mode insert
And I run :paste-primary
Then the error "Focused element is not editable!" should be shown

View File

@ -599,7 +599,7 @@ class TestJavascriptEscape:
# http://qutebrowser.org:8010/builders/debian-jessie/builds/765/steps/unittests/
# Should that be ignored?
@pytest.mark.parametrize('before, after', TESTS.items(), ids=repr)
@pytest.mark.parametrize('before, after', sorted(TESTS.items()), ids=repr)
def test_fake_escape(self, before, after):
"""Test javascript escaping with some expected outcomes."""
assert webelem.javascript_escape(before) == after
@ -645,7 +645,7 @@ class TestJavascriptEscape:
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
assert result == text
@pytest.mark.parametrize('text', TESTS, ids=repr)
@pytest.mark.parametrize('text', sorted(TESTS), ids=repr)
def test_real_escape(self, webframe, qtbot, text):
"""Test javascript escaping with a real QWebPage."""
self._test_escape(text, qtbot, webframe)

View File

@ -189,7 +189,7 @@ class TestConfigParser:
assert new_sect in configdata.DATA
@pytest.mark.parametrize('old_tuple, new_option',
config.ConfigManager.RENAMED_OPTIONS.items())
sorted(config.ConfigManager.RENAMED_OPTIONS.items()))
def test_renamed_options(self, old_tuple, new_option):
"""Make sure renamed options exist under the new name."""
section, old_option = old_tuple

View File

@ -254,7 +254,7 @@ class TestMappingType:
def klass(self):
return MappingSubclass
@pytest.mark.parametrize('val', TESTS.keys())
@pytest.mark.parametrize('val', sorted(TESTS.keys()))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -263,7 +263,7 @@ class TestMappingType:
with pytest.raises(configexc.ValidationError):
klass().validate(val)
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@ -488,11 +488,11 @@ class TestBool:
def klass(self):
return configtypes.Bool
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -518,11 +518,11 @@ class TestBoolAsk:
def klass(self):
return configtypes.BoolAsk
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -871,7 +871,7 @@ class TestColorSystem:
def klass(self):
return configtypes.ColorSystem
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -880,7 +880,7 @@ class TestColorSystem:
with pytest.raises(configexc.ValidationError):
klass().validate(val)
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@ -1048,7 +1048,7 @@ class TestFont:
def qtfont_class(self):
return configtypes.QtFont
@pytest.mark.parametrize('val', list(TESTS) + [''])
@pytest.mark.parametrize('val', sorted(list(TESTS)) + [''])
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -1077,11 +1077,11 @@ class TestFont:
with pytest.raises(configexc.ValidationError):
klass().validate('')
@pytest.mark.parametrize('string', TESTS)
@pytest.mark.parametrize('string', sorted(TESTS))
def test_transform_font(self, font_class, string):
assert font_class().transform(string) == string
@pytest.mark.parametrize('string, desc', TESTS.items())
@pytest.mark.parametrize('string, desc', sorted(TESTS.items()))
def test_transform_qtfont(self, qtfont_class, string, desc):
assert Font(qtfont_class().transform(string)) == Font.fromdesc(desc)
@ -1828,7 +1828,7 @@ class TestAutoSearch:
def klass(self):
return configtypes.AutoSearch
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -1837,7 +1837,7 @@ class TestAutoSearch:
with pytest.raises(configexc.ValidationError):
klass().validate(val)
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@ -1858,7 +1858,7 @@ class TestIgnoreCase:
def klass(self):
return configtypes.IgnoreCase
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -1867,7 +1867,7 @@ class TestIgnoreCase:
with pytest.raises(configexc.ValidationError):
klass().validate(val)
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected
@ -1909,7 +1909,7 @@ class TestUrlList:
def klass(self):
return configtypes.UrlList
@pytest.mark.parametrize('val', TESTS)
@pytest.mark.parametrize('val', sorted(TESTS))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -1927,7 +1927,7 @@ class TestUrlList:
with pytest.raises(configexc.ValidationError):
klass().validate('foo,,bar')
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform_single(self, klass, val, expected):
assert klass().transform(val) == expected
@ -1967,7 +1967,7 @@ class TestConfirmQuit:
def klass(self):
return configtypes.ConfirmQuit
@pytest.mark.parametrize('val', TESTS.keys())
@pytest.mark.parametrize('val', sorted(TESTS.keys()))
def test_validate_valid(self, klass, val):
klass(none_ok=True).validate(val)
@ -1984,7 +1984,7 @@ class TestConfirmQuit:
with pytest.raises(configexc.ValidationError):
klass().validate(val)
@pytest.mark.parametrize('val, expected', TESTS.items())
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
def test_transform(self, klass, val, expected):
assert klass().transform(val) == expected

View File

@ -94,7 +94,7 @@ class TestParseFatalStacktrace:
"QT_IM_MODULE = fcitx"
),
({'LANGUAGE': 'foo', 'LANG': 'en_US.UTF-8'}, "LANG = en_US.UTF-8"),
], ids=repr)
], ids=lambda e: e[1])
def test_get_environment_vars(monkeypatch, env, expected):
"""Test for crashdialog._get_environment_vars."""
for key in os.environ.copy():

View File

@ -126,7 +126,8 @@ class TestSplit:
"""Test split."""
@pytest.fixture(params=_parse_split_test_data_str(), ids=lambda e: e.input)
@pytest.fixture(params=list(_parse_split_test_data_str()),
ids=lambda e: e.input)
def split_test_case(self, request):
"""Fixture to automatically parametrize all depending tests.
@ -163,7 +164,7 @@ class TestSimpleSplit:
'foo\nbar': ['foo', '\nbar'],
}
@pytest.mark.parametrize('test', TESTS, ids=repr)
@pytest.mark.parametrize('test', sorted(TESTS), ids=repr)
def test_str_split(self, test):
"""Test if the behavior matches str.split."""
assert split.simple_split(test) == test.rstrip().split()
@ -177,7 +178,7 @@ class TestSimpleSplit:
expected = s.rstrip().split(maxsplit=maxsplit)
assert actual == expected
@pytest.mark.parametrize('test, expected', TESTS.items(), ids=repr)
@pytest.mark.parametrize('test, expected', sorted(TESTS.items()), ids=repr)
def test_split_keep(self, test, expected):
"""Test splitting with keep=True."""
assert split.simple_split(test, keep=True) == expected

View File

@ -49,12 +49,20 @@ BAD_VALUES = {
}
def good_values():
return list(iter_good_values())
def bad_values():
return list(iter_bad_values())
def iter_good_values():
"""Yield "good" (C data type, value) tuples.
Those should pass overflow checking.
"""
for ctype, values in GOOD_VALUES.items():
for ctype, values in sorted(GOOD_VALUES.items()):
for value in values:
yield ctype, value
@ -65,6 +73,6 @@ def iter_bad_values():
These should not pass overflow checking. The third value is the value they
should be replaced with if overflow checking should not be fatal.
"""
for ctype, values in BAD_VALUES.items():
for ctype, values in sorted(BAD_VALUES.items()):
for value, repl in values:
yield ctype, value, repl

View File

@ -69,21 +69,21 @@ class TestCheckOverflow:
"""Test check_overflow."""
@pytest.mark.parametrize('ctype, val',
overflow_test_cases.iter_good_values())
overflow_test_cases.good_values())
def test_good_values(self, ctype, val):
"""Test values which are inside bounds."""
qtutils.check_overflow(val, ctype)
@pytest.mark.parametrize('ctype, val',
[(ctype, val) for (ctype, val, _) in
overflow_test_cases.iter_bad_values()])
overflow_test_cases.bad_values()])
def test_bad_values_fatal(self, ctype, val):
"""Test values which are outside bounds with fatal=True."""
with pytest.raises(OverflowError):
qtutils.check_overflow(val, ctype)
@pytest.mark.parametrize('ctype, val, repl',
overflow_test_cases.iter_bad_values())
overflow_test_cases.bad_values())
def test_bad_values_nonfatal(self, ctype, val, repl):
"""Test values which are outside bounds with fatal=False."""
newval = qtutils.check_overflow(val, ctype, fatal=False)

View File

@ -805,7 +805,8 @@ QUALNAME_OBJ = QualnameObj()
(qutebrowser, 'qutebrowser'), # module
(qutebrowser.utils, 'qutebrowser.utils'), # submodule
(utils, 'qutebrowser.utils.utils'), # submodule (from-import)
])
], ids=['instance', 'class', 'unbound-method', 'bound-method', 'function',
'partial', 'module', 'submodule', 'from-import'])
def test_qualname(obj, expected):
assert utils.qualname(obj) == expected

33
tox.ini
View File

@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
envlist = py34,py35-cov,misc,vulture,pep257,flake8,pylint,pyroma,check-manifest
envlist = py34,py35-cov,misc,vulture,pydocstyle,flake8,pylint,pyroma,check-manifest
[testenv]
# https://bitbucket.org/hpk42/tox/issue/246/ - only needed for Windows though
@ -19,7 +19,7 @@ deps =
decorator==4.0.6
Flask==0.10.1
glob2==0.4.1
httpbin==0.4.0
httpbin==0.4.1
hypothesis==2.0.0
itsdangerous==0.24
Mako==1.0.3
@ -29,10 +29,10 @@ deps =
pytest==2.8.7
pytest-bdd==2.16.0
pytest-catchlog==1.2.2
pytest-cov==2.2.0
pytest-cov==2.2.1
pytest-faulthandler==1.3.0
pytest-html==1.7
pytest-mock==0.9.0
pytest-mock==0.10.1
pytest-qt==1.11.0
pytest-instafail==0.3.0
pytest-travis-fold==1.2.0
@ -42,7 +42,7 @@ deps =
vulture==0.8.1
Werkzeug==0.11.3
wheel==0.26.0
xvfbwrapper==0.2.7
xvfbwrapper==0.2.8
cherrypy==4.0.0
commands =
{envpython} scripts/link_pyqt.py --tox {envdir}
@ -150,34 +150,25 @@ commands =
{envpython} -m pylint scripts qutebrowser --output-format=colorized --reports=no
{envpython} scripts/dev/run_pylint_on_tests.py --output-format=colorized --reports=no
[testenv:pep257]
[testenv:pydocstyle]
basepython = python3
skip_install = true
passenv = PYTHON LANG
deps = pep257==0.7.0
# Disabled checks:
# D102: Missing docstring in public method (will be handled by others)
# D103: Missing docstring in public function (will be handled by others)
# D104: Missing docstring in public package (will be handled by others)
# D105: Missing docstring in magic method (will be handled by others)
# D209: Blank line before closing """ (removed from PEP257)
# D211: Now b lank lines allowed before class docstring
# (PEP257 got changed, but let's stick to the old standard)
# D402: First line should not be function's signature (false-positives)
commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D103,D104,D105,D209,D211,D402 '--match=(?!resources|test_*).*\.py'
deps = pydocstyle==1.0.0
commands = {envpython} -m pydocstyle scripts tests qutebrowser
[testenv:flake8]
basepython = python3
passenv =
deps =
-r{toxinidir}/requirements.txt
flake8==2.5.1
flake8==2.5.2
flake8-debugger==1.4.0
pep8-naming==0.3.3
flake8-putty==0.2.0
ebb-lint==0.4.3
ebb-lint==0.4.4
flake8-copyright==0.1
mccabe==0.3.1
mccabe==0.4.0
pep8==1.7.0
pyflakes==1.0.0
flake8-string-format==0.2.1
@ -202,7 +193,7 @@ basepython = python3
skip_install = true
passenv =
deps =
check-manifest==0.30
check-manifest==0.31
commands =
{envdir}/bin/check-manifest --ignore 'qutebrowser/git-commit-id,qutebrowser/html/doc,qutebrowser/html/doc/*,*/__pycache__'