Merge branch 'master' into es6ify-js

This commit is contained in:
plexigras 2017-11-02 16:44:25 +01:00 committed by GitHub
commit 6e624bcd3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 272 additions and 115 deletions

View File

@ -51,7 +51,7 @@ matrix:
env: TESTENV=eslint
language: node_js
python: null
node_js: node
node_js: "lts/*"
fast_finish: true
cache:

View File

@ -34,6 +34,8 @@ Added
widget.
- `:edit-url` now handles the `--private` and `--related` flags, which have the
same effect they have with `:open`.
- New `{line}` and `{column}` replacements for `editor.command` to position the
cursor correctly.
Changed
~~~~~~~
@ -76,6 +78,8 @@ Fixed
~~~~~
- Handle accessing a locked sqlite database gracefully
- Abort pinned tab dialogs properly when a tab is closed e.g. by closing a
window.
v1.0.2
------

View File

@ -152,7 +152,7 @@ For QtWebEngine:
`:set spellcheck.languages "['en-US', 'pl-PL']"`
How do I use Tor with qutebrowser?::
Start tor on your machine, and do `:set network proxy socks://localhost:9050/`
Start tor on your machine, and do `:set content.proxy socks://localhost:9050/`
in qutebrowser. Note this won't give you the same amount of fingerprinting
protection that the Tor Browser does, but it's useful to be able to access
`.onion` sites.
@ -162,7 +162,7 @@ Why does J move to the next (right) tab, and K to the previous (left) one?::
and qutebrowser's keybindings are designed to be compatible with dwb's.
The rationale behind it is that J is "down" in vim, and K is "up", which
corresponds nicely to "next"/"previous". It also makes much more sense with
vertical tabs (e.g. `:set tabs position left`).
vertical tabs (e.g. `:set tabs.position left`).
What's the difference between insert and passthrough mode?::
They are quite similar, but insert mode has some bindings (like `Ctrl-e` to

View File

@ -1961,7 +1961,14 @@ Default: +pass:[-1]+
[[editor.command]]
=== editor.command
The editor (and arguments) to use for the `open-editor` command.
`{}` gets replaced by the filename of the file to be edited.
Several placeholders are supported. Placeholders are substituted by the
respective value when executing the command.
`{file}` gets replaced by the filename of the file to be edited.
`{line}` gets replaced by the line in which the caret is found in the text.
`{column}` gets replaced by the column in which the caret is found in the text.
`{line0}` same as `{line}`, but starting from index 0.
`{column0}` same as `{column}`, but starting from index 0.
Type: <<types,ShellCommand>>

View File

@ -660,9 +660,9 @@ class Quitter:
try:
args, cwd = self._get_restart_args(pages, session, override_args)
if cwd is None:
subprocess.Popen(args)
subprocess.run(args)
else:
subprocess.Popen(args, cwd=cwd)
subprocess.run(args, cwd=cwd)
except OSError:
log.destroy.exception("Failed to restart")
return False

View File

@ -1618,10 +1618,12 @@ class CommandDispatcher:
return
assert isinstance(text, str), text
caret_position = elem.caret_position()
ed = editor.ExternalEditor(self._tabbed_browser)
ed.editing_finished.connect(functools.partial(
self.on_editing_finished, elem))
ed.edit(text)
ed.edit(text, caret_position)
@cmdutils.register(instance='command-dispatcher', hide=True,
scope='window')

View File

@ -47,6 +47,7 @@ class WebEngineElement(webelem.AbstractWebElement):
'class_name': str,
'rects': list,
'attributes': dict,
'caret_position': int,
}
assert set(js_dict.keys()).issubset(js_dict_types.keys())
for name, typ in js_dict_types.items():
@ -132,6 +133,10 @@ class WebEngineElement(webelem.AbstractWebElement):
def set_value(self, value):
self._js_call('set_value', value)
def caret_position(self):
"""Get the text caret position for the current element."""
return self._js_dict.get('caret_position', 0)
def insert_text(self, text):
if not self.is_editable(strict=True):
raise webelem.Error("Element is not editable!")

View File

@ -126,6 +126,14 @@ class WebKitElement(webelem.AbstractWebElement):
value = javascript.string_escape(value)
self._elem.evaluateJavaScript("this.value='{}'".format(value))
def caret_position(self):
"""Get the text caret position for the current element."""
self._check_vanished()
pos = self._elem.evaluateJavaScript('this.selectionStart')
if pos is None:
return 0
return int(pos)
def insert_text(self, text):
self._check_vanished()
if not self.is_editable(strict=True):

View File

@ -766,11 +766,11 @@ editor.command:
type:
name: ShellCommand
placeholder: true
default: ["gvim", "-f", "{}"]
default: ["gvim", "-f", "{file}", "-c", "normal {line}G{column0}l"]
desc: >-
The editor (and arguments) to use for the `open-editor` command.
`{}` gets replaced by the filename of the file to be edited.
`{file}` gets replaced by the filename of the file to be edited.
editor.encoding:
type: Encoding

View File

@ -755,6 +755,6 @@ def get_diff():
lexer = pygments.lexers.DiffLexer()
formatter = pygments.formatters.HtmlFormatter(
full=True, linenos='table',
title='Config diff')
title='Diffing pre-1.0 default config with pre-1.0 modified config')
# pylint: enable=no-member
return pygments.highlight(conf_diff + key_diff, lexer, formatter)

View File

@ -1341,9 +1341,12 @@ class ShellCommand(List):
if not value:
return value
if self.placeholder and '{}' not in ' '.join(value):
if (self.placeholder and
'{}' not in ' '.join(value) and
'{file}' not in ' '.join(value)):
raise configexc.ValidationError(value, "needs to contain a "
"{}-placeholder.")
"{}-placeholder or a "
"{file}-placeholder.")
return value

View File

@ -48,11 +48,26 @@ window._qutebrowser.webelem = (function() {
const id = elements.length;
elements[id] = elem;
// InvalidStateError will be thrown if elem doesn't have selectionStart
let caret_position = 0;
try {
caret_position = elem.selectionStart;
} catch (err) {
if (err instanceof DOMException &&
err.name === "InvalidStateError") {
// nothing to do, caret_position is already 0
} else {
// not the droid we're looking for
throw err;
}
}
const out = {
"id": id,
"value": elem.value,
"outer_xml": elem.outerHTML,
"rects": [], // Gets filled up later
"caret_position": caret_position,
};
// https://github.com/qutebrowser/qutebrowser/issues/2569

View File

@ -259,13 +259,14 @@ class TabbedBrowser(tabwidget.TabWidget):
def tab_close_prompt_if_pinned(self, tab, force, yes_action):
"""Helper method for tab_close.
If tab is pinned, prompt. If everything is good, run yes_action.
If tab is pinned, prompt. If not, run yes_action.
If tab is destroyed, abort question.
"""
if tab.data.pinned and not force:
message.confirm_async(
title='Pinned Tab',
text="Are you sure you want to close a pinned tab?",
yes_action=yes_action, default=False)
yes_action=yes_action, default=False, abort_on=[tab.destroyed])
else:
yes_action()

View File

@ -96,11 +96,12 @@ class ExternalEditor(QObject):
def on_proc_error(self, _err):
self._cleanup()
def edit(self, text):
def edit(self, text, caret_position=0):
"""Edit a given text.
Args:
text: The initial text to edit.
caret_position: The position of the caret in the text.
"""
if self._filename is not None:
raise ValueError("Already editing a file!")
@ -121,7 +122,9 @@ class ExternalEditor(QObject):
return
self._remove_file = True
self._start_editor()
line, column = self._calc_line_and_column(text, caret_position)
self._start_editor(line=line, column=column)
def edit_file(self, filename):
"""Edit the file with the given filename."""
@ -129,13 +132,82 @@ class ExternalEditor(QObject):
self._remove_file = False
self._start_editor()
def _start_editor(self):
"""Start the editor with the file opened as self._filename."""
def _start_editor(self, line=1, column=1):
"""Start the editor with the file opened as self._filename.
Args:
line: the line number to pass to the editor
column: the column number to pass to the editor
"""
self._proc = guiprocess.GUIProcess(what='editor', parent=self)
self._proc.finished.connect(self.on_proc_closed)
self._proc.error.connect(self.on_proc_error)
editor = config.val.editor.command
executable = editor[0]
args = [arg.replace('{}', self._filename) for arg in editor[1:]]
args = [self._sub_placeholder(arg, line, column) for arg in editor[1:]]
log.procs.debug("Calling \"{}\" with args {}".format(executable, args))
self._proc.start(executable, args)
def _calc_line_and_column(self, text, caret_position):
r"""Calculate line and column numbers given a text and caret position.
Both line and column are 1-based indexes, because that's what most
editors use as line and column starting index. By "most" we mean at
least vim, nvim, gvim, emacs, atom, sublimetext, notepad++, brackets,
visual studio, QtCreator and so on.
To find the line we just count how many newlines there are before the
caret and add 1.
To find the column we calculate the difference between the caret and
the last newline before the caret.
For example in the text `aaa\nbb|bbb` (| represents the caret):
caret_position = 6
text[:caret_position] = `aaa\nbb`
text[:caret_position].count('\n') = 1
caret_position - text[:caret_position].rfind('\n') = 3
Thus line, column = 2, 3, and the caret is indeed in the second
line, third column
Args:
text: the text for which the numbers must be calculated
caret_position: the position of the caret in the text
Return:
A (line, column) tuple of (int, int)
"""
line = text[:caret_position].count('\n') + 1
column = caret_position - text[:caret_position].rfind('\n')
return line, column
def _sub_placeholder(self, arg, line, column):
"""Substitute a single placeholder.
If the `arg` input to this function is a valid placeholder it will
be substituted with the appropriate value, otherwise it will be left
unchanged.
Args:
arg: an argument of editor.command.
line: the previously-calculated line number for the text caret.
column: the previously-calculated column number for the text caret.
Return:
The substituted placeholder or the original argument.
"""
replacements = {
'{}': self._filename,
'{file}': self._filename,
'{line}': str(line),
'{line0}': str(line-1),
'{column}': str(column),
'{column0}': str(column-1)
}
for old, new in replacements.items():
arg = arg.replace(old, new)
return arg

View File

@ -151,12 +151,14 @@ def _git_str_subprocess(gitpath):
return None
try:
# https://stackoverflow.com/questions/21017300/21017394#21017394
commit_hash = subprocess.check_output(
commit_hash = subprocess.run(
['git', 'describe', '--match=NeVeRmAtCh', '--always', '--dirty'],
cwd=gitpath).decode('UTF-8').strip()
date = subprocess.check_output(
cwd=gitpath, check=True,
stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
date = subprocess.run(
['git', 'show', '-s', '--format=%ci', 'HEAD'],
cwd=gitpath).decode('UTF-8').strip()
cwd=gitpath, check=True,
stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
return '{} ({})'.format(commit_hash, date)
except (subprocess.CalledProcessError, OSError):
return None

View File

@ -224,16 +224,16 @@ class AsciiDoc:
return self._args.asciidoc
try:
subprocess.call(['asciidoc'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
pass
else:
return ['asciidoc']
try:
subprocess.call(['asciidoc.py'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
pass
else:
@ -258,7 +258,7 @@ class AsciiDoc:
try:
env = os.environ.copy()
env['HOME'] = self._homedir
subprocess.check_call(cmdline, env=env)
subprocess.run(cmdline, check=True, env=env)
except (subprocess.CalledProcessError, OSError) as e:
self._failed = True
utils.print_col(str(e), 'red')

View File

@ -50,7 +50,7 @@ def call_script(name, *args, python=sys.executable):
python: The python interpreter to use.
"""
path = os.path.join(os.path.dirname(__file__), os.pardir, name)
subprocess.check_call([python, path] + list(args))
subprocess.run([python, path] + list(args), check=True)
def call_tox(toxenv, *args, python=sys.executable):
@ -64,9 +64,9 @@ def call_tox(toxenv, *args, python=sys.executable):
env = os.environ.copy()
env['PYTHON'] = python
env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python)
subprocess.check_call(
subprocess.run(
[sys.executable, '-m', 'tox', '-vv', '-e', toxenv] + list(args),
env=env)
env=env, check=True)
def run_asciidoc2html(args):
@ -89,8 +89,9 @@ def _maybe_remove(path):
def smoke_test(executable):
"""Try starting the given qutebrowser executable."""
subprocess.check_call([executable, '--no-err-windows', '--nowindow',
'--temp-basedir', 'about:blank', ':later 500 quit'])
subprocess.run([executable, '--no-err-windows', '--nowindow',
'--temp-basedir', 'about:blank', ':later 500 quit'],
check=True)
def patch_mac_app():
@ -178,7 +179,7 @@ def build_mac():
utils.print_title("Patching .app")
patch_mac_app()
utils.print_title("Building .dmg")
subprocess.check_call(['make', '-f', 'scripts/dev/Makefile-dmg'])
subprocess.run(['make', '-f', 'scripts/dev/Makefile-dmg'], check=True)
dmg_name = 'qutebrowser-{}.dmg'.format(qutebrowser.__version__)
os.rename('qutebrowser.dmg', dmg_name)
@ -187,14 +188,14 @@ def build_mac():
try:
with tempfile.TemporaryDirectory() as tmpdir:
subprocess.check_call(['hdiutil', 'attach', dmg_name,
'-mountpoint', tmpdir])
subprocess.run(['hdiutil', 'attach', dmg_name,
'-mountpoint', tmpdir], check=True)
try:
binary = os.path.join(tmpdir, 'qutebrowser.app', 'Contents',
'MacOS', 'qutebrowser')
smoke_test(binary)
finally:
subprocess.call(['hdiutil', 'detach', tmpdir])
subprocess.run(['hdiutil', 'detach', tmpdir])
except PermissionError as e:
print("Failed to remove tempdir: {}".format(e))
@ -242,13 +243,13 @@ def build_windows():
patch_windows(out_64)
utils.print_title("Building installers")
subprocess.check_call(['makensis.exe',
'/DVERSION={}'.format(qutebrowser.__version__),
'misc/qutebrowser.nsi'])
subprocess.check_call(['makensis.exe',
'/DX64',
'/DVERSION={}'.format(qutebrowser.__version__),
'misc/qutebrowser.nsi'])
subprocess.run(['makensis.exe',
'/DVERSION={}'.format(qutebrowser.__version__),
'misc/qutebrowser.nsi'], check=True)
subprocess.run(['makensis.exe',
'/DX64',
'/DVERSION={}'.format(qutebrowser.__version__),
'misc/qutebrowser.nsi'], check=True)
name_32 = 'qutebrowser-{}-win32.exe'.format(qutebrowser.__version__)
name_64 = 'qutebrowser-{}-amd64.exe'.format(qutebrowser.__version__)
@ -292,12 +293,12 @@ def build_sdist():
_maybe_remove('dist')
subprocess.check_call([sys.executable, 'setup.py', 'sdist'])
subprocess.run([sys.executable, 'setup.py', 'sdist'], check=True)
dist_files = os.listdir(os.path.abspath('dist'))
assert len(dist_files) == 1
dist_file = os.path.join('dist', dist_files[0])
subprocess.check_call(['gpg', '--detach-sign', '-a', dist_file])
subprocess.run(['gpg', '--detach-sign', '-a', dist_file], check=True)
tar = tarfile.open(dist_file)
by_ext = collections.defaultdict(list)
@ -366,7 +367,7 @@ def github_upload(artifacts, tag):
def pypi_upload(artifacts):
"""Upload the given artifacts to PyPI using twine."""
filenames = [a[0] for a in artifacts]
subprocess.check_call(['twine', 'upload'] + filenames)
subprocess.run(['twine', 'upload'] + filenames, check=True)
def main():

View File

@ -285,8 +285,8 @@ def main_check():
print(msg.text)
print()
filters = ','.join('qutebrowser/' + msg.filename for msg in messages)
subprocess.check_call([sys.executable, '-m', 'coverage', 'report',
'--show-missing', '--include', filters])
subprocess.run([sys.executable, '-m', 'coverage', 'report',
'--show-missing', '--include', filters], check=True)
print()
print("To debug this, run 'tox -e py36-pyqt59-cov' "
"(or py35-pyqt59-cov) locally and check htmlcov/index.html")
@ -312,9 +312,9 @@ def main_check_all():
for test_file, src_file in PERFECT_FILES:
if test_file is None:
continue
subprocess.check_call(
subprocess.run(
[sys.executable, '-m', 'pytest', '--cov', 'qutebrowser',
'--cov-report', 'xml', test_file])
'--cov-report', 'xml', test_file], check=True)
with open('coverage.xml', encoding='utf-8') as f:
messages = check(f, [(test_file, src_file)])
os.remove('coverage.xml')

View File

@ -24,7 +24,8 @@ import sys
import subprocess
import os
code = subprocess.call(['git', '--no-pager', 'diff', '--exit-code', '--stat'])
code = subprocess.run(['git', '--no-pager', 'diff',
'--exit-code', '--stat']).returncode
if os.environ.get('TRAVIS_PULL_REQUEST', 'false') != 'false':
if code != 0:
@ -42,6 +43,6 @@ if code != 0:
if 'TRAVIS' in os.environ:
print()
print("travis_fold:start:gitdiff")
subprocess.call(['git', '--no-pager', 'diff'])
subprocess.run(['git', '--no-pager', 'diff'])
print("travis_fold:end:gitdiff")
sys.exit(code)

View File

@ -23,4 +23,4 @@
import subprocess
with open('qutebrowser/resources.py', 'w', encoding='utf-8') as f:
subprocess.check_call(['pyrcc5', 'qutebrowser.rcc'], stdout=f)
subprocess.run(['pyrcc5', 'qutebrowser.rcc'], stdout=f, check=True)

View File

@ -84,7 +84,8 @@ def parse_coredumpctl_line(line):
def get_info(pid):
"""Get and parse "coredumpctl info" output for the given PID."""
data = {}
output = subprocess.check_output(['coredumpctl', 'info', str(pid)])
output = subprocess.run(['coredumpctl', 'info', str(pid)], check=True,
stdout=subprocess.PIPE).stdout
output = output.decode('utf-8')
for line in output.split('\n'):
if not line.strip():
@ -117,12 +118,12 @@ def dump_infos_gdb(parsed):
"""Dump all needed infos for the given crash using gdb."""
with tempfile.TemporaryDirectory() as tempdir:
coredump = os.path.join(tempdir, 'dump')
subprocess.check_call(['coredumpctl', 'dump', '-o', coredump,
str(parsed.pid)])
subprocess.check_call(['gdb', parsed.exe, coredump,
'-ex', 'info threads',
'-ex', 'thread apply all bt full',
'-ex', 'quit'])
subprocess.run(['coredumpctl', 'dump', '-o', coredump,
str(parsed.pid)], check=True)
subprocess.run(['gdb', parsed.exe, coredump,
'-ex', 'info threads',
'-ex', 'thread apply all bt full',
'-ex', 'quit'], check=True)
def dump_infos(parsed):
@ -143,7 +144,7 @@ def check_prerequisites():
"""Check if coredumpctl/gdb are installed."""
for binary in ['coredumpctl', 'gdb']:
try:
subprocess.check_call([binary, '--version'])
subprocess.run([binary, '--version'], check=True)
except FileNotFoundError:
print("{} is needed to run this script!".format(binary),
file=sys.stderr)
@ -158,7 +159,8 @@ def main():
action='store_true')
args = parser.parse_args()
coredumps = subprocess.check_output(['coredumpctl', 'list'])
coredumps = subprocess.run(['coredumpctl', 'list'], check=True,
stdout=subprocess.PIPE).stdout
lines = coredumps.decode('utf-8').split('\n')
for line in lines[1:]:
if not line.strip():

View File

@ -62,7 +62,8 @@ def check_git():
print()
return False
untracked = []
gitst = subprocess.check_output(['git', 'status', '--porcelain'])
gitst = subprocess.run(['git', 'status', '--porcelain'], check=True,
stdout=subprocess.PIPE).stdout
gitst = gitst.decode('UTF-8').strip()
for line in gitst.splitlines():
s, name = line.split(maxsplit=1)

View File

@ -116,9 +116,11 @@ def main():
with tempfile.TemporaryDirectory() as tmpdir:
pip_bin = os.path.join(tmpdir, 'bin', 'pip')
subprocess.check_call(['virtualenv', tmpdir])
subprocess.check_call([pip_bin, 'install', '-r', filename])
reqs = subprocess.check_output([pip_bin, 'freeze']).decode('utf-8')
subprocess.run(['virtualenv', tmpdir], check=True)
subprocess.run([pip_bin, 'install', '-r', filename], check=True)
reqs = subprocess.run([pip_bin, 'freeze'], check=True,
stdout=subprocess.PIPE
).stdout.decode('utf-8')
with open(filename, 'r', encoding='utf-8') as f:
comments = read_comments(f)

View File

@ -76,14 +76,15 @@ def main():
pass
elif args.profile_tool == 'gprof2dot':
# yep, shell=True. I know what I'm doing.
subprocess.call('gprof2dot -f pstats {} | dot -Tpng | feh -F -'.format(
shlex.quote(profilefile)), shell=True)
subprocess.run(
'gprof2dot -f pstats {} | dot -Tpng | feh -F -'.format(
shlex.quote(profilefile)), shell=True)
elif args.profile_tool == 'kcachegrind':
callgraphfile = os.path.join(tempdir, 'callgraph')
subprocess.call(['pyprof2calltree', '-k', '-i', profilefile,
'-o', callgraphfile])
subprocess.run(['pyprof2calltree', '-k', '-i', profilefile,
'-o', callgraphfile])
elif args.profile_tool == 'snakeviz':
subprocess.call(['snakeviz', profilefile])
subprocess.run(['snakeviz', profilefile])
shutil.rmtree(tempdir)

View File

@ -73,7 +73,7 @@ def main():
env = os.environ.copy()
env['PYTHONPATH'] = os.pathsep.join(pythonpath)
ret = subprocess.call(['pylint'] + args, env=env)
ret = subprocess.run(['pylint'] + args, env=env).returncode
return ret

View File

@ -92,22 +92,22 @@ def main():
utils.print_bold("==== {} ====".format(page))
if test_harfbuzz:
print("With system harfbuzz:")
ret = subprocess.call([sys.executable, '-c', SCRIPT, page])
ret = subprocess.run([sys.executable, '-c', SCRIPT, page]).returncode
print_ret(ret)
retvals.append(ret)
if test_harfbuzz:
print("With QT_HARFBUZZ=old:")
env = dict(os.environ)
env['QT_HARFBUZZ'] = 'old'
ret = subprocess.call([sys.executable, '-c', SCRIPT, page],
env=env)
ret = subprocess.run([sys.executable, '-c', SCRIPT, page],
env=env).returncode
print_ret(ret)
retvals.append(ret)
print("With QT_HARFBUZZ=new:")
env = dict(os.environ)
env['QT_HARFBUZZ'] = 'new'
ret = subprocess.call([sys.executable, '-c', SCRIPT, page],
env=env)
ret = subprocess.run([sys.executable, '-c', SCRIPT, page],
env=env).returncode
print_ret(ret)
retvals.append(ret)
if all(r == 0 for r in retvals):

View File

@ -529,9 +529,9 @@ def regenerate_cheatsheet():
]
for filename, x, y in files:
subprocess.check_call(['inkscape', '-e', filename, '-b', 'white',
'-w', str(x), '-h', str(y),
'misc/cheatsheet.svg'])
subprocess.run(['inkscape', '-e', filename, '-b', 'white',
'-w', str(x), '-h', str(y),
'misc/cheatsheet.svg'], check=True)
def main():

View File

@ -46,12 +46,14 @@ def run_py(executable, *code):
f.write('\n'.join(code))
cmd = [executable, filename]
try:
ret = subprocess.check_output(cmd, universal_newlines=True)
ret = subprocess.run(cmd, universal_newlines=True, check=True,
stdout=subprocess.PIPE).stdout
finally:
os.remove(filename)
else:
cmd = [executable, '-c', '\n'.join(code)]
ret = subprocess.check_output(cmd, universal_newlines=True)
ret = subprocess.run(cmd, universal_newlines=True, check=True,
stdout=subprocess.PIPE).stdout
return ret.rstrip()

View File

@ -51,12 +51,14 @@ def _git_str():
return None
try:
# https://stackoverflow.com/questions/21017300/21017394#21017394
commit_hash = subprocess.check_output(
commit_hash = subprocess.run(
['git', 'describe', '--match=NeVeRmAtCh', '--always', '--dirty'],
cwd=BASEDIR).decode('UTF-8').strip()
date = subprocess.check_output(
cwd=BASEDIR, check=True,
stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
date = subprocess.run(
['git', 'show', '-s', '--format=%ci', 'HEAD'],
cwd=BASEDIR).decode('UTF-8').strip()
cwd=BASEDIR, check=True,
stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
return '{} ({})'.format(commit_hash, date)
except (subprocess.CalledProcessError, OSError):
return None

View File

@ -47,10 +47,10 @@ def update_documentation():
return
try:
subprocess.call(['asciidoc'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
pytest.skip("Docs outdated and asciidoc unavailable!")
update_script = os.path.join(script_path, 'asciidoc2html.py')
subprocess.call([sys.executable, update_script])
subprocess.run([sys.executable, update_script])

View File

@ -259,14 +259,13 @@ def test_command_on_start(request, quteproc_new):
def test_launching_with_python2():
try:
proc = subprocess.Popen(['python2', '-m', 'qutebrowser',
'--no-err-windows'], stderr=subprocess.PIPE)
proc = subprocess.run(['python2', '-m', 'qutebrowser',
'--no-err-windows'], stderr=subprocess.PIPE)
except FileNotFoundError:
pytest.skip("python2 not found")
_stdout, stderr = proc.communicate()
assert proc.returncode == 1
error = "At least Python 3.5 is required to run qutebrowser"
assert stderr.decode('ascii').startswith(error)
assert proc.stderr.decode('ascii').startswith(error)
def test_initial_private_browsing(request, quteproc_new):

View File

@ -60,8 +60,9 @@ class TestSet:
@pytest.mark.parametrize('option, old_value, inp, new_value', [
('url.auto_search', 'naive', 'dns', 'dns'),
# https://github.com/qutebrowser/qutebrowser/issues/2962
('editor.command', ['gvim', '-f', '{}'], '[emacs, "{}"]',
['emacs', '{}']),
('editor.command',
['gvim', '-f', '{file}', '-c', 'normal {line}G{column0}l'],
'[emacs, "{}"]', ['emacs', '{}']),
])
def test_set_simple(self, monkeypatch, commands, config_stub,
temp, option, old_value, inp, new_value):

View File

@ -1815,9 +1815,12 @@ class TestShellCommand:
@pytest.mark.parametrize('kwargs, val, expected', [
({}, '[foobar]', ['foobar']),
({'placeholder': '{}'}, '[foo, "{}", bar]', ['foo', '{}', 'bar']),
({'placeholder': '{}'}, '["foo{}bar"]', ['foo{}bar']),
({'placeholder': '{}'}, '[foo, "bar {}"]', ['foo', 'bar {}']),
({'placeholder': True}, '[foo, "{}", bar]', ['foo', '{}', 'bar']),
({'placeholder': True}, '["foo{}bar"]', ['foo{}bar']),
({'placeholder': True}, '[foo, "bar {}"]', ['foo', 'bar {}']),
({'placeholder': True}, '[f, "{file}", b]', ['f', '{file}', 'b']),
({'placeholder': True}, '["f{file}b"]', ['f{file}b']),
({'placeholder': True}, '[f, "b {file}"]', ['f', 'b {file}']),
])
def test_valid(self, klass, kwargs, val, expected):
cmd = klass(**kwargs)
@ -1825,8 +1828,15 @@ class TestShellCommand:
assert cmd.to_py(expected) == expected
@pytest.mark.parametrize('kwargs, val', [
({'placeholder': '{}'}, '[foo, bar]'),
({'placeholder': '{}'}, '[foo, "{", "}", bar'),
({'placeholder': True}, '[foo, bar]'),
({'placeholder': True}, '[foo, "{", "}", bar'),
({'placeholder': True}, '[foo, bar]'),
({'placeholder': True}, '[foo, "{fi", "le}", bar'),
# Like valid ones but with wrong placeholder
({'placeholder': True}, '[f, "{wrong}", b]'),
({'placeholder': True}, '["f{wrong}b"]'),
({'placeholder': True}, '[f, "b {wrong}"]'),
])
def test_from_str_invalid(self, klass, kwargs, val):
with pytest.raises(configexc.ValidationError):

View File

@ -36,15 +36,14 @@ TEXT = (r"At least Python 3.5 is required to run qutebrowser, but it's "
def test_python2():
"""Run checkpyver with python 2."""
try:
proc = subprocess.Popen(
proc = subprocess.run(
['python2', checkpyver.__file__, '--no-err-windows'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
except FileNotFoundError:
pytest.skip("python2 not found")
assert not stdout
stderr = stderr.decode('utf-8')
assert not proc.stdout
stderr = proc.stderr.decode('utf-8')
assert re.match(TEXT, stderr), stderr
assert proc.returncode == 1

View File

@ -177,3 +177,18 @@ def test_modify(qtbot, editor, initial_text, edited_text):
editor._proc.finished.emit(0, QProcess.NormalExit)
assert blocker.args == [edited_text]
@pytest.mark.parametrize('text, caret_position, result', [
('', 0, (1, 1)),
('a', 0, (1, 1)),
('a\nb', 1, (1, 2)),
('a\nb', 2, (2, 1)),
('a\nb', 3, (2, 2)),
('a\nbb\nccc', 4, (2, 3)),
('a\nbb\nccc', 5, (3, 1)),
('a\nbb\nccc', 8, (3, 4)),
])
def test_calculation(editor, text, caret_position, result):
"""Test calculation for line and column given text and caret_position."""
assert editor._calc_line_and_column(text, caret_position) == result

View File

@ -555,8 +555,9 @@ def test_no_qapplication(qapp, tmpdir):
pyfile = tmpdir / 'sub.py'
pyfile.write_text(textwrap.dedent(sub_code), encoding='ascii')
output = subprocess.check_output([sys.executable, str(pyfile)] + sys.path,
universal_newlines=True)
output = subprocess.run([sys.executable, str(pyfile)] + sys.path,
universal_newlines=True,
check=True, stdout=subprocess.PIPE).stdout
sub_locations = json.loads(output)
standarddir._init_dirs()

View File

@ -299,8 +299,8 @@ class TestGitStr:
def _has_git():
"""Check if git is installed."""
try:
subprocess.check_call(['git', '--version'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.run(['git', '--version'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, check=True)
except (OSError, subprocess.CalledProcessError):
return False
else:
@ -337,12 +337,13 @@ class TestGitStrSubprocess:
# If we don't call this with shell=True it might fail under
# some environments on Windows...
# http://bugs.python.org/issue24493
subprocess.check_call(
subprocess.run(
'git -C "{}" {}'.format(tmpdir, ' '.join(args)),
env=env, shell=True)
env=env, check=True, shell=True)
else:
subprocess.check_call(
['git', '-C', str(tmpdir)] + list(args), env=env)
subprocess.run(
['git', '-C', str(tmpdir)] + list(args),
check=True, env=env)
(tmpdir / 'file').write_text("Hello World!", encoding='utf-8')
_git('init')
@ -368,14 +369,14 @@ class TestGitStrSubprocess:
subprocess.CalledProcessError(1, 'foobar')
])
def test_exception(self, exc, mocker, tmpdir):
"""Test with subprocess.check_output raising an exception.
"""Test with subprocess.run raising an exception.
Args:
exc: The exception to raise.
"""
m = mocker.patch('qutebrowser.utils.version.os')
m.path.isdir.return_value = True
mocker.patch('qutebrowser.utils.version.subprocess.check_output',
mocker.patch('qutebrowser.utils.version.subprocess.run',
side_effect=exc)
ret = version._git_str_subprocess(str(tmpdir))
assert ret is None