Skip to content

Commit

Permalink
Merge pull request #6230 from KKoukiou/fedora-42
Browse files Browse the repository at this point in the history
Enhance reading of the Live keyboard layouts
  • Loading branch information
KKoukiou authored Mar 4, 2025
2 parents 2cd9472 + 73b0291 commit 79c33e7
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 24 deletions.
25 changes: 15 additions & 10 deletions pyanaconda/modules/localization/live_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.i18n import _
from pyanaconda.core.util import execWithCaptureAsLiveUser
from pyanaconda.modules.common.errors.configuration import KeyboardConfigurationError

log = get_module_logger(__name__)

Expand All @@ -34,7 +36,6 @@ def get_live_keyboard_instance():
if conf.system.provides_liveuser:
return GnomeShellKeyboard()

# TODO: Add support for other Live systems
return None


Expand Down Expand Up @@ -91,14 +92,18 @@ def _convert_to_xkb_format(self, sources):
result = []

for t in sources:
# keep only 'xkb' type and ignore 'ibus' variants which can't be used in localed
if t[0] == "xkb":
layout = t[1]
# change layout variant from 'cz+qwerty' to 'cz (qwerty)'
if '+' in layout:
layout, variant = layout.split('+')
result.append(f"{layout} ({variant})")
else:
result.append(layout)
# keep only 'xkb' type and raise an error on 'ibus' variants which can't
# be used in localed
if t[0] != "xkb":
msg = _("The live system has layout '%s' which can't be used for installation.")
raise KeyboardConfigurationError(msg.format(t[1]))

layout = t[1]
# change layout variant from 'cz+qwerty' to 'cz (qwerty)'
if '+' in layout:
layout, variant = layout.split('+')
result.append(f"{layout} ({variant})")
else:
result.append(layout)

return result
15 changes: 15 additions & 0 deletions pyanaconda/modules/localization/localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,21 @@ def _update_settings_from_task(self, result):
self.set_vc_keymap(vc_keymap)
self.set_x_layouts(x_layouts)

def get_keyboard_configuration_with_task(self):
"""Get current keyboard configuration without storing it into module.
This configuration will be used for the installation at the time of task execution.
The task is read only, the results are not stored anywhere.
:returns: a task reading keyboard configuration
"""
task = GetMissingKeyboardConfigurationTask(
localed_wrapper=self.localed_wrapper,
x_layouts=self.x_layouts,
vc_keymap=self.vc_keymap,
)
return task

def apply_keyboard_with_task(self):
"""Apply keyboard configuration to the current system.
Expand Down
28 changes: 27 additions & 1 deletion pyanaconda/modules/localization/localization_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# Red Hat, Inc.
#

from dasbus.server.interface import dbus_interface, dbus_signal
from dasbus.server.interface import dbus_class, dbus_interface, dbus_signal
from dasbus.server.property import emits_properties_changed
from dasbus.typing import * # pylint: disable=wildcard-import

Expand All @@ -27,6 +27,21 @@
from pyanaconda.modules.common.containers import TaskContainer
from pyanaconda.modules.common.structures.keyboard_layout import KeyboardLayout
from pyanaconda.modules.common.structures.language import LanguageData, LocaleData
from pyanaconda.modules.common.task import TaskInterface


@dbus_class
class KeyboardConfigurationTaskInterface(TaskInterface):
"""Interface to get keyboard configuration data."""

@staticmethod
def convert_result(value):
"""Convert value to publishable result.
From:
(("us", "cs (qwerty)"), "cs-qwerty")
"""
return get_variant(Tuple[List[Str], Str], value)


@dbus_interface(LOCALIZATION.interface_name)
Expand Down Expand Up @@ -255,6 +270,17 @@ def PopulateMissingKeyboardConfigurationWithTask(self) -> ObjPath:
self.implementation.populate_missing_keyboard_configuration_with_task()
)

def GetKeyboardConfigurationWithTask(self) -> ObjPath:
"""Get current keyboard configuration without storing it into module.
This task will give you a potential configuration to be installed at the time of
task execution. The task is read only, the results are not used anywhere by
the localization module.
"""
return TaskContainer.to_object_path(
self.implementation.get_keyboard_configuration_with_task()
)

def ApplyKeyboardWithTask(self) -> ObjPath:
"""Apply keyboard configuration to the current system.
Expand Down
8 changes: 8 additions & 0 deletions pyanaconda/modules/localization/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from pyanaconda.modules.common.errors.configuration import KeyboardConfigurationError
from pyanaconda.modules.common.task import Task
from pyanaconda.modules.localization.installation import write_vc_configuration
from pyanaconda.modules.localization.localization_interface import (
KeyboardConfigurationTaskInterface,
)
from pyanaconda.modules.localization.utils import get_missing_keyboard_configuration

log = get_module_logger(__name__)
Expand Down Expand Up @@ -81,6 +84,9 @@ def __init__(self, localed_wrapper, x_layouts, vc_keymap):
self._x_layouts = x_layouts
self._vc_keymap = vc_keymap

def for_publication(self):
return KeyboardConfigurationTaskInterface(self)

@property
def name(self):
return "Get missing keyboard settings."
Expand All @@ -90,6 +96,8 @@ def run(self):
:returns: tuple of X layouts and VC keyboard settings
:rtype: (list(str), str))
:raises: KeyboardConfigurationError exception when we should use unsupported layouts
from Live
"""
return get_missing_keyboard_configuration(self._localed_wrapper,
self._x_layouts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import unittest
from unittest.mock import patch

import pytest

from pyanaconda.modules.common.errors.configuration import KeyboardConfigurationError
from pyanaconda.modules.localization.live_keyboard import (
GnomeShellKeyboard,
get_live_keyboard_instance,
Expand All @@ -35,13 +38,24 @@ def test_get_live_keyboard_instance(self, mocked_conf):
mocked_conf.system.provides_liveuser = False
assert get_live_keyboard_instance() is None

def _check_gnome_shell_layouts_conversion(self, mocked_exec_with_capture, system_input, output):
def _check_gnome_shell_layouts_conversion(self,
mocked_exec_with_capture,
system_input,
output,
should_raise):
mocked_exec_with_capture.reset_mock()
mocked_exec_with_capture.return_value = system_input

gs = GnomeShellKeyboard()

assert gs.read_keyboard_layouts() == output
if should_raise:
with pytest.raises(KeyboardConfigurationError):
gs.read_keyboard_layouts()
return

result = gs.read_keyboard_layouts()

assert result == output
mocked_exec_with_capture.assert_called_once_with(
"gsettings",
["get", "org.gnome.desktop.input-sources", "sources"]
Expand All @@ -54,40 +68,46 @@ def test_gnome_shell_keyboard(self, mocked_exec_with_capture):
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"[('xkb', 'cz')]",
output=["cz"]
output=["cz"],
should_raise=False
)

# test one complex layout is set
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"[('xkb', 'cz+qwerty')]",
output=["cz (qwerty)"]
output=["cz (qwerty)"],
should_raise=False
)

# test multiple layouts are set
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"[('xkb', 'cz+qwerty'), ('xkb', 'us'), ('xkb', 'cz+dvorak-ucw')]",
output=["cz (qwerty)", "us", "cz (dvorak-ucw)"]
output=["cz (qwerty)", "us", "cz (dvorak-ucw)"],
should_raise=False
)

# test layouts with ibus (ibus is ignored)
# test layouts with ibus (ibus will raise error)
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"[('xkb', 'cz'), ('ibus', 'libpinyin')]",
output=["cz"]
output=[],
should_raise=True
)

# test only ibus layout
# test only ibus layout (raise the error)
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"[('ibus', 'libpinyin')]",
output=[]
output=[],
should_raise=True
)

# test wrong input
self._check_gnome_shell_layouts_conversion(
mocked_exec_with_capture=mocked_exec_with_capture,
system_input=r"wrong input",
output=[]
output=[],
should_raise=False
)
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@

import langtable
from dasbus.signal import Signal
from dasbus.typing import Bool, Str, get_variant
from dasbus.typing import Bool, List, Str, Tuple, get_variant

from pyanaconda.modules.common.constants.services import LOCALIZATION
from pyanaconda.modules.common.structures.keyboard_layout import KeyboardLayout
from pyanaconda.modules.common.structures.language import LanguageData, LocaleData
from pyanaconda.modules.common.task import TaskInterface
from pyanaconda.modules.common.task import TaskInterface, sync_run_task
from pyanaconda.modules.localization.installation import (
KeyboardInstallationTask,
LanguageInstallationTask,
)
from pyanaconda.modules.localization.localization import LocalizationService
from pyanaconda.modules.localization.localization_interface import LocalizationInterface
from pyanaconda.modules.localization.localization_interface import (
KeyboardConfigurationTaskInterface,
LocalizationInterface,
)
from pyanaconda.modules.localization.runtime import (
ApplyKeyboardTask,
GetMissingKeyboardConfigurationTask,
Expand Down Expand Up @@ -350,9 +353,31 @@ def test_populate_missing_keyboard_configuration_with_task(self, publisher):
task_path = self.localization_interface.PopulateMissingKeyboardConfigurationWithTask()

obj = check_task_creation(task_path, publisher, GetMissingKeyboardConfigurationTask)
assert isinstance(obj, KeyboardConfigurationTaskInterface)
assert obj.implementation._vc_keymap == 'us'
assert obj.implementation._x_layouts == ['cz', 'cz (qwerty)']

@patch_dbus_publish_object
def test_get_keyboard_configuration_with_task(self, publisher):
"""Test GetKeyboardConfigurationWithTask."""
self.localization_interface.VirtualConsoleKeymap = 'us'
self.localization_interface.XLayouts = ['cz', 'cz (qwerty)']

task_path = self.localization_interface.GetKeyboardConfigurationWithTask()

obj = check_task_creation(task_path, publisher, GetMissingKeyboardConfigurationTask)
assert isinstance(obj, KeyboardConfigurationTaskInterface)
assert obj.implementation._vc_keymap == 'us'
assert obj.implementation._x_layouts == ['cz', 'cz (qwerty)']

with patch.object(obj.implementation, "run") as run_mock:
run_mock.return_value = ("cz (qwerty)", "us"), "cz-qwerty"

sync_run_task(obj)
# test the conversion from task to DBus variant
assert obj.GetResult() == get_variant(Tuple[List[Str], Str],
(['cz (qwerty)', 'us'], 'cz-qwerty'))

@patch_dbus_publish_object
def test_apply_keyboard_with_task(self, publisher):
"""Test ApplyKeyboardWithTask."""
Expand Down

0 comments on commit 79c33e7

Please sign in to comment.