Accept keys in indeterminate order in KeyboardInputGesture.fromName - #20647
Accept keys in indeterminate order in KeyboardInputGesture.fromName#20647LeonarddeR wants to merge 2 commits into
Conversation
… key The main key is the non modifier key, regardless of its position in the name. Names containing only modifiers keep the last key as the main key. fromName raises ValueError for multiple non modifier keys, unknown key names and empty names. Key name resolution moves to a new _resolveKeyName classmethod. Add unit tests for fromName. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves keyboardHandler.KeyboardInputGesture.fromName so written key names can be parsed correctly even when the “main” (non-modifier) key is not last, and so invalid inputs raise documented ValueErrors instead of leaking KeyError. This aligns gesture parsing behavior with how NVDA treats modifier vs main keys elsewhere, and prevents malformed identifiers when users/devs provide unordered key sequences.
Changes:
- Refactors single-key resolution into
KeyboardInputGesture._resolveKeyName, centralizing virtual-key lookup and modifier expansion for characters. - Updates
KeyboardInputGesture.fromNameto select the (single) non-modifier key as the main key regardless of position; keeps legacy behavior for names containing only modifiers; normalizes lookup failures toValueError. - Adds unit tests covering key-order independence and new error cases, plus a changelog entry.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
source/keyboardHandler.py |
Refactors key-name resolution and updates fromName main-key selection + error handling to accept indeterminate key order. |
tests/unit/test_keyboardHandler.py |
Adds unit tests for reordered key names, NVDA modifier handling, and ValueError cases. |
user_docs/en/changes.md |
Documents the developer-facing behavior change and new ValueError semantics. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Hi @LeonarddeR #8201 still hasn't been triaged yet. Closing until we have triaged and decided on an acceptable solution |
|
@seanbudd please note that @SaschaCowley wrote:
I considered this as a go. Note that the fact that there is a bug in play here, although small, is really evident. |
|
Please read the triaging docs we don't allow contributors to triage their own issues |
Link to issue number:
Fixes #8201
Summary of the issue:
NVDA can create a keyboard gesture from a written key name. It treats the last key in the name as the main key. All other keys become modifiers. Hand-written names do not always place the main key last. The name "alt+b+control" then yields a gesture with control as the main key and the letter b as a modifier. A letter has no modifier name. The gesture identifier therefore becomes the garbage value
kb:+alt+control. The intended key is lost. Invalid key names also escape with an undocumentedKeyError.Description of user facing changes:
None. This change is developer oriented.
Description of developer facing changes:
KeyboardInputGesture.fromNamenow treats the non modifier key as the main key, regardless of its position in the name. Names containing only modifiers keep the last key as the main key. The method raisesValueErrorfor a name with multiple non modifier keys, an unknown key name, or an empty name. Unknown names previously escaped withKeyError.Description of development approach:
fromNameresolves each key name in the sequence to a virtual key code. It used the last resolved key as the main key. This is only correct when the main key is written last.The resolution of a single key name moves to a new
_resolveKeyNameclassmethod. It returns virtual key code / extended flag pairs. For a character, it also returns the modifiers required to produce that character. Lookup failures propagate asLookupError.fromNamecollects the resolved keys and re-raises lookup failures asValueError. It then determines which keys are modifiers. A key is a modifier when its code is inNORMAL_MODIFIER_KEYSor when it is an NVDA modifier key. This is the same test the gesture itself uses for itsisModifierproperty. More than one non modifier key raisesValueError. The single non modifier key becomes the main key. When there is none, the last key remains the main key. Names for bare modifiers therefore keep working.Testing strategy:
New unit test module
tests/unit/test_keyboardHandler.py. The tests cover:ValueErroris raised for multiple main keys, a duplicated main key, an unknown key name and an empty name.The tests were written first and failed against the old implementation.
Known issues with pull request:
None known.
Code Review Checklist: