summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Albrecht <palbrecht@mailbox.org>2023-08-18 14:19:40 +0200
committerPhilipp Albrecht <palbrecht@mailbox.org>2023-08-27 13:31:11 +0200
commit51aa7abe548ccaec66f64b08c4bdc9a3fcdba67f (patch)
treee5ccd6a1513ebf58afc383c666070735dae9d65d
parent07e1376e64ad27a273682997ad705ef156b785ef (diff)
downloadqutebrowser-51aa7abe548ccaec66f64b08c4bdc9a3fcdba67f.tar.gz
qutebrowser-51aa7abe548ccaec66f64b08c4bdc9a3fcdba67f.zip
Handle count exceeding string to int conversion
When handling counts during keyparsing we convert the count string to an integer. If the count is too high (i.e. the count string has too many digits), we run into Python's integer string conversion length limit[1]: ``` ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 4301 digits; use sys.set_int_max_str_digits() to increase the limit ``` Instead of blowing up with an exception, we now handle this more gracefully by showing an error message. Reproducer: ``` $ qutebrowser --temp-basedir ":later 500 fake-key -g $(printf '1%.0s' {1..4301})j" ``` **NOTE:** I had to rename `_debug_log()`'s `message` argument to `msg`, because pylint yelled at me for redefined-outer-name[2]. [1] https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation [2] https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/redefined-outer-name.html
-rw-r--r--qutebrowser/keyinput/basekeyparser.py17
-rw-r--r--tests/unit/keyinput/test_basekeyparser.py20
2 files changed, 33 insertions, 4 deletions
diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py
index 8c8ca4613..df6b66f7f 100644
--- a/qutebrowser/keyinput/basekeyparser.py
+++ b/qutebrowser/keyinput/basekeyparser.py
@@ -7,13 +7,14 @@
import string
import types
import dataclasses
+import traceback
from typing import Mapping, MutableMapping, Optional, Sequence
from qutebrowser.qt.core import QObject, pyqtSignal
from qutebrowser.qt.gui import QKeySequence, QKeyEvent
from qutebrowser.config import config
-from qutebrowser.utils import log, usertypes, utils
+from qutebrowser.utils import log, usertypes, utils, message
from qutebrowser.keyinput import keyutils
@@ -189,7 +190,7 @@ class BaseKeyParser(QObject):
passthrough=self.passthrough,
supports_count=self._supports_count)
- def _debug_log(self, message: str) -> None:
+ def _debug_log(self, msg: str) -> None:
"""Log a message to the debug log if logging is active.
Args:
@@ -198,7 +199,7 @@ class BaseKeyParser(QObject):
if self._do_log:
prefix = '{} for mode {}: '.format(self.__class__.__name__,
self._mode.name)
- log.keyboard.debug(prefix + message)
+ log.keyboard.debug(prefix + msg)
def _match_key(self, sequence: keyutils.KeySequence) -> MatchResult:
"""Try to match a given keystring with any bound keychain.
@@ -315,7 +316,15 @@ class BaseKeyParser(QObject):
assert result.command is not None
self._debug_log("Definitive match for '{}'.".format(
result.sequence))
- count = int(self._count) if self._count else None
+
+ try:
+ count = int(self._count) if self._count else None
+ except ValueError as err:
+ message.error(f"Failed to parse count: {err}",
+ stack=traceback.format_exc())
+ self.clear_keystring()
+ return
+
self.clear_keystring()
self.execute(result.command, count)
elif result.match_type == QKeySequence.SequenceMatch.PartialMatch:
diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py
index 68239d4b4..ec7c225bf 100644
--- a/tests/unit/keyinput/test_basekeyparser.py
+++ b/tests/unit/keyinput/test_basekeyparser.py
@@ -4,6 +4,9 @@
"""Tests for BaseKeyParser."""
+import logging
+import re
+import sys
from unittest import mock
from qutebrowser.qt.core import Qt
@@ -342,3 +345,20 @@ def test_respect_config_when_matching_counts(keyparser, config_stub):
assert not keyparser._sequence
assert not keyparser._count
+
+
+def test_count_limit_exceeded(handle_text, keyparser, caplog):
+ try:
+ max_digits = sys.get_int_max_str_digits()
+ except AttributeError:
+ pytest.skip('sys.get_int_max_str_digits() not available')
+
+ keys = (max_digits + 1) * [Qt.Key.Key_1]
+
+ with caplog.at_level(logging.ERROR):
+ handle_text(keyparser, *keys, Qt.Key.Key_B, Qt.Key.Key_A)
+
+ pattern = re.compile(r"^Failed to parse count: Exceeds the limit .* for integer string conversion: .*")
+ assert any(pattern.fullmatch(msg) for msg in caplog.messages)
+ assert not keyparser._sequence
+ assert not keyparser._count