Merge branch 'master' of https://github.com/qutebrowser/qutebrowser into stylesheet-fix

This commit is contained in:
Jay Kamat 2019-02-14 17:51:19 -08:00
commit bc29ff60b3
No known key found for this signature in database
GPG Key ID: 5D2E399600F4F7B5
37 changed files with 184 additions and 84 deletions

View File

@ -68,16 +68,3 @@ after_success:
after_failure:
- bash scripts/dev/ci/travis_backtrace.sh
notifications:
webhooks:
- https://buildtimetrend.herokuapp.com/travis
irc:
channels:
- "chat.freenode.net#qutebrowser-dev"
on_success: always
on_failure: always
skip_join: true
template:
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}"
- "%{compare_url} - %{build_url}"

View File

@ -13,7 +13,7 @@ include qutebrowser/utils/testfile
include qutebrowser/git-commit-id
include LICENSE doc/* README.asciidoc
include misc/qutebrowser.desktop
include misc/qutebrowser.appdata.xml
include misc/org.qutebrowser.qutebrowser.appdata.xml
include misc/Makefile
include requirements.txt
include tox.ini

View File

@ -25,6 +25,7 @@ Added
opened from a page should stack on each other or not.
- New `completion.open_categories` setting which allows to configure which
categories are shown in the `:open` completion, and how they are ordered.
- New `tabs.pinned.frozen` setting to allow/deny navigating in pinned tabs.
- New config manipulation commands:
* `:config-dict-add` and `:config-list-add` to a new element to a dict/list
setting.
@ -53,6 +54,11 @@ Changed
- Various small performance improvements for hints and the completion.
- The Wayland check for QtWebEngine is now disabled on Qt >= 5.11.2, as those
versions should work without any issues.
- The JavaScript `console` object is now available in PAC files.
- The metainfo file `qutebrowser.appdata.xml` is now renamed to
`org.qutebrowser.qutebrowser.appdata.xml`.
- The `qute-pass` userscript now understands domains in gpg filenames
in addition to directory names.
Fixed
~~~~~
@ -67,9 +73,13 @@ Fixed
`content.cookies.accept = no-3rdparty` from working properly on some pages
like GMail. However, the default for `content.cookies.accept` is still `all`
to be in line with what other browsers do.
- `:navigate` not incrementing in anchors or queries or anchors.
- `:navigate` not incrementing in anchors or queries.
- Crash when trying to use a proxy requiring authentication with QtWebKit.
- Slashes in search terms are now percent-escaped.
- When `scrolling.bar = True` was set in versions before v1.5.0, this now
correctly gets migrated to `always` instead of `when-searching`.
- Completion highlighting now works again on Qt 5.11.3 and 5.12.1.
- The outdated header `X-Do-Not-Track` is no longer sent.
v1.5.2
------

View File

@ -211,9 +211,10 @@ Why does J move to the next (right) tab, and K to the previous (left) one?::
What's the difference between insert and passthrough mode?::
They are quite similar, but insert mode has some bindings (like `Ctrl-e` to
open an editor) while passthrough mode only has escape bound. It might also
be useful to rebind escape to something else in passthrough mode only, to be
able to send an escape keypress to the website.
open an editor) while passthrough mode only has shift+escape bound. This is
because shift+escape is unlikely to be a useful binding to be passed to a
webpage. However, any other keys may be assigned to leaving passthrough mode
instead of shift+escape should this be desired.
Why does it take longer to open a URL in qutebrowser than in chromium?::
When opening a URL in an existing instance, the normal qutebrowser

View File

@ -396,6 +396,7 @@ Pre-built colorschemes
- A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager].
- Two implementations of the https://github.com/arcticicestudio/nord[Nord] colorscheme for qutebrowser exist: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon]
- https://github.com/evannagle/qutebrowser-dracula-theme[Dracula]
- https://github.com/jjzmajic/qutewal[Pywal theme]
Avoiding flake8 errors
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -261,6 +261,7 @@
|<<tabs.new_position.stacking,tabs.new_position.stacking>>|Stack related tabs on top of each other when opened consecutively.
|<<tabs.new_position.unrelated,tabs.new_position.unrelated>>|Position of new tabs which are not opened from another tab.
|<<tabs.padding,tabs.padding>>|Padding (in pixels) around text for tabs.
|<<tabs.pinned.frozen,tabs.pinned.frozen>>|Force pinned tabs to stay at fixed URL.
|<<tabs.pinned.shrink,tabs.pinned.shrink>>|Shrink pinned tabs down to their contents.
|<<tabs.position,tabs.position>>|Position of the tab bar.
|<<tabs.select_on_remove,tabs.select_on_remove>>|Which tab to select when the focused tab is removed.
@ -3307,6 +3308,14 @@ Default:
- +pass:[right]+: +pass:[5]+
- +pass:[top]+: +pass:[0]+
[[tabs.pinned.frozen]]
=== tabs.pinned.frozen
Force pinned tabs to stay at fixed URL.
Type: <<types,Bool>>
Default: +pass:[true]+
[[tabs.pinned.shrink]]
=== tabs.pinned.shrink
Shrink pinned tabs down to their contents.

View File

@ -102,18 +102,12 @@ $ python3 scripts/asciidoc2html.py
On Fedora
---------
NOTE: Fedora's packages used to be outdated for a long time, but are
now (November 2017) maintained and up-to-date again.
qutebrowser is available in the official repositories:
-----
# dnf install qutebrowser
-----
However, note that Fedora 25/26 won't be updated to qutebrowser v1.0, so you
might want to <<tox,install qutebrowser via tox>> instead there.
Additional hints
~~~~~~~~~~~~~~~~

View File

@ -17,8 +17,8 @@ doc/qutebrowser.1.html:
install: doc/qutebrowser.1.html
$(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS)
install -Dm644 misc/qutebrowser.appdata.xml \
"$(DESTDIR)$(DATADIR)/metainfo/qutebrowser.appdata.xml"
install -Dm644 misc/org.qutebrowser.qutebrowser.appdata.xml \
"$(DESTDIR)$(DATADIR)/metainfo/org.qutebrowser.qutebrowser.appdata.xml"
install -Dm644 doc/qutebrowser.1 \
"$(DESTDIR)$(MANDIR)/man1/qutebrowser.1"
install -Dm644 misc/qutebrowser.desktop \

View File

@ -11,7 +11,7 @@ flake8-deprecated==1.3
flake8-docstrings==1.3.0
flake8-future-import==0.4.5
flake8-mock==0.3
flake8-per-file-ignores==0.6
flake8-per-file-ignores==0.7
flake8-polyfill==1.0.2
flake8-string-format==0.2.3
flake8-tidy-imports==1.1.0

View File

@ -16,6 +16,6 @@ pytz==2018.7
requests==2.21.0
six==1.12.0
snowballstemmer==1.2.1
Sphinx==1.8.2
Sphinx==1.8.3
sphinxcontrib-websupport==1.1.0
urllib3==1.24.1

View File

@ -3,8 +3,8 @@
atomicwrites==1.2.1
attrs==18.2.0
backports.functools-lru-cache==1.5
beautifulsoup4==4.6.3
cheroot==6.5.2
beautifulsoup4==4.7.0
cheroot==6.5.3
Click==7.0
# colorama==0.4.1
coverage==4.5.2
@ -12,12 +12,12 @@ EasyProcess==0.2.5
Flask==1.0.2
glob2==0.6
hunter==2.1.0
hypothesis==3.84.5
hypothesis==3.85.2
itsdangerous==1.1.0
# Jinja2==2.10
Mako==1.0.7
# MarkupSafe==1.1.0
more-itertools==4.3.0
more-itertools==5.0.0
parse==1.9.0
parse-type==0.4.2
pluggy==0.8.0

View File

@ -64,6 +64,7 @@ die() {
javascript_escape() {
# print the first argument in an escaped way, such that it can safely
# be used within javascripts double quotes
# shellcheck disable=SC2001
sed "s,[\\\\'\"],\\\\&,g" <<< "$1"
}
@ -111,6 +112,7 @@ simplify_url() {
# are found:
no_entries_found() {
while [ 0 -eq "${#files[@]}" ] && [ -n "$simple_url" ]; do
# shellcheck disable=SC2001
shorter_simple_url=$(sed 's,^[^.]*\.,,' <<< "$simple_url")
if [ "$shorter_simple_url" = "$simple_url" ] ; then
# if no dot, then even remove the top level domain

View File

@ -97,13 +97,19 @@ def qute_command(command):
def find_pass_candidates(domain, password_store_path):
candidates = []
for path, directories, file_names in os.walk(password_store_path, followlinks=True):
if directories or domain not in path.split(os.path.sep):
secrets = fnmatch.filter(file_names, '*.gpg')
if not secrets:
continue
# Strip password store path prefix to get the relative pass path
pass_path = path[len(password_store_path) + 1:]
secrets = fnmatch.filter(file_names, '*.gpg')
candidates.extend(os.path.join(pass_path, os.path.splitext(secret)[0]) for secret in secrets)
split_path = pass_path.split(os.path.sep)
for secret in secrets:
secret_base = os.path.splitext(secret)[0]
if domain not in (split_path + [secret_base]):
continue
candidates.append(os.path.join(pass_path, secret_base))
return candidates

View File

@ -37,7 +37,7 @@ get_selection() {
# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font
[[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font
[[ $font ]] && opts+=(-fn "$font")
[[ -n $font ]] && opts+=(-fn "$font")
# shellcheck source=/dev/null
[[ -s $optsfile ]] && source "$optsfile"
@ -46,7 +46,7 @@ url=$(get_selection)
url=${url/*http/http}
# If no selection is made, exit (escape pressed, e.g.)
[[ ! $url ]] && exit 0
[[ -z $url ]] && exit 0
case $1 in
open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;

View File

@ -64,6 +64,7 @@ qt_log_ignore =
^QSettings::value: Empty key passed
^Icon theme ".*" not found
^Error receiving trust for a CA certificate
^QBackingStore::endPaint\(\) called with active painter on backingstore paint device
xfail_strict = true
filterwarnings =
error

View File

@ -943,6 +943,10 @@ class AbstractTab(QWidget):
evt.posted = True
QApplication.postEvent(recipient, evt)
def navigation_blocked(self) -> bool:
"""Test if navigation is allowed on the current tab."""
return self.data.pinned and config.val.tabs.pinned.frozen
@pyqtSlot(QUrl)
def _on_before_load_started(self, url: QUrl) -> None:
"""Adjust the title if we are going to visit a URL soon."""

View File

@ -316,7 +316,7 @@ class CommandDispatcher:
else:
# Explicit count with a tab that doesn't exist.
return
elif curtab.data.pinned:
elif curtab.navigation_blocked():
message.info("Tab is pinned!")
else:
curtab.load_url(cur_url)

View File

@ -180,6 +180,8 @@ class PACResolver:
"""
self._engine = QJSEngine()
self._engine.installExtensions(QJSEngine.ConsoleExtension)
self._ctx = _PACContext(self._engine)
self._engine.globalObject().setProperty(
"PAC", self._engine.newQObject(self._ctx))

View File

@ -42,7 +42,6 @@ def custom_headers(url):
if dnt_config is not None:
dnt = b'1' if dnt_config else b'0'
headers[b'DNT'] = dnt
headers[b'X-Do-Not-Track'] = dnt
conf_headers = config.instance.get('content.headers.custom', url=url)
for header, value in conf_headers.items():

View File

@ -22,6 +22,11 @@
from PyQt5.QtCore import QBuffer, QIODevice, QUrl
from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler,
QWebEngineUrlRequestJob)
try:
from PyQt5.QtWebEngineCore import QWebEngineUrlScheme # type: ignore
except ImportError:
# Added in Qt 5.12
QWebEngineUrlScheme = None
from qutebrowser.browser import qutescheme
from qutebrowser.utils import log, qtutils
@ -33,8 +38,12 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
def install(self, profile):
"""Install the handler for qute:// URLs on the given profile."""
if QWebEngineUrlScheme is not None:
assert QWebEngineUrlScheme.schemeByName(b'qute') is not None
profile.installUrlSchemeHandler(b'qute', self)
if qtutils.version_check('5.11', compiled=False):
if (qtutils.version_check('5.11', compiled=False) and
not qtutils.version_check('5.12', compiled=False)):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
profile.installUrlSchemeHandler(b'chrome-error', self)
profile.installUrlSchemeHandler(b'chrome-extension', self)
@ -130,3 +139,16 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
buf.seek(0)
buf.close()
job.reply(mimetype.encode('ascii'), buf)
def init():
"""Register the qute:// scheme.
Note this needs to be called early, before constructing any QtWebEngine
classes.
"""
if QWebEngineUrlScheme is not None:
scheme = QWebEngineUrlScheme(b'qute')
scheme.setFlags(QWebEngineUrlScheme.LocalScheme |
QWebEngineUrlScheme.LocalAccessAllowed)
QWebEngineUrlScheme.registerScheme(scheme)

View File

@ -30,7 +30,7 @@ from PyQt5.QtGui import QFont
from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile,
QWebEnginePage)
from qutebrowser.browser.webengine import spell
from qutebrowser.browser.webengine import spell, webenginequtescheme
from qutebrowser.config import config, websettings
from qutebrowser.config.websettings import AttributeInfo as Attr
from qutebrowser.utils import utils, standarddir, qtutils, message, log
@ -298,6 +298,7 @@ def init(args):
not hasattr(QWebEnginePage, 'setInspectedPage')): # only Qt < 5.11
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = str(utils.random_port())
webenginequtescheme.init()
spell.init()
_init_profiles()

View File

@ -212,11 +212,11 @@ class CompletionItemDelegate(QStyledItemDelegate):
view = self.parent()
pattern = view.pattern
columns_to_filter = index.model().columns_to_filter(index)
self._doc.setPlainText(self._opt.text)
if index.column() in columns_to_filter and pattern:
pat = re.escape(pattern).replace(r'\ ', r'|')
_Highlighter(self._doc, pat,
config.val.colors.completion.match.fg)
self._doc.setPlainText(self._opt.text)
else:
self._doc.setHtml(
'<span style="font: {};">{}</span>'.format(

View File

@ -118,7 +118,7 @@ def printpage(tab: apitypes.Tab,
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
def home(tab: apitypes.Tab) -> None:
"""Open main startpage in current tab."""
if tab.data.pinned:
if tab.navigation_blocked():
message.info("Tab is pinned!")
else:
tab.load_url(config.val.url.start_pages[0])

View File

@ -46,7 +46,9 @@ class ConfigCache:
self._cache[attr] = config.instance.get(attr)
def __getitem__(self, attr: str) -> typing.Any:
if attr not in self._cache:
try:
return self._cache[attr]
except KeyError:
assert not config.instance.get_opt(attr).supports_pattern
self._cache[attr] = config.instance.get(attr)
return self._cache[attr]
return self._cache[attr]

View File

@ -1768,6 +1768,11 @@ tabs.pinned.shrink:
type: Bool
desc: Shrink pinned tabs down to their contents.
tabs.pinned.frozen:
type: Bool
default: True
desc: Force pinned tabs to stay at fixed URL.
tabs.wrap:
default: true
type: Bool

View File

@ -308,7 +308,7 @@ class YamlConfig(QObject):
self._migrate_bool(settings, 'tabs.favicons.show', 'always', 'never')
self._migrate_bool(settings, 'scrolling.bar',
'when-searching', 'never')
'always', 'when-searching')
self._migrate_bool(settings, 'qt.force_software_rendering',
'software-opengl', 'none')

View File

@ -71,7 +71,7 @@ EOF
set -e
if [[ $DOCKER ]]; then
if [[ -n $DOCKER ]]; then
exit 0
elif [[ $TRAVIS_OS_NAME == osx ]]; then
# Disable App Nap

View File

@ -1,6 +1,6 @@
#!/bin/bash
if [[ $DOCKER ]]; then
if [[ -n $DOCKER ]]; then
docker run \
--privileged \
-v "$PWD:/outside" \

View File

@ -93,7 +93,7 @@ Feature: Downloading things from a website.
Then no crash should happen
# https://github.com/qutebrowser/qutebrowser/issues/4240
@qt!=5.11.2
@qt<5.11.2
Scenario: Downloading with SSL errors (issue 1413)
When SSL is supported
And I clear SSL errors

View File

@ -331,19 +331,16 @@ Feature: Various utility commands.
When I set content.headers.do_not_track to true
And I open headers
Then the header Dnt should be set to 1
And the header X-Do-Not-Track should be set to 1
Scenario: DNT header (off)
When I set content.headers.do_not_track to false
And I open headers
Then the header Dnt should be set to 0
And the header X-Do-Not-Track should be set to 0
Scenario: DNT header (unset)
When I set content.headers.do_not_track to <empty>
And I open headers
Then the header Dnt should be set to <unset>
And the header X-Do-Not-Track should be set to <unset>
Scenario: Accept-Language header
When I set content.headers.accept_language to en,de

View File

@ -1289,6 +1289,14 @@ Feature: Tab management
And the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
Scenario: :tab-pin open url with tabs.pinned.frozen = false
When I set tabs.pinned.frozen to false
And I open data/numbers/1.txt
And I run :tab-pin
And I open data/numbers/2.txt
Then the following tabs should be open:
- data/numbers/2.txt (active) (pinned)
Scenario: :home on a pinned tab
When I open data/numbers/1.txt
And I run :tab-pin
@ -1297,6 +1305,16 @@ Feature: Tab management
And the following tabs should be open:
- data/numbers/1.txt (active) (pinned)
Scenario: :home on a pinned tab with tabs.pinned.frozen = false
When I set url.start_pages to ["http://localhost:(port)/data/numbers/2.txt"]
And I set tabs.pinned.frozen to false
And I open data/numbers/1.txt
And I run :tab-pin
And I run :home
Then data/numbers/2.txt should be loaded
And the following tabs should be open:
- data/numbers/2.txt (active) (pinned)
Scenario: Cloning a pinned tab
When I open data/numbers/1.txt
And I run :tab-pin

View File

@ -26,18 +26,15 @@ from qutebrowser.browser import shared
@pytest.mark.parametrize('dnt, accept_language, custom_headers, expected', [
# DNT
(True, None, {}, {b'DNT': b'1', b'X-Do-Not-Track': b'1'}),
(False, None, {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0'}),
(True, None, {}, {b'DNT': b'1'}),
(False, None, {}, {b'DNT': b'0'}),
(None, None, {}, {}),
# Accept-Language
(False, 'de, en', {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
b'Accept-Language': b'de, en'}),
(False, 'de, en', {}, {b'DNT': b'0', b'Accept-Language': b'de, en'}),
# Custom headers
(False, None, {'X-Qute': 'yes'}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
b'X-Qute': b'yes'}),
(False, None, {'X-Qute': 'yes'}, {b'DNT': b'0', b'X-Qute': b'yes'}),
# Mixed
(False, 'de, en', {'X-Qute': 'yes'}, {b'DNT': b'0',
b'X-Do-Not-Track': b'0',
b'Accept-Language': b'de, en',
b'X-Qute': b'yes'}),
])

View File

@ -205,6 +205,20 @@ def test_secret_url(url, has_secret, from_file):
res.resolve(QNetworkProxyQuery(QUrl(url)), from_file=from_file)
def test_logging(qtlog):
"""Make sure console.log() works for PAC files."""
test_str = """
function FindProxyForURL(domain, host) {
console.log("logging test");
return "DIRECT";
}
"""
res = pac.PACResolver(test_str)
res.resolve(QNetworkProxyQuery(QUrl("https://example.com/test")))
assert len(qtlog.records) == 1
assert qtlog.records[0].message == 'logging test'
def fetcher_test(test_str):
class PACHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):

View File

@ -20,7 +20,8 @@ from unittest import mock
import pytest
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextDocument
from PyQt5.QtGui import QTextDocument, QColor
from PyQt5.QtWidgets import QTextEdit
from qutebrowser.completion import completiondelegate
@ -50,3 +51,24 @@ def test_highlight(pat, txt, segments):
highlighter.setFormat.assert_has_calls([
mock.call(s[0], s[1], mock.ANY) for s in segments
])
def test_highlighted(qtbot):
"""Make sure highlighting works.
Note that with Qt 5.11.3 and > 5.12.1 we need to call setPlainText *after*
creating the highlighter for highlighting to work. Ideally, we'd test
whether CompletionItemDelegate._get_textdoc() works properly, but testing
that is kind of hard, so we just test it in isolation here.
"""
doc = QTextDocument()
completiondelegate._Highlighter(doc, 'Hello', Qt.red)
doc.setPlainText('Hello World')
# Needed so the highlighting actually works.
edit = QTextEdit()
qtbot.addWidget(edit)
edit.setDocument(doc)
colors = [f.foreground().color() for f in doc.allFormats()]
assert QColor('red') in colors

View File

@ -50,3 +50,17 @@ def test_configcache_get_after_set(config_stub):
assert not config.cache['auto_save.session']
config_stub.val.auto_save.session = True
assert config.cache['auto_save.session']
def test_configcache_naive_benchmark(config_stub, benchmark):
def _run_bench():
for _i in range(10000):
# pylint: disable=pointless-statement
config.cache['tabs.padding']
config.cache['tabs.indicator.width']
config.cache['tabs.indicator.padding']
config.cache['tabs.min_width']
config.cache['tabs.max_width']
config.cache['tabs.pinned.shrink']
# pylint: enable=pointless-statement
benchmark(_run_bench)

View File

@ -250,36 +250,28 @@ class TestYaml:
data = autoconfig.read()
assert data['content.webrtc_ip_handling_policy']['global'] == expected
@pytest.mark.parametrize('show, expected', [
(True, 'always'),
(False, 'never'),
('always', 'always'),
('never', 'never'),
('pinned', 'pinned'),
@pytest.mark.parametrize('setting, old, new', [
('tabs.favicons.show', True, 'always'),
('tabs.favicons.show', False, 'never'),
('tabs.favicons.show', 'always', 'always'),
('scrolling.bar', True, 'always'),
('scrolling.bar', False, 'when-searching'),
('scrolling.bar', 'always', 'always'),
('qt.force_software_rendering', True, 'software-opengl'),
('qt.force_software_rendering', False, 'none'),
('qt.force_software_rendering', 'chromium', 'chromium'),
])
def test_tabs_favicons_show(self, yaml, autoconfig, show, expected):
"""Tests for migration of tabs.favicons.show."""
autoconfig.write({'tabs.favicons.show': {'global': show}})
def test_bool_migrations(self, yaml, autoconfig, setting, old, new):
"""Tests for migration of former boolean settings."""
autoconfig.write({setting: {'global': old}})
yaml.load()
yaml._save()
data = autoconfig.read()
assert data['tabs.favicons.show']['global'] == expected
@pytest.mark.parametrize('force, expected', [
(True, 'software-opengl'),
(False, 'none'),
('chromium', 'chromium'),
])
def test_force_software_rendering(self, yaml, autoconfig, force, expected):
autoconfig.write({'qt.force_software_rendering': {'global': force}})
yaml.load()
yaml._save()
data = autoconfig.read()
assert data['qt.force_software_rendering']['global'] == expected
assert data[setting]['global'] == new
def test_renamed_key_unknown_target(self, monkeypatch, yaml,
autoconfig):