Skip to content

Commit 81fffcf

Browse files
committed
generator: some linting, many docstrings
1 parent 01aec32 commit 81fffcf

File tree

1 file changed

+117
-56
lines changed

1 file changed

+117
-56
lines changed

generator/layout_generator.py

Lines changed: 117 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from .keycode_us_ref import Keycode
2626
from .virtualkey_table_us import VIRTUAL_KEY_US
2727

28-
""" debug level dev: only show dev prints """
28+
""" debug level dev: only show dev prints (use for print debugging) """
2929
DEBUG_DEV = -1
3030
""" debug level error: only show errors """
3131
DEBUG_ERROR = 1
@@ -71,59 +71,76 @@
7171
7272
"""
7373

74+
7475
def _echo(*text, nl=True, **kwargs):
75-
""" printout things using click with some defaults """
76+
"""printout things using click joining all varargs"""
7677
text = [
7778
(item if isinstance(item, str) else repr(item))
7879
for item in text
7980
]
8081
click.secho(" ".join(text), nl=nl, **kwargs)
8182

83+
8284
def echo(*text, nl=True, **kwargs):
83-
""" print as info """
85+
"""print as info"""
8486
if DEBUG_LEVEL >= DEBUG_INFO:
8587
_echo(*text, nl=nl, **kwargs)
8688

89+
8790
def echoE(*text, nl=True, **kwargs):
88-
""" print as error, in red by default """
91+
"""print as error, in red by default"""
8992
if "fg" not in kwargs:
9093
kwargs["fg"] = "red"
9194
if DEBUG_LEVEL >= DEBUG_ERROR:
9295
_echo(*text, nl=nl, **kwargs)
9396

94-
def echoF(*text, nl=True, **kwargs):
95-
""" print only in dev mode """
97+
98+
def echoD(*text, nl=True, **kwargs):
99+
"""print only in dev mode, use for print debugging"""
96100
if DEBUG_LEVEL == DEBUG_DEV:
97101
_echo(*text, nl=nl, **kwargs)
98102

99103

100104
def jprint(data, nl=True, **kwargs):
105+
"""dump a structure as json"""
101106
echo("<<< " + str(len(data)) + " >>>", nl, **kwargs)
102107
echo(json.dumps(data, indent=2), nl, **kwargs)
103108

104109

105-
def filter_codepoints(text):
106-
return text.replace("\r", "\n")
110+
def get_v_to_k():
111+
"""create the reverse virtualkey/keyname table"""
112+
virtualkey_to_keyname = {}
113+
for name, vkey in name_to_virtualkey.items():
114+
if vkey not in virtualkey_to_keyname:
115+
virtualkey_to_keyname[vkey] = []
116+
virtualkey_to_keyname[vkey].append(name)
117+
return virtualkey_to_keyname
118+
119+
120+
virtualkey_to_keyname = get_v_to_k()
107121

108122

109-
virtualkey_to_keyname = {}
110-
for name, vkey in name_to_virtualkey.items():
111-
if vkey not in virtualkey_to_keyname:
112-
virtualkey_to_keyname[vkey] = []
113-
virtualkey_to_keyname[vkey].append(name)
123+
def filter_codepoints(text):
124+
"""filter converted codepoints from XML"""
125+
return text.replace("\r", "\n")
114126

115127

116128
def list_keycode_name(key, value):
129+
"""list the keycode names associated with a virtual key name"""
117130
output = []
118131
if key in virtualkey_to_keyname:
119132
for name in virtualkey_to_keyname[key]:
120-
output.append( (name, value) )
133+
output.append((name, value))
121134
else:
122135
output = [(key, value)]
123136
return output
124137

125138

126139
def get_name_to_keycode():
140+
"""
141+
create the table mapping virtual key names to keycodes
142+
from the adafruit_hid Keycode file
143+
"""
127144
name_to_kc = {}
128145
kcnums = [
129146
(name, getattr(Keycode, name))
@@ -161,6 +178,16 @@ def modif(res):
161178

162179

163180
def get_vk_to_sc(data):
181+
"""
182+
Analyse the XML file to make the table of all keys.
183+
Each entry:
184+
- is indexed by a virtual key name or made-up name
185+
- is associated with a scancode
186+
- has a letter associated with different modifiers (or lack thereof)
187+
- dead keys and combined keys have additional information
188+
- dead = True means it's the dead key (don't press it alone)
189+
- firstkey/secondkey are the respective keys for dead key combinations
190+
"""
164191
keything = xmltodict.parse(data)
165192
physical_keys = keything["KeyboardLayout"]["PhysicalKeys"]["PK"]
166193
# jprint(physical_keys)
@@ -210,10 +237,12 @@ def get_vk_to_sc(data):
210237
firstkey = res["DeadKeyTable"]["@Accent"]
211238
# the name of the dead key for the Keycode table
212239
if "@Name" in res["DeadKeyTable"]:
213-
deadname = res["DeadKeyTable"]["@Name"].replace(" ","_")
240+
deadname = res["DeadKeyTable"]["@Name"].replace(" ", "_")
214241
else:
215242
# if none, generate one with "_" to exclude it from Keycode
216-
deadname = "_accent" + "".join(["_" + str(ord(x)) for x in firstkey])
243+
deadname = "_accent" + "".join(
244+
["_" + str(ord(x)) for x in firstkey]
245+
)
217246
# dead key base: in keycode, not in layout
218247
if deadname not in vk_to_sc:
219248
vk_to_sc[deadname] = {
@@ -242,6 +271,7 @@ def get_vk_to_sc(data):
242271

243272

244273
def get_scancode_to_keycode():
274+
"""create the table associating scancodes and keycodes from the US XML file"""
245275
name_to_kc = get_name_to_keycode()
246276
name_to_kc_left = set(name_to_kc)
247277
vk_to_sc = get_vk_to_sc(VIRTUAL_KEY_US)
@@ -257,42 +287,49 @@ def get_scancode_to_keycode():
257287
return sc_to_kc
258288

259289

260-
# TODO: add non-US scancodes/keycodes in `sc_to_kc`
290+
# TODO: are there missing non-US scancodes/keycodes in `sc_to_kc` ?
261291

262292

263-
"""
264-
The actual conversion from scan codes to key codes
265-
Missing unidentified names
266-
NUMPAD is particularly missing (it's refed as arrows, page up, etc.)
267-
"""
268-
# jprint(sc_to_kc)
269-
# print("name_to_kc_left", len(name_to_kc_left), sorted(name_to_kc_left))
270-
# print("vk_to_sc_left", len(vk_to_sc_left), sorted(vk_to_sc_left))
271-
# kc_to_sc = dict([(y,x) for (x,y) in sc_to_kc.items()])
272-
273293
########################################################################
274294

275295

276296
class LayoutData:
297+
"""
298+
Asimple struct class to carry around the layout information.
299+
- asciis has the keycode information for each low ascii character (with shift bit)
300+
- charas has said characters, for display in the comment string and testing existence
301+
- atgr is the list of letters that need alt-gr pressed
302+
- high is the list of high-ascii/unicode letters and their keycode
303+
- keycodes is the table associating key names with keycodes (for the Keycode class)
304+
- combined is the table of combined keys
305+
A combined key has two bytes:
306+
- the keycode to the first key, with the high bit as the shift bit
307+
- the letter for the second key (assumed to be low ascii)
308+
- the second key's high bit is the altgr bit for the first key
309+
"""
277310
def __init__(self, asciis, charas, altgr, high, keycodes, combined):
278311
self.asciis = asciis
279312
self.charas = charas
280313
self.altgr = altgr
281314
self.high = high
282315
self.keycodes = keycodes
283316
self.combined = combined
317+
284318
def __repr__(self):
285-
return repr({
286-
"asciis": self.asciis,
287-
"charas": self.charas,
288-
"altgr": self.altgr,
289-
"high": self.high,
290-
"keycodes": self.keycodes,
291-
"combined": self.combined,
292-
})
319+
return repr(
320+
{
321+
"asciis": self.asciis,
322+
"charas": self.charas,
323+
"altgr": self.altgr,
324+
"high": self.high,
325+
"keycodes": self.keycodes,
326+
"combined": self.combined,
327+
}
328+
)
293329

294330

295331
def get_layout_data(virtual_key_defs_lang):
332+
"""Create the layout data from a language file."""
296333
asciis = [0] * 128
297334
charas = [""] * 128
298335
NEED_ALTGR = []
@@ -347,15 +384,15 @@ def get_layout_data(virtual_key_defs_lang):
347384
asciis[pos] = keycode
348385
charas[pos] = letter
349386
else:
350-
echoF("dead", key_info)
387+
echoD("dead", key_info)
351388
KEYCODES.update(list_keycode_name(virtualkey, keycode))
352389
# KEYCODES[virtualkey] = keycode
353390
else:
354391
if letter not in HIGHER_ASCII:
355392
if not dead:
356393
HIGHER_ASCII[letter] = keycode
357394
else:
358-
echoF("dead", key_info)
395+
echoD("dead", key_info)
359396
KEYCODES.update(list_keycode_name(virtualkey, keycode))
360397
# KEYCODES[virtualkey] = keycode
361398
else:
@@ -430,6 +467,7 @@ def add_alt_gr(letter):
430467

431468

432469
def make_layout_file(layout_data):
470+
"""make the layout file contents"""
433471
output_file_data = (
434472
COMMON_HEADER_COPYRIGHT
435473
+ "from keyboard_layout import KeyboardLayoutBase\n"
@@ -461,40 +499,38 @@ def make_layout_file(layout_data):
461499
)
462500
for k, c in layout_data.high.items():
463501
output_file_data += f" {repr(k)}: 0x{c:02x},\n"
464-
output_file_data += (
465-
" }\n"
466-
" COMBINED_KEYS = {\n"
467-
)
502+
output_file_data += " }\n" " COMBINED_KEYS = {\n"
468503
for k, c in layout_data.combined.items():
469504
first, second, altgr = c
470505
second = ord(second) | altgr
471506
output_file_data += (
472507
f" {repr(k)}: "
473-
f"b\"\\x{first:02x}\\x{second:02x}\","
508+
f'b"\\x{first:02x}\\x{second:02x}",'
474509
"\n"
475510
)
476-
output_file_data += (
477-
" }\n"
478-
)
511+
output_file_data += " }\n"
479512
return output_file_data
480513

514+
481515
def output_layout_file(output_file, output_file_data):
516+
"""write out the layout file"""
482517
with open(output_file, "w") as fp:
483518
fp.write(output_file_data)
484519

485520

486521
def make_keycode_file(layout_data):
487-
output_file_data = (
488-
COMMON_HEADER_COPYRIGHT + "class Keycode:\n"
489-
)
522+
"""make the keycode file contents"""
523+
output_file_data = COMMON_HEADER_COPYRIGHT + "class Keycode:\n"
524+
490525
def ck(x):
491526
l = x[0]
492527
if len(l) == 2:
493528
l = l + " "
494529
if len(l) > 5:
495530
l = l.ljust(20)
496531
return (len(l), l)
497-
for name,code in natsort.natsorted(layout_data.keycodes.items(), key=ck):
532+
533+
for name, code in natsort.natsorted(layout_data.keycodes.items(), key=ck):
498534
# code = layout_data.keycodes[name]
499535
if name[0] != "_":
500536
output_file_data += f" {name} = 0x{code:02x}\n"
@@ -509,21 +545,45 @@ def modifier_bit(cls, keycode):
509545
"""
510546
return output_file_data
511547

548+
512549
def output_keycode_file(output_file, output_file_data):
550+
"""write out the keycode file"""
513551
with open(output_file, "w") as fp:
514552
fp.write(output_file_data)
515553

516554

517555
@click.group(invoke_without_command=True)
518-
@click.option("--keyboard", "-k", required=True)
519-
@click.option("--lang", "-l", default="")
520-
@click.option("--platform", "-p", default="win")
521-
@click.option("--output", "-o", is_flag=True)
522-
@click.option("--output-layout", default="")
523-
@click.option("--output-keycode", default="")
524-
@click.option("--debug", "-d", type=click.INT, default=1)
556+
@click.option(
557+
"--keyboard", "-k", required=True,
558+
help="The XML layout file, or URL to the layout on kbdlayout.info."
559+
)
560+
@click.option(
561+
"--lang", "-l", default="",
562+
help="The language string to use in the output file name, defaults to the last part of the file name or the language part of the URL."
563+
)
564+
@click.option(
565+
"--platform", "-p", default="win",
566+
help="The platform string to use in the output file name. Only windows currently."
567+
)
568+
@click.option(
569+
"--output", "-o", is_flag=True,
570+
help="Activate writing out the layout and keycode files."
571+
)
572+
@click.option(
573+
"--output-layout", default="",
574+
help="Override the layout output file path and name."
575+
)
576+
@click.option(
577+
"--output-keycode", default="",
578+
help="Override the keycode output file path and name."
579+
)
580+
@click.option(
581+
"--debug", "-d", default=1,
582+
help="Set the debug level, -1 (dev only), 0 (silent), 1 (errors), 2 (all), default is 1"
583+
)
525584
@click.option("--show", "-s", default="")
526585
def main(keyboard, lang, platform, output, output_layout, output_keycode, debug, show):
586+
"""Make keyboard layout files from layout XML data."""
527587
global DEBUG_LEVEL
528588
DEBUG_LEVEL = debug
529589
echo(">", keyboard, fg="green")
@@ -584,5 +644,6 @@ def main(keyboard, lang, platform, output, output_layout, output_keycode, debug,
584644
if show == "keycode" or show == "s":
585645
print(data_keycode)
586646

647+
587648
if __name__ == "__main__":
588649
main()

0 commit comments

Comments
 (0)