Merge branch 'rcorre-messages'
This commit is contained in:
commit
6fc2217b07
@ -29,6 +29,7 @@ Added
|
|||||||
- The `'` mark gets set when moving away (hinting link with anchor, searching, etc.) so you can move back with `''`
|
- The `'` mark gets set when moving away (hinting link with anchor, searching, etc.) so you can move back with `''`
|
||||||
- New `--force-color` argument to force colored logging even if stdout is not a
|
- New `--force-color` argument to force colored logging even if stdout is not a
|
||||||
terminal
|
terminal
|
||||||
|
- New `:messages` command to show error messages
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -145,8 +145,8 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Alexander Cogneau
|
* Alexander Cogneau
|
||||||
* Felix Van der Jeugt
|
* Felix Van der Jeugt
|
||||||
* Martin Tournoij
|
* Martin Tournoij
|
||||||
* Raphael Pierzina
|
|
||||||
* Ryan Roden-Corrent
|
* Ryan Roden-Corrent
|
||||||
|
* Raphael Pierzina
|
||||||
* Joel Torstensson
|
* Joel Torstensson
|
||||||
* Patric Schmitz
|
* Patric Schmitz
|
||||||
* Claude
|
* Claude
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|<<jseval,jseval>>|Evaluate a JavaScript string.
|
|<<jseval,jseval>>|Evaluate a JavaScript string.
|
||||||
|<<jump-mark,jump-mark>>|Jump to the mark named by `key`.
|
|<<jump-mark,jump-mark>>|Jump to the mark named by `key`.
|
||||||
|<<later,later>>|Execute a command after some time.
|
|<<later,later>>|Execute a command after some time.
|
||||||
|
|<<messages,messages>>|Show a log of past messages.
|
||||||
|<<navigate,navigate>>|Open typical prev/next links or navigate using the URL path.
|
|<<navigate,navigate>>|Open typical prev/next links or navigate using the URL path.
|
||||||
|<<open,open>>|Open a URL in the current/[count]th tab.
|
|<<open,open>>|Open a URL in the current/[count]th tab.
|
||||||
|<<paste,paste>>|Open a page from the clipboard.
|
|<<paste,paste>>|Open a page from the clipboard.
|
||||||
@ -403,6 +404,22 @@ Execute a command after some time.
|
|||||||
* This command does not split arguments after the last argument and handles quotes literally.
|
* This command does not split arguments after the last argument and handles quotes literally.
|
||||||
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
|
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
|
||||||
|
|
||||||
|
[[messages]]
|
||||||
|
=== messages
|
||||||
|
Syntax: +:messages [*--plain*] [*--tab*] [*--bg*] [*--window*] ['level']+
|
||||||
|
|
||||||
|
Show a log of past messages.
|
||||||
|
|
||||||
|
==== positional arguments
|
||||||
|
* +'level'+: Include messages with `level` or higher severity. Valid values: vdebug, debug, info, warning, error, critical.
|
||||||
|
|
||||||
|
|
||||||
|
==== optional arguments
|
||||||
|
* +*-p*+, +*--plain*+: Whether to show plaintext (as opposed to html).
|
||||||
|
* +*-t*+, +*--tab*+: Open in a new tab.
|
||||||
|
* +*-b*+, +*--bg*+: Open in a background tab.
|
||||||
|
* +*-w*+, +*--window*+: Open in a new window.
|
||||||
|
|
||||||
[[navigate]]
|
[[navigate]]
|
||||||
=== navigate
|
=== navigate
|
||||||
Syntax: +:navigate [*--tab*] [*--bg*] [*--window*] 'where'+
|
Syntax: +:navigate [*--tab*] [*--bg*] [*--window*] 'where'+
|
||||||
|
@ -1339,6 +1339,27 @@ class CommandDispatcher:
|
|||||||
url = QUrl('qute://help/{}'.format(path))
|
url = QUrl('qute://help/{}'.format(path))
|
||||||
self._open(url, tab, bg, window)
|
self._open(url, tab, bg, window)
|
||||||
|
|
||||||
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||||
|
def messages(self, level='error', plain=False, tab=False, bg=False,
|
||||||
|
window=False):
|
||||||
|
"""Show a log of past messages.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: Include messages with `level` or higher severity.
|
||||||
|
Valid values: vdebug, debug, info, warning, error, critical.
|
||||||
|
plain: Whether to show plaintext (as opposed to html).
|
||||||
|
tab: Open in a new tab.
|
||||||
|
bg: Open in a background tab.
|
||||||
|
window: Open in a new window.
|
||||||
|
"""
|
||||||
|
if level.upper() not in log.LOG_LEVELS:
|
||||||
|
raise cmdexc.CommandError("Invalid log level {}!".format(level))
|
||||||
|
if plain:
|
||||||
|
url = QUrl('qute://plainlog?level={}'.format(level))
|
||||||
|
else:
|
||||||
|
url = QUrl('qute://log?level={}'.format(level))
|
||||||
|
self._open(url, tab, bg, window)
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher',
|
@cmdutils.register(instance='command-dispatcher',
|
||||||
modes=[KeyMode.insert], hide=True, scope='window')
|
modes=[KeyMode.insert], hide=True, scope='window')
|
||||||
def open_editor(self):
|
def open_editor(self):
|
||||||
|
@ -26,6 +26,7 @@ Module attributes:
|
|||||||
import functools
|
import functools
|
||||||
import configparser
|
import configparser
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, QObject
|
from PyQt5.QtCore import pyqtSlot, QObject
|
||||||
from PyQt5.QtNetwork import QNetworkReply
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
@ -158,23 +159,42 @@ def qute_version(_win_id, _request):
|
|||||||
|
|
||||||
|
|
||||||
@add_handler('plainlog')
|
@add_handler('plainlog')
|
||||||
def qute_plainlog(_win_id, _request):
|
def qute_plainlog(_win_id, request):
|
||||||
"""Handler for qute:plainlog. Return HTML content as bytes."""
|
"""Handler for qute:plainlog. Return HTML content as bytes.
|
||||||
|
|
||||||
|
An optional query parameter specifies the minimum log level to print.
|
||||||
|
For example, qute://log?level=warning prints warnings and errors.
|
||||||
|
Level can be one of: vdebug, debug, info, warning, error, critical.
|
||||||
|
"""
|
||||||
if log.ram_handler is None:
|
if log.ram_handler is None:
|
||||||
text = "Log output was disabled."
|
text = "Log output was disabled."
|
||||||
else:
|
else:
|
||||||
text = log.ram_handler.dump_log()
|
try:
|
||||||
|
level = urllib.parse.parse_qs(request.url().query())['level'][0]
|
||||||
|
except KeyError:
|
||||||
|
level = 'vdebug'
|
||||||
|
text = log.ram_handler.dump_log(html=False, level=level)
|
||||||
html = jinja.render('pre.html', title='log', content=text)
|
html = jinja.render('pre.html', title='log', content=text)
|
||||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||||
|
|
||||||
|
|
||||||
@add_handler('log')
|
@add_handler('log')
|
||||||
def qute_log(_win_id, _request):
|
def qute_log(_win_id, request):
|
||||||
"""Handler for qute:log. Return HTML content as bytes."""
|
"""Handler for qute:log. Return HTML content as bytes.
|
||||||
|
|
||||||
|
An optional query parameter specifies the minimum log level to print.
|
||||||
|
For example, qute://log?level=warning prints warnings and errors.
|
||||||
|
Level can be one of: vdebug, debug, info, warning, error, critical.
|
||||||
|
"""
|
||||||
if log.ram_handler is None:
|
if log.ram_handler is None:
|
||||||
html_log = None
|
html_log = None
|
||||||
else:
|
else:
|
||||||
html_log = log.ram_handler.dump_log(html=True)
|
try:
|
||||||
|
level = urllib.parse.parse_qs(request.url().query())['level'][0]
|
||||||
|
except KeyError:
|
||||||
|
level = 'vdebug'
|
||||||
|
html_log = log.ram_handler.dump_log(html=True, level=level)
|
||||||
|
|
||||||
html = jinja.render('log.html', title='log', content=html_log)
|
html = jinja.render('log.html', title='log', content=html_log)
|
||||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ th, td {
|
|||||||
<table>
|
<table>
|
||||||
{{ content | safe() }}
|
{{ content | safe() }}
|
||||||
</table>
|
</table>
|
||||||
|
{% elif content is not none %}
|
||||||
|
<p>No messages to show.</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Log output was disabled.</p>
|
<p>Log output was disabled.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -64,13 +64,21 @@ LOG_COLORS = {
|
|||||||
'CRITICAL': 'red',
|
'CRITICAL': 'red',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# We first monkey-patch logging to support our VDEBUG level before getting the
|
# We first monkey-patch logging to support our VDEBUG level before getting the
|
||||||
# loggers. Based on http://stackoverflow.com/a/13638084
|
# loggers. Based on http://stackoverflow.com/a/13638084
|
||||||
VDEBUG_LEVEL = 9
|
VDEBUG_LEVEL = 9
|
||||||
logging.addLevelName(VDEBUG_LEVEL, 'VDEBUG')
|
logging.addLevelName(VDEBUG_LEVEL, 'VDEBUG')
|
||||||
logging.VDEBUG = VDEBUG_LEVEL
|
logging.VDEBUG = VDEBUG_LEVEL
|
||||||
|
|
||||||
|
LOG_LEVELS = {
|
||||||
|
'VDEBUG': logging.VDEBUG,
|
||||||
|
'DEBUG': logging.DEBUG,
|
||||||
|
'INFO': logging.INFO,
|
||||||
|
'WARNING': logging.WARNING,
|
||||||
|
'ERROR': logging.ERROR,
|
||||||
|
'CRITICAL': logging.CRITICAL,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def vdebug(self, msg, *args, **kwargs):
|
def vdebug(self, msg, *args, **kwargs):
|
||||||
"""Log with a VDEBUG level.
|
"""Log with a VDEBUG level.
|
||||||
@ -430,13 +438,14 @@ class RAMHandler(logging.Handler):
|
|||||||
# We don't log VDEBUG to RAM.
|
# We don't log VDEBUG to RAM.
|
||||||
self._data.append(record)
|
self._data.append(record)
|
||||||
|
|
||||||
def dump_log(self, html=False):
|
def dump_log(self, html=False, level='vdebug'):
|
||||||
"""Dump the complete formatted log data as as string.
|
"""Dump the complete formatted log data as as string.
|
||||||
|
|
||||||
FIXME: We should do all the HTML formatter via jinja2.
|
FIXME: We should do all the HTML formatter via jinja2.
|
||||||
(probably obsolete when moving to a widget for logging,
|
(probably obsolete when moving to a widget for logging,
|
||||||
https://github.com/The-Compiler/qutebrowser/issues/34
|
https://github.com/The-Compiler/qutebrowser/issues/34
|
||||||
"""
|
"""
|
||||||
|
minlevel = LOG_LEVELS.get(level.upper(), VDEBUG_LEVEL)
|
||||||
lines = []
|
lines = []
|
||||||
fmt = self.html_formatter.format if html else self.format
|
fmt = self.html_formatter.format if html else self.format
|
||||||
self.acquire()
|
self.acquire()
|
||||||
@ -445,6 +454,7 @@ class RAMHandler(logging.Handler):
|
|||||||
finally:
|
finally:
|
||||||
self.release()
|
self.release()
|
||||||
for record in records:
|
for record in records:
|
||||||
|
if record.levelno >= minlevel:
|
||||||
lines.append(fmt(record))
|
lines.append(fmt(record))
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
@ -366,6 +366,14 @@ def check_contents_plain(quteproc, text):
|
|||||||
assert text in content
|
assert text in content
|
||||||
|
|
||||||
|
|
||||||
|
@bdd.then(bdd.parsers.parse('the page should not contain the plaintext '
|
||||||
|
'"{text}"'))
|
||||||
|
def check_not_contents_plain(quteproc, text):
|
||||||
|
"""Check the current page's content based on a substring."""
|
||||||
|
content = quteproc.get_content().strip()
|
||||||
|
assert text not in content
|
||||||
|
|
||||||
|
|
||||||
@bdd.then(bdd.parsers.parse('the json on the page should be:\n{text}'))
|
@bdd.then(bdd.parsers.parse('the json on the page should be:\n{text}'))
|
||||||
def check_contents_json(quteproc, text):
|
def check_contents_json(quteproc, text):
|
||||||
"""Check the current page's content as json."""
|
"""Check the current page's content as json."""
|
||||||
|
@ -379,3 +379,55 @@ Feature: Various utility commands.
|
|||||||
When I set network -> custom-headers to {"X-Qute-Test": "testvalue"}
|
When I set network -> custom-headers to {"X-Qute-Test": "testvalue"}
|
||||||
And I open headers
|
And I open headers
|
||||||
Then the header X-Qute-Test should be set to testvalue
|
Then the header X-Qute-Test should be set to testvalue
|
||||||
|
|
||||||
|
## :messages
|
||||||
|
|
||||||
|
Scenario: Showing error messages
|
||||||
|
When I run :message-error the-error-message
|
||||||
|
And I run :message-warning the-warning-message
|
||||||
|
And I run :message-info the-info-message
|
||||||
|
And I run :messages
|
||||||
|
Then the error "the-error-message" should be shown
|
||||||
|
And the warning "the-warning-message" should be shown
|
||||||
|
And the page should contain the plaintext "the-error-message"
|
||||||
|
And the page should not contain the plaintext "the-warning-message"
|
||||||
|
And the page should not contain the plaintext "the-info-message"
|
||||||
|
|
||||||
|
Scenario: Showing messages of type 'warning' or greater
|
||||||
|
When I run :message-error the-error-message
|
||||||
|
And I run :message-warning the-warning-message
|
||||||
|
And I run :message-info the-info-message
|
||||||
|
And I run :messages warning
|
||||||
|
Then the error "the-error-message" should be shown
|
||||||
|
And the warning "the-warning-message" should be shown
|
||||||
|
And the page should contain the plaintext "the-error-message"
|
||||||
|
And the page should contain the plaintext "the-warning-message"
|
||||||
|
And the page should not contain the plaintext "the-info-message"
|
||||||
|
|
||||||
|
Scenario: Showing messages of type 'info' or greater
|
||||||
|
When I run :message-error the-error-message
|
||||||
|
And I run :message-warning the-warning-message
|
||||||
|
And I run :message-info the-info-message
|
||||||
|
And I run :messages info
|
||||||
|
Then the error "the-error-message" should be shown
|
||||||
|
And the warning "the-warning-message" should be shown
|
||||||
|
And the page should contain the plaintext "the-error-message"
|
||||||
|
And the page should contain the plaintext "the-warning-message"
|
||||||
|
And the page should contain the plaintext "the-info-message"
|
||||||
|
|
||||||
|
Scenario: Showing messages of an invalid level
|
||||||
|
When I run :messages cataclysmic
|
||||||
|
Then the error "Invalid log level cataclysmic!" should be shown
|
||||||
|
|
||||||
|
Scenario: Using qute:log directly
|
||||||
|
When I open qute:log
|
||||||
|
Then no crash should happen
|
||||||
|
|
||||||
|
Scenario: Using qute:plainlog directly
|
||||||
|
When I open qute:plainlog
|
||||||
|
Then no crash should happen
|
||||||
|
|
||||||
|
Scenario: Using :messages without messages
|
||||||
|
Given I have a fresh instance
|
||||||
|
When I run :messages
|
||||||
|
Then the page should contain the plaintext "No messages to show."
|
||||||
|
@ -85,3 +85,12 @@ def test_ascii_locale(httpbin, tmpdir, quteproc_new):
|
|||||||
|
|
||||||
assert len(tmpdir.listdir()) == 1
|
assert len(tmpdir.listdir()) == 1
|
||||||
assert (tmpdir / '?-issue908.bin').exists()
|
assert (tmpdir / '?-issue908.bin').exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_loglines(quteproc_new):
|
||||||
|
"""Test qute:log with --loglines=0."""
|
||||||
|
quteproc_new.start(args=['--debug', '--no-err-windows', '--temp-basedir',
|
||||||
|
'--loglines=0', 'about:blank'])
|
||||||
|
quteproc_new.open_path('qute:log')
|
||||||
|
quteproc_new.wait_for_load_finished('qute:log')
|
||||||
|
assert quteproc_new.get_content() == 'Log output was disabled.'
|
||||||
|
Loading…
Reference in New Issue
Block a user