Merge pull request #1578 from dbungert/lp-2008271-validate-kbd-layout

keyboard: validate layout and variant
This commit is contained in:
Dan Bungert 2023-03-01 07:04:57 -07:00 committed by GitHub
commit 359a5caa7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 48 deletions

View File

@ -20,7 +20,11 @@ import os
import yaml
from subiquity.common.resources import resource_path
from subiquity.common.types import KeyboardSetting
from subiquity.common.serialize import Serializer
from subiquity.common.types import (
KeyboardLayout,
KeyboardSetting,
)
log = logging.getLogger("subiquity.models.keyboard")
@ -71,6 +75,8 @@ class KeyboardModel:
self.default_setting = from_config_file(self.config_path)
else:
self.default_setting = self.layout_for_lang['en_US.UTF-8']
self.keyboard_list = KeyboardList()
self.keyboard_list.load_language('C')
self._setting = None
@property
@ -81,8 +87,18 @@ class KeyboardModel:
@setting.setter
def setting(self, value):
self.validate_setting(value)
self._setting = value
def validate_setting(self, setting: KeyboardSetting) -> None:
kbd_layout = self.keyboard_list.layout_map.get(setting.layout)
if kbd_layout is None:
raise ValueError(f'Unknown keyboard layout "{setting.layout}"')
if not any(variant.code == setting.variant
for variant in kbd_layout.variants):
raise ValueError(f'Unknown keyboard variant "{setting.variant}" '
f'for layout "{setting.layout}"')
def render_config_file(self):
options = ""
if self.setting.toggle:
@ -130,3 +146,44 @@ class KeyboardModel:
for k, v in data.items():
ret[k] = KeyboardSetting(**v)
return ret
class KeyboardList:
def __init__(self):
self._kbnames_dir = resource_path('kbds')
self.serializer = Serializer(compact=True)
self._clear()
def _file_for_lang(self, code):
return os.path.join(self._kbnames_dir, code + '.jsonl')
def _has_language(self, code):
return os.path.exists(self._file_for_lang(code))
def load_language(self, code):
if '.' in code:
code = code.split('.')[0]
if not self._has_language(code):
code = code.split('_')[0]
if not self._has_language(code):
code = 'C'
if code == self.current_lang:
return
self._clear()
with open(self._file_for_lang(code)) as kbdnames:
self.layouts = []
self.layout_map = {}
for line in kbdnames:
kbd_layout = self.serializer.from_json(KeyboardLayout, line)
self.layouts.append(kbd_layout)
self.layout_map[kbd_layout.code] = kbd_layout
self.current_lang = code
def _clear(self):
self.current_lang = None
self.layouts = []
self.layout_map = {}

View File

@ -29,11 +29,21 @@ class TestKeyboardModel(SubiTestCase):
self.assertIsNone(self.model._setting)
self.assertEqual('us', self.model.setting.layout)
def testSetToZZ(self):
val = KeyboardSetting(layout='zz')
self.model.setting = val
self.assertEqual(val, self.model.setting)
self.assertEqual(val, self.model._setting)
@parameterized.expand((['zz'], ['en']))
def testSetToInvalidLayout(self, layout):
initial = self.model.setting
val = KeyboardSetting(layout=layout)
with self.assertRaises(ValueError):
self.model.setting = val
self.assertEqual(initial, self.model.setting)
@parameterized.expand((['zz']))
def testSetToInvalidVariant(self, variant):
initial = self.model.setting
val = KeyboardSetting(layout='us', variant=variant)
with self.assertRaises(ValueError):
self.model.setting = val
self.assertEqual(initial, self.model.setting)
@parameterized.expand([
['ast_ES.UTF-8', 'es', 'ast'],

View File

@ -28,7 +28,6 @@ from subiquity.common.resources import resource_path
from subiquity.common.serialize import Serializer
from subiquity.common.types import (
AnyStep,
KeyboardLayout,
KeyboardSetting,
KeyboardSetup,
)
@ -118,44 +117,6 @@ def for_ui(setting):
layout=layout, variant=variant, toggle=setting.toggle)
class KeyboardList:
def __init__(self):
self._kbnames_dir = resource_path('kbds')
self.serializer = Serializer(compact=True)
self._clear()
def _file_for_lang(self, code):
return os.path.join(self._kbnames_dir, code + '.jsonl')
def _has_language(self, code):
return os.path.exists(self._file_for_lang(code))
def load_language(self, code):
if '.' in code:
code = code.split('.')[0]
if not self._has_language(code):
code = code.split('_')[0]
if not self._has_language(code):
code = 'C'
if code == self.current_lang:
return
self._clear()
with open(self._file_for_lang(code)) as kbdnames:
self.layouts = [
self.serializer.from_json(KeyboardLayout, line)
for line in kbdnames
]
self.current_lang = code
def _clear(self):
self.current_lang = None
self.layouts = []
class KeyboardController(SubiquityController):
endpoint = API.keyboard
@ -177,7 +138,6 @@ class KeyboardController(SubiquityController):
self.serializer = Serializer(compact=True)
self.pc105_steps = None
self.needs_set_keyboard = False
self.keyboard_list = KeyboardList()
super().__init__(app)
def load_autoinstall_data(self, data):
@ -213,10 +173,10 @@ class KeyboardController(SubiquityController):
async def GET(self) -> KeyboardSetup:
lang = self.app.base_model.locale.selected_language
self.keyboard_list.load_language(lang)
self.model.keyboard_list.load_language(lang)
return KeyboardSetup(
setting=for_ui(self.model.setting_for_lang(lang)),
layouts=self.keyboard_list.layouts)
layouts=self.model.keyboard_list.layouts)
async def POST(self, data: KeyboardSetting):
log.debug(data)