- Add a couple new "raise utils.Unreachable" to avoid
possibly-used-before-assignment issues.
- Simplify an "if" for the same reason
- Remove an unneeded "return"
- Use "NoReturn" to prepare for pylint knowing about it in the future:
https://github.com/pylint-dev/pylint/issues/9674
- Add some ignores for used-before-assignment false-positives
- Ignore new undefined-variable messages for Qt wrapers
- Ignore a new no-member warning for KeySequence:
https://github.com/pylint-dev/astroid/issues/2448#issuecomment-2130124755
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
This used to be possible in some situations and was handled in somewhat
unexpected places (e.g. .to_qt()). Instead, we now assume that KeyInfo
is always "clean", and we handle the conversion from an int to a Qt.Key
elsewhere.
This only seems to affect tests, since otherwise we already made sure
we get a Qt.Key and Qt.KeyboardModifier(s) e.g. in .from_event().
We're deprecating vim modelines in favor of `.editorconfig`.
Removing vim modelines could be done using two one-liners. Most of the vim modelines
were followed by an empty line, so this one-liner took care of these ones:
```sh
rg '^# vim: .+\n\n' -l | xargs sed -i '/^# vim: /,+1d'
```
Then some of the vim modelines were followed by a pylint configuration line, so running
this one-liner afterwards took care of that:
```sh
rg '^# vim:' -l | xargs sed -i '/^# vim: /d'
```
This avoids the temptation of creating a Qt.Key() manually, which needs
to be checked for ValueError with PyQt 6.2 due to its handling of unknown enum
values.
This is exactly what happened in RegisterKeyParser, which caused such a
ValueError:
https://github.com/qutebrowser/qutebrowser/issues/7047#issuecomment-1163288560Closes#7047
TODO: Pick to master?
In 42e2438efb, we changed "_handle_key" to return
a QKeySequence.SequenceMatch instead of a bool.
Later in b85fe8f678, that change propagated to
"handle" too.
However, the FakeKeyParser introduced in
c1d3a94936 still returned a bool.
This wasn't a problem because we still just checked "if match:".
However, with 756db287ac4c65350aaf53d6f93b6a0a2b356a77, we now check
"if match != QKeySequence.SequenceMatch.NoMatch:", thus the comparsison
with the FakeKeyParser is now the wrong way around.
If we test for this in KeyInfo() directly (via __post_init__), all code using a
KeyInfo can be sure it has a properly split key/modifier. This is especially
important on Qt 5, where lots of Qt internals use a split value (Qt 6 introduced
QKeyCombination instead).
This however leads to different behaviour when -1 is passed as a key, that now
fails with an AssertionError. However, I believe that to be more correct:
- Testing for -1 was originally introduced in
866c758660, expecting an AssertionError.
- It was updated in d1854eddaf, still
expecting an AssertionError
- In 0ee7fac727, we changed that to KeyParseError
(in the tests only), probably in response to
78f6ad14c2.
Now that we properly check for this, it's fine to expect an AssertionError
again. I don't think we can ever get -1 from Qt anyways.
Everything in keyutils constructing a Qt.Key should catch ValueError and reraise
it as InvalidKeyError (low-level). Anything in KeySequence that calls such
methods should re-raise that as a KeyParseError to user code (high-level).
The tests skip anything that relies on constructing things with "strange" keys.
Maybe some of them could be gotten to work, but most probably won't work until
PyQt gets fixed, see e.g.:
https://www.riverbankcomputing.com/pipermail/pyqt/2022-April/044607.html
For some reason, QtMsgType was not included and missing.
(cherry picked from commit 6afa00c465)
For completiondelegate.py, we accessed the enum members via self instead of
properly using the class.
(cherry picked from commit d37cc4ac73)
For interceptor.py, line breaks broke our script.
QKeyEvent.KeyPress was used inherited from QEvent.KeyPress, thus not
renamed.
By default numbers are interpreted as counts for bindings. Making this
behavior configurable allows for emacs-like bindings, where number keys
are passed through.
Otherwise, when e.g. doing "<Meta+Up>": "gg" in bindings.key_mappings,
there's a crash like:
Traceback (most recent call last):
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/eventfilter.py", line 105, in eventFilter
return handler(typing.cast(QKeyEvent, event))
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/eventfilter.py", line 75, in _handle_key_event
return man.handle_event(event)
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/modeman.py", line 462, in handle_event
return handler(cast(QKeyEvent, event))
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/modeman.py", line 283, in _handle_keypress
match = parser.handle(event, dry_run=dry_run)
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/modeparsers.py", line 105, in handle
match = super().handle(e, dry_run=dry_run)
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/basekeyparser.py", line 309, in handle
result = self._match_key_mapping(result.sequence)
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/basekeyparser.py", line 246, in _match_key_mapping
mapped = sequence.with_mappings(
File "/usr/lib/python3.9/site-packages/qutebrowser/keyinput/keyutils.py", line 675, in with_mappings
assert len(new_seq) == 1
AssertionError
While this isn't the intended way to use this setting, we shouldn't
crash - and let's just make it work instead of forbidding it.
Instead of binding hints to fake :follow-hint commands, we now use a separate
CommandKeyParser and ask that for its match result.
If the key matches with the command parser, it is bound in hint mode, so we
clear the hint keystring and defer to the command handling instead.
If it doesn't, we continue hint handling as usual - however, the HintKeyParser
is now not a CommandKeyParser anymore, so we don't have to deal with command
parsing (and have a custom execute implementation instead).
Closes#4504Fixes#4392Fixes#4368
Helps with #5084, though it doesn't completely fix that yet.
Supersedes #3742 (fix for #3735)
Supersedes #4691 (fix for #4264)