Repository: qutebrowser/qutebrowser

Issue:
With giant count: `ValueError: Exceeds the limit (4300 digits) for integer string conversion`
**Version info**:
qutebrowser v2.5.4
Git commit: 6d84462d6 on main (2023-07-25 11:00:33 +0200)
Backend: QtWebEngine 6.5.2, based on Chromium 108.0.5359.220 (from api)
Qt: 6.5.2

CPython: 3.11.4
PyQt: 6.5.2

**Does the bug happen if you start with `--temp-basedir`?**: yes

**Description**

```bash
qutebrowser --temp-basedir ":later 500 fake-key -g $(printf '1%.0s' {1..4301})j"
```

results in:

```pytb
Traceback (most recent call last):
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/eventfilter.py", line 100, in eventFilter
    return handler(cast(QKeyEvent, event))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/eventfilter.py", line 60, in _handle_key_event
    return man.handle_event(event)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/modeman.py", line 465, in handle_event
    return handler(cast(QKeyEvent, event))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/modeman.py", line 278, in _handle_keypress
    match = parser.handle(event, dry_run=dry_run)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/modeparsers.py", line 100, in handle
    match = super().handle(e, dry_run=dry_run)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/basekeyparser.py", line 309, in handle
    self._handle_result(info, result)
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/basekeyparser.py", line 318, in _handle_result
    count = int(self._count) if self._count else None
            ^^^^^^^^^^^^^^^^
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
```

due to Python's [Integer string conversion length limitation](https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation)


**How to reproduce**

- Cat steps on keyboard
- Cat decides laptop is warm and cozy
- Cat does a long purrrrrrrrrr while resting on a number key
- Cat steps on letter key bound to some qutebrowser command

(I don't actually have a cat. But I secretly imagine this is how it happened for the person who reported this via the crashreporter.)


Discussion:
Leaving this here for reference:
```
13:44:33    pylbrecht | what would be the desired behavior? Cap at Python's integer string conversion max length?
13:55:50 The-Compiler | pylbrecht: sounds good! I'd say a message.error() and ignoring the command
13:56:24 The-Compiler | see modeparsers.py for some inspiration re message.error
```

I'm hoping for the swiftest fix. I'm getting a cat in a few days and I'm scared for my browser.

Resolving patch:
diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py
--- 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 @@ def __repr__(self) -> str:
                               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 @@ def _debug_log(self, message: str) -> None:
         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 @@ def _handle_result(self, info: keyutils.KeyInfo, result: MatchResult) -> None:
             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:


Test patch:
diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py
--- 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