Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite6e4efc

Browse files
lysnikolaoueugenetrigubaambv
authored
[3.13]gh-119357: Increase test coverage for keymap in _pyrepl (GH-119358) (#119414)
(cherry picked from commit73ab83b)Co-authored-by: Eugene Triguba <eugenetriguba@gmail.com>Co-authored-by: Łukasz Langa <lukasz@langa.pl>
1 parent3e30a38 commite6e4efc

File tree

3 files changed

+94
-53
lines changed

3 files changed

+94
-53
lines changed

‎Lib/_pyrepl/completing_reader.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
# types
3131
Command=commands.Command
3232
ifFalse:
33-
from .typesimportCallback,SimpleContextManager,KeySpec,CommandName
33+
from .typesimportKeySpec,CommandName
3434

3535

3636
defprefix(wordlist:list[str],j:int=0)->str:

‎Lib/_pyrepl/keymap.py‎

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,32 @@
1919
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2020

2121
"""
22-
functions for parsing keyspecs
22+
Keymap contains functions for parsing keyspecs and turning keyspecs into
23+
appropriate sequences.
2324
24-
Support for turning keyspecs into appropriate sequences.
25+
A keyspec is a string representing a sequence of key presses that can
26+
be bound to a command. All characters other than the backslash represent
27+
themselves. In the traditional manner, a backslash introduces an escape
28+
sequence.
2529
26-
pyrepl uses it's own bastardized keyspec format, which is meant to be
27-
a strict superset of readline's\"KEYSEQ\" format (which is to say
28-
that if you can come up with a spec readline accepts that this
29-
doesn't, you've found a bug and should tell me about it).
30-
31-
Note that this is the `\\C-o' style of readline keyspec, not the
32-
`Control-o' sort.
33-
34-
A keyspec is a string representing a sequence of keypresses that can
35-
be bound to a command.
36-
37-
All characters other than the backslash represent themselves. In the
38-
traditional manner, a backslash introduces a escape sequence.
30+
pyrepl uses its own keyspec format that is meant to be a strict superset of
31+
readline's KEYSEQ format. This means that if a spec is found that readline
32+
accepts that this doesn't, it should be logged as a bug. Note that this means
33+
we're using the `\\C-o' style of readline's keyspec, not the `Control-o' sort.
3934
4035
The extension to readline is that the sequence\\<KEY> denotes the
41-
sequence ofcharaters produced by hitting KEY.
36+
sequence ofcharacters produced by hitting KEY.
4237
4338
Examples:
44-
45-
`a' - what you get when you hit the `a' key
39+
`a' - what you get when you hit the `a' key
4640
`\\EOA' - Escape - O - A (up, on my terminal)
4741
`\\<UP>' - the up arrow key
48-
`\\<up>' - ditto (keynames are caseinsensitive)
42+
`\\<up>' - ditto (keynames are case-insensitive)
4943
`\\C-o', `\\c-o' - control-o
5044
`\\M-.' - meta-period
5145
`\\E.' - ditto (that's how meta works for pyrepl)
5246
`\\<tab>', `\\<TAB>', `\\t', `\\011', '\\x09', '\\X09', '\\C-i', '\\C-I'
53-
- all of these are the tab character. Can you think of any more?
47+
- all of these are the tab character.
5448
"""
5549

5650
_escapes= {
@@ -111,7 +105,17 @@ class KeySpecError(Exception):
111105
pass
112106

113107

114-
def_parse_key1(key,s):
108+
defparse_keys(keys:str)->list[str]:
109+
"""Parse keys in keyspec format to a sequence of keys."""
110+
s=0
111+
r:list[str]= []
112+
whiles<len(keys):
113+
k,s=_parse_single_key_sequence(keys,s)
114+
r.extend(k)
115+
returnr
116+
117+
118+
def_parse_single_key_sequence(key:str,s:int)->tuple[list[str],int]:
115119
ctrl=0
116120
meta=0
117121
ret=""
@@ -183,20 +187,11 @@ def _parse_key1(key, s):
183187
ret=f"ctrl{ret}"
184188
else:
185189
raiseKeySpecError("\\C- followed by invalid key")
186-
ifmeta:
187-
ret= ["\033",ret]
188-
else:
189-
ret= [ret]
190-
returnret,s
191190

192-
193-
defparse_keys(key:str)->list[str]:
194-
s=0
195-
r= []
196-
whiles<len(key):
197-
k,s=_parse_key1(key,s)
198-
r.extend(k)
199-
returnr
191+
result= [ret],s
192+
ifmeta:
193+
result[0].insert(0,"\033")
194+
returnresult
200195

201196

202197
defcompile_keymap(keymap,empty=b""):

‎Lib/test/test_pyrepl/test_keymap.py‎

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,78 @@
1+
importstring
12
importunittest
23

3-
from_pyrepl.keymapimportparse_keys,compile_keymap
4+
from_pyrepl.keymapimport_keynames,_escapes,parse_keys,compile_keymap,KeySpecError
45

56

67
classTestParseKeys(unittest.TestCase):
78
deftest_single_character(self):
8-
self.assertEqual(parse_keys("a"), ["a"])
9-
self.assertEqual(parse_keys("b"), ["b"])
10-
self.assertEqual(parse_keys("1"), ["1"])
9+
"""Ensure that single ascii characters or single digits are parsed as single characters."""
10+
test_cases= [(key, [key])forkeyinstring.ascii_letters+string.digits]
11+
fortest_key,expected_keysintest_cases:
12+
withself.subTest(f"{test_key} should be parsed as{expected_keys}"):
13+
self.assertEqual(parse_keys(test_key),expected_keys)
14+
15+
deftest_keynames(self):
16+
"""Ensure that keynames are parsed to their corresponding mapping.
17+
18+
A keyname is expected to be of the following form:\\<keyname> such as\\<left>
19+
which would get parsed as "left".
20+
"""
21+
test_cases= [(f"\\<{keyname}>", [parsed_keyname])forkeyname,parsed_keynamein_keynames.items()]
22+
fortest_key,expected_keysintest_cases:
23+
withself.subTest(f"{test_key} should be parsed as{expected_keys}"):
24+
self.assertEqual(parse_keys(test_key),expected_keys)
1125

1226
deftest_escape_sequences(self):
13-
self.assertEqual(parse_keys("\\n"), ["\n"])
14-
self.assertEqual(parse_keys("\\t"), ["\t"])
15-
self.assertEqual(parse_keys("\\\\"), ["\\"])
16-
self.assertEqual(parse_keys("\\'"), ["'"])
17-
self.assertEqual(parse_keys('\\"'),['"'])
27+
"""Ensure that escaping sequences are parsed to their corresponding mapping."""
28+
test_cases= [(f"\\{escape}", [parsed_escape])forescape,parsed_escapein_escapes.items()]
29+
fortest_key,expected_keysintest_cases:
30+
withself.subTest(f"{test_key} should be parsed as{expected_keys}"):
31+
self.assertEqual(parse_keys(test_key),expected_keys)
1832

1933
deftest_control_sequences(self):
20-
self.assertEqual(parse_keys("\\C-a"), ["\x01"])
21-
self.assertEqual(parse_keys("\\C-b"), ["\x02"])
22-
self.assertEqual(parse_keys("\\C-c"), ["\x03"])
34+
"""Ensure that supported control sequences are parsed successfully."""
35+
keys= ["@","[","]","\\","^","_","\\<space>","\\<delete>"]
36+
keys.extend(string.ascii_letters)
37+
test_cases= [(f"\\C-{key}",chr(ord(key)&0x1F))forkeyin []]
38+
fortest_key,expected_keysintest_cases:
39+
withself.subTest(f"{test_key} should be parsed as{expected_keys}"):
40+
self.assertEqual(parse_keys(test_key),expected_keys)
2341

2442
deftest_meta_sequences(self):
2543
self.assertEqual(parse_keys("\\M-a"), ["\033","a"])
2644
self.assertEqual(parse_keys("\\M-b"), ["\033","b"])
2745
self.assertEqual(parse_keys("\\M-c"), ["\033","c"])
2846

29-
deftest_keynames(self):
30-
self.assertEqual(parse_keys("\\<up>"), ["up"])
31-
self.assertEqual(parse_keys("\\<down>"), ["down"])
32-
self.assertEqual(parse_keys("\\<left>"), ["left"])
33-
self.assertEqual(parse_keys("\\<right>"), ["right"])
34-
3547
deftest_combinations(self):
3648
self.assertEqual(parse_keys("\\C-a\\n\\<up>"), ["\x01","\n","up"])
3749
self.assertEqual(parse_keys("\\M-a\\t\\<down>"), ["\033","a","\t","down"])
3850

51+
deftest_keyspec_errors(self):
52+
cases= [
53+
("\\Ca","\\C must be followed by `-'"),
54+
("\\ca","\\C must be followed by `-'"),
55+
("\\C-\\C-","doubled\\C-"),
56+
("\\Ma","\\M must be followed by `-'"),
57+
("\\ma","\\M must be followed by `-'"),
58+
("\\M-\\M-","doubled\\M-"),
59+
("\\<left","unterminated\\<"),
60+
("\\<unsupported>","unrecognised keyname"),
61+
("\\大","unknown backslash escape"),
62+
("\\C-\\<backspace>","\\C- followed by invalid key")
63+
]
64+
fortest_keys,expected_errincases:
65+
withself.subTest(f"{test_keys} should give error{expected_err}"):
66+
withself.assertRaises(KeySpecError)ase:
67+
parse_keys(test_keys)
68+
self.assertIn(expected_err,str(e.exception))
69+
70+
deftest_index_errors(self):
71+
test_cases= ["\\","\\C","\\C-\\C"]
72+
fortest_keysintest_cases:
73+
withself.assertRaises(IndexError):
74+
parse_keys(test_keys)
75+
3976

4077
classTestCompileKeymap(unittest.TestCase):
4178
deftest_empty_keymap(self):
@@ -72,3 +109,12 @@ def test_nested_multiple_keymaps(self):
72109
keymap= {b"a": {b"b": {b"c":"action"}}}
73110
result=compile_keymap(keymap)
74111
self.assertEqual(result, {b"a": {b"b": {b"c":"action"}}})
112+
113+
deftest_clashing_definitions(self):
114+
km= {b'a':'c',b'a'+b'b':'d'}
115+
withself.assertRaises(KeySpecError):
116+
compile_keymap(km)
117+
118+
deftest_non_bytes_key(self):
119+
withself.assertRaises(TypeError):
120+
compile_keymap({123:'a'})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp