diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cc71b237..b048586b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,6 @@
include:
+ - project: 'QubesOS/qubes-continuous-integration'
+ file: '/common.yml'
- project: 'QubesOS/qubes-continuous-integration'
file: '/r4.3/gitlab-base.yml'
- project: 'QubesOS/qubes-continuous-integration'
@@ -24,6 +26,7 @@ checks:tests:
- ci/codecov-wrapper
checks:pylint:
+ stage: checks
before_script:
- sudo dnf install -y python3-gobject gtk3 xorg-x11-server-Xvfb python3-mypy gtksourceview4 python3-pip curl
- pip3 install --quiet -r ci/requirements.txt
@@ -36,4 +39,10 @@ checks:pylint:
script:
- PYTHONPATH=~/core-admin-client:~/core-qrexec python3 -m pylint --rcfile=.pylintrc qui qubes_config
- mypy
+
+checks:black:
+ extends: .lint
stage: checks
+ variables:
+ DIR: .
+ SKIP_PYLINT: 1
diff --git a/qubes_config/global_config/basics_handler.py b/qubes_config/global_config/basics_handler.py
index 95b3aafb..aca51640 100644
--- a/qubes_config/global_config/basics_handler.py
+++ b/qubes_config/global_config/basics_handler.py
@@ -32,35 +32,46 @@
import qubesadmin.vm
from qubesadmin.utils import parse_size
-from ..widgets.gtk_widgets import VMListModeler, \
- TextModeler, TraitSelector, NONE_CATEGORY
+from ..widgets.gtk_widgets import (
+ VMListModeler,
+ TextModeler,
+ TraitSelector,
+ NONE_CATEGORY,
+)
from .page_handler import PageHandler
-from ..widgets.utils import get_feature, get_boolean_feature, \
- apply_feature_change
+from ..widgets.utils import (
+ get_feature,
+ get_boolean_feature,
+ apply_feature_change,
+)
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
-logger = logging.getLogger('qubes-global-config')
+logger = logging.getLogger("qubes-global-config")
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
+
class KernelVersion: # pylint: disable=too-few-public-methods
"""Helper class to be used in sorting kernels. Cannot use
distutils.version.LooseVersion, because it fails at handling
versions that have no numbers in them, which is quite possible with
custom kernels."""
+
def __init__(self, string):
self.string = string
- self.groups = re.compile(r'(\d+)').split(self.string)
+ self.groups = re.compile(r"(\d+)").split(self.string)
def __lt__(self, other):
- for (self_content, other_content) in itertools.zip_longest(
- self.groups, other.groups):
+ for self_content, other_content in itertools.zip_longest(
+ self.groups, other.groups
+ ):
if self_content == other_content:
continue
if self_content is None:
@@ -74,6 +85,7 @@ def __lt__(self, other):
class AbstractTraitHolder(abc.ABC):
"""Handler for all sorts of widgets reflecting system traits."""
+
@abc.abstractmethod
def get_model(self) -> TraitSelector:
"""Get the TraitSelector for current Trait."""
@@ -113,11 +125,17 @@ def get_unsaved(self):
class PropertyHandler(AbstractTraitHolder):
"""Handles comboboxes reflecting for object properties."""
- def __init__(self, qapp: qubesadmin.Qubes, trait_holder: Any,
- trait_name: str, widget: Gtk.ComboBox,
- vm_filter: Optional[Callable] = None,
- readable_name: Optional[str] = None,
- additional_options: Optional[Dict[str, str]] = None):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ trait_holder: Any,
+ trait_name: str,
+ widget: Gtk.ComboBox,
+ vm_filter: Optional[Callable] = None,
+ readable_name: Optional[str] = None,
+ additional_options: Optional[Dict[str, str]] = None,
+ ):
self.qapp = qapp
self.trait_holder = trait_holder
self.trait_name = trait_name
@@ -130,7 +148,7 @@ def __init__(self, qapp: qubesadmin.Qubes, trait_holder: Any,
filter_function=vm_filter,
current_value=self.get_current_value(),
style_changes=True,
- additional_options=additional_options
+ additional_options=additional_options,
)
def get_readable_description(self) -> str:
@@ -150,9 +168,16 @@ def get_model(self) -> TraitSelector:
class FeatureHandler(AbstractTraitHolder):
"""Handles comboboxes reflecting vm features."""
- def __init__(self, trait_holder: Any, trait_name: str,
- widget: Gtk.ComboBoxText, options: Dict[str, Any],
- readable_name: str, is_bool: bool = False):
+
+ def __init__(
+ self,
+ trait_holder: Any,
+ trait_name: str,
+ widget: Gtk.ComboBoxText,
+ options: Dict[str, Any],
+ readable_name: str,
+ is_bool: bool = False,
+ ):
self.trait_holder = trait_holder
self.trait_name = trait_name
self.widget = widget
@@ -161,8 +186,10 @@ def __init__(self, trait_holder: Any, trait_name: str,
self.model = TextModeler(
combobox=self.widget,
- values=options, selected_value=self.get_current_value(),
- style_changes=True)
+ values=options,
+ selected_value=self.get_current_value(),
+ style_changes=True,
+ )
def get_readable_description(self) -> str:
return self.readable_name
@@ -180,29 +207,28 @@ def update_current_value(self):
def get_model(self) -> TraitSelector:
return self.model
+
class QMemManHelper:
"""Helper class to handle the ugliness of managing qmemman config."""
- QMEMMAN_CONFIG_PATH = '/etc/qubes/qmemman.conf'
- MINMEM_NAME = 'vm-min-mem'
- DOM0_NAME = 'dom0-mem-boost'
+
+ QMEMMAN_CONFIG_PATH = "/etc/qubes/qmemman.conf"
+ MINMEM_NAME = "vm-min-mem"
+ DOM0_NAME = "dom0-mem-boost"
def __init__(self):
self.qmemman_config = None
def get_values(self) -> Dict[str, int]:
"""Returns a dict of 'vm-min-mem': value in MiB and
- 'dom0-mem-boost': value in MiB """
+ 'dom0-mem-boost': value in MiB"""
self.qmemman_config = ConfigParser()
self.qmemman_config.read(self.QMEMMAN_CONFIG_PATH)
- result = {
- self.MINMEM_NAME: 200,
- self.DOM0_NAME: 350
- }
+ result = {self.MINMEM_NAME: 200, self.DOM0_NAME: 350}
- if self.qmemman_config.has_section('global'):
+ if self.qmemman_config.has_section("global"):
for key in result:
- str_value = self.qmemman_config.get('global', key)
+ str_value = self.qmemman_config.get("global", key)
value = parse_size(str_value)
result[key] = int(value / 1024 / 1024)
@@ -212,11 +238,15 @@ def save_values(self, values_dict: Dict[str, int]):
"""Wants a dict of 'vm-min-mem': value in MiB and
'dom0-mem-boost': value in MiB"""
# qmemman settings
- text_dict = {key: str(int(value)) + 'MiB'
- for key, value in values_dict.items()}
+ text_dict = {
+ key: str(int(value)) + "MiB" for key, value in values_dict.items()
+ }
- assert len(text_dict) == 2 and \
- self.MINMEM_NAME in text_dict and self.DOM0_NAME in text_dict
+ assert (
+ len(text_dict) == 2
+ and self.MINMEM_NAME in text_dict
+ and self.DOM0_NAME in text_dict
+ )
# reinitialize the ConfigParser object, because it is somewhat
# unhappy to handle multiple consecutive writes and reads
@@ -224,27 +254,26 @@ def save_values(self, values_dict: Dict[str, int]):
self.qmemman_config = ConfigParser()
self.qmemman_config.read(self.QMEMMAN_CONFIG_PATH)
- if not self.qmemman_config.has_section('global'):
+ if not self.qmemman_config.has_section("global"):
# add the whole section
- self.qmemman_config.add_section('global')
+ self.qmemman_config.add_section("global")
for key in text_dict:
- self.qmemman_config.set(
- 'global', key, text_dict[key])
- self.qmemman_config.set(
- 'global', 'cache-margin-factor', str(1.3))
+ self.qmemman_config.set("global", key, text_dict[key])
+ self.qmemman_config.set("global", "cache-margin-factor", str(1.3))
- with open(self.QMEMMAN_CONFIG_PATH, 'a') as qmemman_config_file:
+ with open(self.QMEMMAN_CONFIG_PATH, "a") as qmemman_config_file:
self.qmemman_config.write(qmemman_config_file)
else:
# If there already is a 'global' section, we don't use
# SafeConfigParser.write() - it would get rid of
# all the comments...
- lines_to_add = {key: f'{key} = {value}\n'
- for key, value in text_dict.items()}
+ lines_to_add = {
+ key: f"{key} = {value}\n" for key, value in text_dict.items()
+ }
config_lines = []
- with open(self.QMEMMAN_CONFIG_PATH, 'r') as qmemman_config_file:
+ with open(self.QMEMMAN_CONFIG_PATH, "r") as qmemman_config_file:
for line in qmemman_config_file:
for key in lines_to_add:
if line.strip().startswith(key):
@@ -257,18 +286,21 @@ def save_values(self, values_dict: Dict[str, int]):
for line in lines_to_add:
config_lines.append(line)
- with open(self.QMEMMAN_CONFIG_PATH, 'w') as qmemman_config_file:
+ with open(self.QMEMMAN_CONFIG_PATH, "w") as qmemman_config_file:
qmemman_config_file.writelines(config_lines)
class MemoryHandler(AbstractTraitHolder):
"""Handler for memory / QMemMan settings. Requires SpinButton widgets:
'basics_min_memory' and 'basics_dom0_memory'"""
+
def __init__(self, gtk_builder):
- self.min_memory_spin: Gtk.SpinButton = \
- gtk_builder.get_object('basics_min_memory')
- self.dom0_memory_spin: Gtk.SpinButton = \
- gtk_builder.get_object('basics_dom0_memory')
+ self.min_memory_spin: Gtk.SpinButton = gtk_builder.get_object(
+ "basics_min_memory"
+ )
+ self.dom0_memory_spin: Gtk.SpinButton = gtk_builder.get_object(
+ "basics_dom0_memory"
+ )
self.min_memory_adjustment = Gtk.Adjustment()
self.min_memory_adjustment.configure(0, 0, 999999, 1, 10, 0)
@@ -288,9 +320,11 @@ def __init__(self, gtk_builder):
self.dom0_memory_spin.set_sensitive(False)
self.min_memory_spin.set_value(
- self.initial_values.get(self.mem_helper.MINMEM_NAME, 0))
+ self.initial_values.get(self.mem_helper.MINMEM_NAME, 0)
+ )
self.dom0_memory_spin.set_value(
- self.initial_values.get(self.mem_helper.DOM0_NAME, 0))
+ self.initial_values.get(self.mem_helper.DOM0_NAME, 0)
+ )
@staticmethod
def get_readable_description() -> str: # pylint: disable=arguments-differ
@@ -306,7 +340,7 @@ def save(self):
values = {
self.mem_helper.MINMEM_NAME: int(self.min_memory_spin.get_value()),
- self.mem_helper.DOM0_NAME: int(self.dom0_memory_spin.get_value())
+ self.mem_helper.DOM0_NAME: int(self.dom0_memory_spin.get_value()),
}
self.mem_helper.save_values(values)
@@ -316,21 +350,27 @@ def reset(self):
if not self.min_memory_spin.is_sensitive():
return
- self.min_memory_spin.set_value(self.initial_values.get(
- self.mem_helper.MINMEM_NAME, 0))
- self.dom0_memory_spin.set_value(self.initial_values.get(
- self.mem_helper.DOM0_NAME, 0))
+ self.min_memory_spin.set_value(
+ self.initial_values.get(self.mem_helper.MINMEM_NAME, 0)
+ )
+ self.dom0_memory_spin.set_value(
+ self.initial_values.get(self.mem_helper.DOM0_NAME, 0)
+ )
def is_changed(self) -> bool:
"""Has the user selected something different from the initial value?"""
- if not self.min_memory_spin.is_sensitive() or \
- not self.dom0_memory_spin.is_sensitive():
+ if (
+ not self.min_memory_spin.is_sensitive()
+ or not self.dom0_memory_spin.is_sensitive()
+ ):
return False
if self.min_memory_spin.get_value() != self.initial_values.get(
- self.mem_helper.MINMEM_NAME, 0):
+ self.mem_helper.MINMEM_NAME, 0
+ ):
return True
if self.dom0_memory_spin.get_value() != self.initial_values.get(
- self.mem_helper.DOM0_NAME, 0):
+ self.mem_helper.DOM0_NAME, 0
+ ):
return True
return False
@@ -356,6 +396,7 @@ def update_current_value(self):
class KernelHolder(AbstractTraitHolder):
"""Trait holder for list of available Linux kernels"""
+
def __init__(self, qapp: qubesadmin.Qubes, widget: Gtk.ComboBoxText):
self.qapp = qapp
self.widget = widget
@@ -364,15 +405,16 @@ def __init__(self, qapp: qubesadmin.Qubes, widget: Gtk.ComboBoxText):
combobox=self.widget,
values=self._get_kernel_options(),
selected_value=self.get_current_value(),
- style_changes=True
+ style_changes=True,
)
def _get_kernel_options(self) -> Dict[str, str]:
- kernels = [kernel.vid for kernel in
- self.qapp.pools['linux-kernel'].volumes]
+ kernels = [
+ kernel.vid for kernel in self.qapp.pools["linux-kernel"].volumes
+ ]
kernels = sorted(kernels, key=KernelVersion)
kernels_dict = {kernel: kernel for kernel in kernels}
- kernels_dict['(none)'] = None
+ kernels_dict["(none)"] = None
return kernels_dict
def get_readable_description(self) -> str:
@@ -393,6 +435,7 @@ class BasicSettingsHandler(PageHandler):
"""
Handler for the Basic Settings page.
"""
+
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
"""
:param gtk_builder: gtk_builder object
@@ -403,85 +446,140 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
self.handlers: List[AbstractTraitHolder] = []
- self.clockvm_combo = gtk_builder.get_object('basics_clockvm_combo')
- self.deftemplate_combo: Gtk.ComboBox = \
- gtk_builder.get_object('basics_deftemplate_combo')
- self.defnetvm_combo: Gtk.ComboBox = \
- gtk_builder.get_object('basics_defnetvm_combo')
- self.defdispvm_combo: Gtk.ComboBox = \
- gtk_builder.get_object('basics_defdispvm_combo')
- self.fullscreen_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('basics_fullscreen_combo')
- self.utf_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('basics_utf_windows_combo')
- self.tray_icon_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('basics_tray_icon_combo')
- self.kernel_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('basics_kernel_combo')
-
- self.handlers.append(PropertyHandler(
- qapp=self.qapp, trait_holder=self.qapp, trait_name="clockvm",
- widget=self.clockvm_combo, vm_filter=self._clock_vm_filter,
- readable_name=_("Clock qube"), additional_options=NONE_CATEGORY))
- self.handlers.append(PropertyHandler(
- qapp=self.qapp, trait_holder=self.qapp,
- trait_name="default_template", widget=self.deftemplate_combo,
- vm_filter=self._default_template_filter,
- readable_name=_("Default template"),
- additional_options=NONE_CATEGORY))
- self.handlers.append(PropertyHandler(
- qapp=self.qapp, trait_holder=self.qapp, trait_name="default_netvm",
- widget=self.defnetvm_combo, vm_filter=self._default_netvm_filter,
- readable_name=_("Default net qube"),
- additional_options=NONE_CATEGORY))
- self.handlers.append(PropertyHandler(
- qapp=self.qapp, trait_holder=self.qapp, trait_name="default_dispvm",
- widget=self.defdispvm_combo, vm_filter=self._default_dispvm_filter,
- readable_name=_("Default disposable qube template"),
- additional_options=NONE_CATEGORY))
- self.handlers.append(FeatureHandler(
- trait_holder=self.vm, trait_name='gui-default-allow-fullscreen',
- widget=self.fullscreen_combo,
- options={_('default (disallow)'): None, _('allow'): True,
- _('disallow'): False},
- readable_name=_("Allow fullscreen"), is_bool=True))
- self.handlers.append(FeatureHandler(
- trait_holder=self.vm, trait_name='gui-default-allow-utf8-titles',
- widget=self.utf_combo,
- options={_('default (disallow)'): None, _('allow'): True,
- _('disallow'): False},
- readable_name=_("Allow utf8 window titles"), is_bool=True))
- self.handlers.append(FeatureHandler(
- trait_holder=self.vm, trait_name='gui-default-trayicon-mode',
- widget=self.tray_icon_combo,
- options={_('default (tinted icon)'): None,
- _('full background'): 'bg',
- _('thin border'): 'border1',
- _('thick border'): 'border2',
- _('tinted icon'): 'tint',
- _('tinted icon with modified white'): 'tint+whitehack',
- _('tinted icon with 50% saturation'): 'tint+saturation50'},
- readable_name="Tray icon mode", is_bool=False))
- self.handlers.append(KernelHolder(qapp=self.qapp,
- widget=self.kernel_combo))
+ self.clockvm_combo = gtk_builder.get_object("basics_clockvm_combo")
+ self.deftemplate_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "basics_deftemplate_combo"
+ )
+ self.defnetvm_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "basics_defnetvm_combo"
+ )
+ self.defdispvm_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "basics_defdispvm_combo"
+ )
+ self.fullscreen_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "basics_fullscreen_combo"
+ )
+ self.utf_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "basics_utf_windows_combo"
+ )
+ self.tray_icon_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "basics_tray_icon_combo"
+ )
+ self.kernel_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "basics_kernel_combo"
+ )
+
+ self.handlers.append(
+ PropertyHandler(
+ qapp=self.qapp,
+ trait_holder=self.qapp,
+ trait_name="clockvm",
+ widget=self.clockvm_combo,
+ vm_filter=self._clock_vm_filter,
+ readable_name=_("Clock qube"),
+ additional_options=NONE_CATEGORY,
+ )
+ )
+ self.handlers.append(
+ PropertyHandler(
+ qapp=self.qapp,
+ trait_holder=self.qapp,
+ trait_name="default_template",
+ widget=self.deftemplate_combo,
+ vm_filter=self._default_template_filter,
+ readable_name=_("Default template"),
+ additional_options=NONE_CATEGORY,
+ )
+ )
+ self.handlers.append(
+ PropertyHandler(
+ qapp=self.qapp,
+ trait_holder=self.qapp,
+ trait_name="default_netvm",
+ widget=self.defnetvm_combo,
+ vm_filter=self._default_netvm_filter,
+ readable_name=_("Default net qube"),
+ additional_options=NONE_CATEGORY,
+ )
+ )
+ self.handlers.append(
+ PropertyHandler(
+ qapp=self.qapp,
+ trait_holder=self.qapp,
+ trait_name="default_dispvm",
+ widget=self.defdispvm_combo,
+ vm_filter=self._default_dispvm_filter,
+ readable_name=_("Default disposable qube template"),
+ additional_options=NONE_CATEGORY,
+ )
+ )
+ self.handlers.append(
+ FeatureHandler(
+ trait_holder=self.vm,
+ trait_name="gui-default-allow-fullscreen",
+ widget=self.fullscreen_combo,
+ options={
+ _("default (disallow)"): None,
+ _("allow"): True,
+ _("disallow"): False,
+ },
+ readable_name=_("Allow fullscreen"),
+ is_bool=True,
+ )
+ )
+ self.handlers.append(
+ FeatureHandler(
+ trait_holder=self.vm,
+ trait_name="gui-default-allow-utf8-titles",
+ widget=self.utf_combo,
+ options={
+ _("default (disallow)"): None,
+ _("allow"): True,
+ _("disallow"): False,
+ },
+ readable_name=_("Allow utf8 window titles"),
+ is_bool=True,
+ )
+ )
+ self.handlers.append(
+ FeatureHandler(
+ trait_holder=self.vm,
+ trait_name="gui-default-trayicon-mode",
+ widget=self.tray_icon_combo,
+ options={
+ _("default (tinted icon)"): None,
+ _("full background"): "bg",
+ _("thin border"): "border1",
+ _("thick border"): "border2",
+ _("tinted icon"): "tint",
+ _("tinted icon with modified white"): "tint+whitehack",
+ _("tinted icon with 50% saturation"): "tint+saturation50",
+ },
+ readable_name="Tray icon mode",
+ is_bool=False,
+ )
+ )
+ self.handlers.append(
+ KernelHolder(qapp=self.qapp, widget=self.kernel_combo)
+ )
self.handlers.append(MemoryHandler(gtk_builder))
@staticmethod
def _clock_vm_filter(vm) -> bool:
- return vm.klass != 'TemplateVM'
+ return vm.klass != "TemplateVM"
@staticmethod
def _default_template_filter(vm) -> bool:
- return vm.klass == 'TemplateVM'
+ return vm.klass == "TemplateVM"
@staticmethod
def _default_netvm_filter(vm) -> bool:
- return getattr(vm, 'provides_network', False)
+ return getattr(vm, "provides_network", False)
@staticmethod
def _default_dispvm_filter(vm) -> bool:
- return getattr(vm, 'template_for_dispvms', False)
+ return getattr(vm, "template_for_dispvms", False)
def save(self):
for handler in self.handlers:
diff --git a/qubes_config/global_config/conflict_handler.py b/qubes_config/global_config/conflict_handler.py
index 5c63a702..aecbc9b0 100644
--- a/qubes_config/global_config/conflict_handler.py
+++ b/qubes_config/global_config/conflict_handler.py
@@ -25,34 +25,38 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
class ConflictFileListRow(Gtk.ListBoxRow):
"""A ListBox row representing a policy file with conflicting info."""
+
def __init__(self, file_name: str):
super().__init__()
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.add(self.box)
- self.get_style_context().add_class('problem_row')
+ self.get_style_context().add_class("problem_row")
self.label = Gtk.Label()
self.label.set_text(file_name)
- self.label.get_style_context().add_class('red_code')
+ self.label.get_style_context().add_class("red_code")
self.box.pack_start(self.label, False, False, 0)
- if file_name.startswith('/etc/qubes-rpc'):
+ if file_name.startswith("/etc/qubes-rpc"):
self.icon = Gtk.Image()
- self.icon.set_from_pixbuf(load_icon('qubes-question', 14, 14))
- tooltip = _('This is a legacy file from previous Qubes versions. '
- 'Custom policy contained there will no longer '
- 'be supported in Qubes 4.2.')
+ self.icon.set_from_pixbuf(load_icon("qubes-question", 14, 14))
+ tooltip = _(
+ "This is a legacy file from previous Qubes versions. "
+ "Custom policy contained there will no longer "
+ "be supported in Qubes 4.2."
+ )
self.set_tooltip_text(tooltip)
self.box.pack_start(self.icon, False, False, 0)
@@ -63,24 +67,34 @@ def __str__(self):
class ConflictFileHandler:
"""Handler for conflicting policy files."""
- def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
- service_names: List[str], own_file_name: str,
- policy_manager: PolicyManager):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ prefix: str,
+ service_names: List[str],
+ own_file_name: str,
+ policy_manager: PolicyManager,
+ ):
self.service_names = service_names
self.own_file_name = own_file_name
self.policy_manager = policy_manager
self.problem_box: Gtk.Box = gtk_builder.get_object(
- f'{prefix}_problem_box')
+ f"{prefix}_problem_box"
+ )
self.problem_list: Gtk.ListBox = gtk_builder.get_object(
- f'{prefix}_problem_files_list')
+ f"{prefix}_problem_files_list"
+ )
conflicting_files = []
for service in self.service_names:
conflicting_files.extend(
self.policy_manager.get_conflicting_policy_files(
- service, self.own_file_name))
+ service, self.own_file_name
+ )
+ )
if conflicting_files:
self.problem_box.set_visible(True)
diff --git a/qubes_config/global_config/global_config.py b/qubes_config/global_config/global_config.py
index fb1a1c55..2d3269cc 100644
--- a/qubes_config/global_config/global_config.py
+++ b/qubes_config/global_config/global_config.py
@@ -35,9 +35,13 @@
from ..widgets.utils import open_url_in_disposable
from .page_handler import PageHandler
from .policy_handler import PolicyHandler, VMSubsetPolicyHandler
-from .policy_rules import RuleSimple, \
- RuleSimpleAskIsAllow, RuleTargeted, SimpleVerbDescription, \
- RuleSimpleNoAllow
+from .policy_rules import (
+ RuleSimple,
+ RuleSimpleAskIsAllow,
+ RuleTargeted,
+ SimpleVerbDescription,
+ RuleSimpleNoAllow,
+)
from .policy_manager import PolicyManager
from .updates_handler import UpdatesHandler
from .usb_devices import DevicesHandler
@@ -47,41 +51,59 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, GObject, Gio
-logger = logging.getLogger('qubes-global-config')
+logger = logging.getLogger("qubes-global-config")
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
# in order to add more places as referencable locations, you need to add
# a focusable widget to the UI and then add its name here
-LOCATIONS = ["default_qubes", "window_management", "memory_balancing",
- "linux_kernel", "usb_input", "u2f", "dom0_updates",
- "check_for_updates", "update_proxy", "template_repositories",
- "clipboard_shortcut", "clipboard_policy", "filecopy_policy",
- "open_in_vm"]
+LOCATIONS = [
+ "default_qubes",
+ "window_management",
+ "memory_balancing",
+ "linux_kernel",
+ "usb_input",
+ "u2f",
+ "dom0_updates",
+ "check_for_updates",
+ "update_proxy",
+ "template_repositories",
+ "clipboard_shortcut",
+ "clipboard_policy",
+ "filecopy_policy",
+ "open_in_vm",
+]
class ClipboardHandler(PageHandler):
"""Handler for Clipboard policy. Adds a couple of comboboxes to a
normal policy handler."""
- COPY_FEATURE = 'gui-default-secure-copy-sequence'
- PASTE_FEATURE = 'gui-default-secure-paste-sequence'
- def __init__(self, qapp: qubesadmin.Qubes,
- gtk_builder: Gtk.Builder,
- policy_manager: PolicyManager):
+ COPY_FEATURE = "gui-default-secure-copy-sequence"
+ PASTE_FEATURE = "gui-default-secure-paste-sequence"
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ gtk_builder: Gtk.Builder,
+ policy_manager: PolicyManager,
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
self.vm = self.qapp.domains[self.qapp.local_name]
- self.copy_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('clipboard_copy_combo')
- self.paste_combo: Gtk.ComboBoxText = \
- gtk_builder.get_object('clipboard_paste_combo')
+ self.copy_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "clipboard_copy_combo"
+ )
+ self.paste_combo: Gtk.ComboBoxText = gtk_builder.get_object(
+ "clipboard_paste_combo"
+ )
self.handlers: List[Union[PolicyHandler, FeatureHandler]] = [
PolicyHandler(
@@ -89,36 +111,44 @@ def __init__(self, qapp: qubesadmin.Qubes,
gtk_builder=gtk_builder,
policy_manager=policy_manager,
prefix="clipboard",
- service_name='qubes.ClipboardPaste',
- policy_file_name='50-config-clipboard',
+ service_name="qubes.ClipboardPaste",
+ policy_file_name="50-config-clipboard",
default_policy="""qubes.ClipboardPaste * @adminvm @anyvm ask\n
qubes.ClipboardPaste * @anyvm @anyvm ask\n""",
- verb_description=SimpleVerbDescription({
- "ask": _('be allowed to paste\n into clipboard of'),
- "deny": _('be allowed to paste\n into clipboard of')
- }),
+ verb_description=SimpleVerbDescription(
+ {
+ "ask": _("be allowed to paste\n into clipboard of"),
+ "deny": _("be allowed to paste\n into clipboard of"),
+ }
+ ),
rule_class=RuleSimpleAskIsAllow,
- include_admin_vm=True
+ include_admin_vm=True,
),
FeatureHandler(
- trait_holder=self.vm, trait_name=self.COPY_FEATURE,
+ trait_holder=self.vm,
+ trait_name=self.COPY_FEATURE,
widget=self.copy_combo,
- options={_('default (Ctrl+Shift+C)'): None,
- _('Ctrl+Shift+C'): 'Ctrl-Shift-c',
- _('Ctrl+Win+C'): 'Ctrl-Mod4-c',
- _("Win+C"): 'Mod4-c'},
- readable_name=_("Global Clipboard copy shortcut")
+ options={
+ _("default (Ctrl+Shift+C)"): None,
+ _("Ctrl+Shift+C"): "Ctrl-Shift-c",
+ _("Ctrl+Win+C"): "Ctrl-Mod4-c",
+ _("Win+C"): "Mod4-c",
+ },
+ readable_name=_("Global Clipboard copy shortcut"),
),
FeatureHandler(
- trait_holder=self.vm, trait_name=self.PASTE_FEATURE,
+ trait_holder=self.vm,
+ trait_name=self.PASTE_FEATURE,
widget=self.paste_combo,
- options= {_('default (Ctrl+Shift+V)'): None,
- _('Ctrl+Shift+V'): 'Ctrl-Shift-V',
- _('Ctrl+Win+V'): 'Ctrl-Mod4-v',
- _('Ctrl+Insert'): 'Ctrl-Ins',
- _('Win+V'): 'Mod4-v'},
- readable_name=_("Global Clipboard paste shortcut")
- )
+ options={
+ _("default (Ctrl+Shift+V)"): None,
+ _("Ctrl+Shift+V"): "Ctrl-Shift-V",
+ _("Ctrl+Win+V"): "Ctrl-Mod4-v",
+ _("Ctrl+Insert"): "Ctrl-Ins",
+ _("Win+V"): "Mod4-v",
+ },
+ readable_name=_("Global Clipboard paste shortcut"),
+ ),
]
def reset(self):
@@ -141,9 +171,13 @@ def get_unsaved(self) -> str:
class FileAccessHandler(PageHandler):
"""Handler for FileAccess page. Requires separate handler because
it combines two policies in itself."""
- def __init__(self, qapp: qubesadmin.Qubes,
- gtk_builder: Gtk.Builder,
- policy_manager: PolicyManager):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ gtk_builder: Gtk.Builder,
+ policy_manager: PolicyManager,
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
@@ -156,12 +190,15 @@ def __init__(self, qapp: qubesadmin.Qubes,
qubes.Filecopy * @anyvm @anyvm ask""",
service_name="qubes.Filecopy",
policy_file_name="50-config-filecopy",
- verb_description=SimpleVerbDescription({
- "ask": _("to be allowed to copy files to"),
- "allow": _("allow files to be copied to"),
- "deny": _("be allowed to copy files to")
- }),
- rule_class=RuleSimple)
+ verb_description=SimpleVerbDescription(
+ {
+ "ask": _("to be allowed to copy files to"),
+ "allow": _("allow files to be copied to"),
+ "deny": _("be allowed to copy files to"),
+ }
+ ),
+ rule_class=RuleSimple,
+ )
self.openinvm_handler = DispvmExceptionHandler(
gtk_builder=gtk_builder,
@@ -193,13 +230,16 @@ class GlobalConfig(Gtk.Application):
"""
Main Gtk.Application for new qube widget.
"""
+
def __init__(self, qapp: qubesadmin.Qubes, policy_manager: PolicyManager):
"""
:param qapp: qubesadmin.Qubes object
:param policy_manager: PolicyManager object
"""
- super().__init__(application_id='org.qubesos.globalconfig',
- flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
+ super().__init__(
+ application_id="org.qubesos.globalconfig",
+ flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
+ )
self.qapp: qubesadmin.Qubes = qapp
self.policy_manager = policy_manager
@@ -215,7 +255,8 @@ def __init__(self, qapp: qubesadmin.Qubes, policy_manager: PolicyManager):
self.open_at: Optional[str] = None
self.progress_bar_dialog = ProgressBarDialog(
- self, _("Loading system settings..."))
+ self, _("Loading system settings...")
+ )
self.handlers: Dict[str, PageHandler] = {}
def do_command_line(self, command_line):
@@ -250,15 +291,20 @@ def do_activate(self, *args, **kwargs):
assert self.main_window
self.main_window.show()
# resize to screen size
- if self.main_window.get_allocated_width() > \
- self.main_window.get_screen().get_width():
+ if (
+ self.main_window.get_allocated_width()
+ > self.main_window.get_screen().get_width()
+ ):
width = int(self.main_window.get_screen().get_width() * 0.9)
else:
# try to have at least 1100 pixels
- width = min(int(self.main_window.get_screen().get_width() * 0.9),
- 1100)
- if self.main_window.get_allocated_height() > \
- self.main_window.get_screen().get_height() * 0.9:
+ width = min(
+ int(self.main_window.get_screen().get_width() * 0.9), 1100
+ )
+ if (
+ self.main_window.get_allocated_height()
+ > self.main_window.get_screen().get_height() * 0.9
+ ):
height = int(self.main_window.get_screen().get_height() * 0.9)
else:
height = self.main_window.get_allocated_height()
@@ -303,21 +349,30 @@ def scroll_to_location(self, location_string):
@staticmethod
def register_signals():
"""Register necessary Gtk signals"""
- GObject.signal_new('rules-changed',
- Gtk.ListBox,
- GObject.SignalFlags.RUN_LAST, GObject.TYPE_PYOBJECT,
- (GObject.TYPE_PYOBJECT,))
+ GObject.signal_new(
+ "rules-changed",
+ Gtk.ListBox,
+ GObject.SignalFlags.RUN_LAST,
+ GObject.TYPE_PYOBJECT,
+ (GObject.TYPE_PYOBJECT,),
+ )
# signal that informs other pages that a given page has been changed
- GObject.signal_new('page-changed',
- Gtk.Window,
- GObject.SignalFlags.RUN_LAST, GObject.TYPE_PYOBJECT,
- (GObject.TYPE_STRING,))
+ GObject.signal_new(
+ "page-changed",
+ Gtk.Window,
+ GObject.SignalFlags.RUN_LAST,
+ GObject.TYPE_PYOBJECT,
+ (GObject.TYPE_STRING,),
+ )
- GObject.signal_new('child-removed',
- Gtk.FlowBox,
- GObject.SignalFlags.RUN_LAST, GObject.TYPE_PYOBJECT,
- (GObject.TYPE_PYOBJECT,))
+ GObject.signal_new(
+ "child-removed",
+ Gtk.FlowBox,
+ GObject.SignalFlags.RUN_LAST,
+ GObject.TYPE_PYOBJECT,
+ (GObject.TYPE_PYOBJECT,),
+ )
def perform_setup(self):
# pylint: disable=attribute-defined-outside-init
@@ -325,85 +380,97 @@ def perform_setup(self):
The function that performs actual widget realization and setup.
"""
self.builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'global_config.glade')
+ glade_ref = (
+ importlib.resources.files("qubes_config") / "global_config.glade"
+ )
with importlib.resources.as_file(glade_ref) as path:
self.builder.add_from_file(str(path))
- self.main_window: Gtk.Window = self.builder.get_object('main_window')
- self.main_notebook: Gtk.Notebook = \
- self.builder.get_object('main_notebook')
+ self.main_window: Gtk.Window = self.builder.get_object("main_window")
+ self.main_notebook: Gtk.Notebook = self.builder.get_object(
+ "main_notebook"
+ )
- load_theme(widget=self.main_window,
- package_name='qubes_config',
- light_file_name='qubes-global-config-light.css',
- dark_file_name='qubes-global-config-dark.css')
+ load_theme(
+ widget=self.main_window,
+ package_name="qubes_config",
+ light_file_name="qubes-global-config-light.css",
+ dark_file_name="qubes-global-config-dark.css",
+ )
self.progress_bar_dialog.show_all()
self.progress_bar_dialog.update_progress(0)
- self.apply_button: Gtk.Button = self.builder.get_object('apply_button')
- self.cancel_button: Gtk.Button = \
- self.builder.get_object('cancel_button')
- self.ok_button: Gtk.Button = self.builder.get_object('ok_button')
+ self.apply_button: Gtk.Button = self.builder.get_object("apply_button")
+ self.cancel_button: Gtk.Button = self.builder.get_object(
+ "cancel_button"
+ )
+ self.ok_button: Gtk.Button = self.builder.get_object("ok_button")
- self.apply_button.connect('clicked', self._apply)
- self.cancel_button.connect('clicked', self._quit)
- self.ok_button.connect('clicked', self._ok)
+ self.apply_button.connect("clicked", self._apply)
+ self.cancel_button.connect("clicked", self._quit)
+ self.ok_button.connect("clicked", self._ok)
- self.main_window.connect('delete-event', self._ask_to_quit)
+ self.main_window.connect("delete-event", self._ask_to_quit)
page_progress = 1 / self.main_notebook.get_n_pages()
# match page by widget name to handler
- self.handlers['basics'] = BasicSettingsHandler(self.builder, self.qapp)
+ self.handlers["basics"] = BasicSettingsHandler(self.builder, self.qapp)
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['usb'] = DevicesHandler(
- self.qapp, self.policy_manager, self.builder)
+ self.handlers["usb"] = DevicesHandler(
+ self.qapp, self.policy_manager, self.builder
+ )
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['updates'] = UpdatesHandler(
- qapp=self.qapp,
- policy_manager=self.policy_manager,
- gtk_builder=self.builder)
+ self.handlers["updates"] = UpdatesHandler(
+ qapp=self.qapp,
+ policy_manager=self.policy_manager,
+ gtk_builder=self.builder,
+ )
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['splitgpg'] = VMSubsetPolicyHandler(
- qapp=self.qapp,
- gtk_builder=self.builder,
- policy_manager=self.policy_manager,
- prefix="splitgpg",
- service_name='qubes.Gpg',
- policy_file_name='50-config-splitgpg',
- default_policy="",
- main_rule_class=RuleSimpleNoAllow,
- main_verb_description=SimpleVerbDescription({
+ self.handlers["splitgpg"] = VMSubsetPolicyHandler(
+ qapp=self.qapp,
+ gtk_builder=self.builder,
+ policy_manager=self.policy_manager,
+ prefix="splitgpg",
+ service_name="qubes.Gpg",
+ policy_file_name="50-config-splitgpg",
+ default_policy="",
+ main_rule_class=RuleSimpleNoAllow,
+ main_verb_description=SimpleVerbDescription(
+ {
"ask": _("ask to access GPG\nkeys from"),
- "deny": _("access GPG\nkeys from")
- }),
- exception_rule_class=RuleTargeted,
- exception_verb_description=SimpleVerbDescription({
- "allow": _('access GPG\nkeys from'),
- "ask": _('to access GPG\nkeys from'),
- "deny": _('access GPG\nkeys from')
- }))
+ "deny": _("access GPG\nkeys from"),
+ }
+ ),
+ exception_rule_class=RuleTargeted,
+ exception_verb_description=SimpleVerbDescription(
+ {
+ "allow": _("access GPG\nkeys from"),
+ "ask": _("to access GPG\nkeys from"),
+ "deny": _("access GPG\nkeys from"),
+ }
+ ),
+ )
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['clipboard'] = ClipboardHandler(
- qapp=self.qapp,
- gtk_builder=self.builder,
- policy_manager=self.policy_manager
- )
+ self.handlers["clipboard"] = ClipboardHandler(
+ qapp=self.qapp,
+ gtk_builder=self.builder,
+ policy_manager=self.policy_manager,
+ )
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['file'] = FileAccessHandler(
- qapp=self.qapp,
- gtk_builder=self.builder,
- policy_manager=self.policy_manager
- )
+ self.handlers["file"] = FileAccessHandler(
+ qapp=self.qapp,
+ gtk_builder=self.builder,
+ policy_manager=self.policy_manager,
+ )
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['url'] = DispvmExceptionHandler(
+ self.handlers["url"] = DispvmExceptionHandler(
gtk_builder=self.builder,
qapp=self.qapp,
service_name="qubes.OpenURL",
@@ -413,9 +480,9 @@ def perform_setup(self):
)
self.progress_bar_dialog.update_progress(page_progress)
- self.handlers['thisdevice'] = ThisDeviceHandler(self.qapp,
- self.builder,
- self.policy_manager)
+ self.handlers["thisdevice"] = ThisDeviceHandler(
+ self.qapp, self.builder, self.policy_manager
+ )
self.progress_bar_dialog.update_progress(page_progress)
self.main_notebook.connect("switch-page", self._page_switched)
@@ -428,25 +495,33 @@ def perform_setup(self):
self.viewport_handler = ViewportHandler(
self.main_window,
- [self.builder.get_object('basics_scrolled_window'),
- self.builder.get_object('usb_scrolled_window'),
- self.builder.get_object('updates_scrolled_window'),
- self.builder.get_object('splitgpg_scrolled_window'),
- self.builder.get_object('clipboard_scrolled_window'),
- self.builder.get_object('file_scrolled_window'),
- self.builder.get_object('url_scrolled_window'),
- self.builder.get_object('thisdevice_scrolled_window'),
- ])
+ [
+ self.builder.get_object("basics_scrolled_window"),
+ self.builder.get_object("usb_scrolled_window"),
+ self.builder.get_object("updates_scrolled_window"),
+ self.builder.get_object("splitgpg_scrolled_window"),
+ self.builder.get_object("clipboard_scrolled_window"),
+ self.builder.get_object("file_scrolled_window"),
+ self.builder.get_object("url_scrolled_window"),
+ self.builder.get_object("thisdevice_scrolled_window"),
+ ],
+ )
self.progress_bar_dialog.update_progress(1)
self.progress_bar_dialog.hide()
self.progress_bar_dialog.destroy()
def _handle_urls(self):
- url_label_ids = ["basics_info", "u2fproxy_info", "splitgpg_info2",
- "copymove_info", "openinvm_info", "url_info",
- "thisdevice_certified_yes_info",
- "thisdevice_security_info"]
+ url_label_ids = [
+ "basics_info",
+ "u2fproxy_info",
+ "splitgpg_info2",
+ "copymove_info",
+ "openinvm_info",
+ "url_info",
+ "thisdevice_certified_yes_info",
+ "thisdevice_security_info",
+ ]
for url_label_id in url_label_ids:
label: Gtk.Label = self.builder.get_object(url_label_id)
label.connect("activate-link", self._activate_link)
@@ -459,7 +534,8 @@ def get_current_page(self) -> Optional[PageHandler]:
"""Get currently visible page."""
page_num = self.main_notebook.get_current_page()
return self.handlers.get(
- self.main_notebook.get_nth_page(page_num).get_name(), None)
+ self.main_notebook.get_nth_page(page_num).get_name(), None
+ )
def save_page(self, page: PageHandler) -> bool:
"""Save provided page and emit any necessary signals;
@@ -473,13 +549,16 @@ def save_page(self, page: PageHandler) -> bool:
page.save()
for name, handler in self.handlers.items():
if handler == page:
- self.main_window.emit('page-changed', name)
+ self.main_window.emit("page-changed", name)
break
self.qapp._invalidate_cache_all()
page.reset()
except Exception as ex:
- show_error(self.main_window, _("Could not save changes"),
- _("The following error occurred: ") + escape(str(ex)))
+ show_error(
+ self.main_window,
+ _("Could not save changes"),
+ _("The following error occurred: ") + escape(str(ex)),
+ )
return False
return True
@@ -489,7 +568,7 @@ def verify_changes(self) -> bool:
page = self.get_current_page()
if page:
unsaved = page.get_unsaved()
- if unsaved != '':
+ if unsaved != "":
response = self._ask_unsaved(unsaved)
if response == Gtk.ResponseType.YES:
return self.save_page(page)
@@ -503,18 +582,21 @@ def _page_switched(self, *_args):
old_page_num = self.main_notebook.get_current_page()
allow_switch = self.verify_changes()
if not allow_switch:
- GLib.timeout_add(1, lambda: self.main_notebook.set_current_page(
- old_page_num))
+ GLib.timeout_add(
+ 1, lambda: self.main_notebook.set_current_page(old_page_num)
+ )
def _ask_unsaved(self, description: str) -> Gtk.ResponseType:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
label_1 = Gtk.Label()
- label_1.set_markup(_("The following unsaved changes "
- "were found:"))
+ label_1.set_markup(
+ _("The following unsaved changes were found:")
+ )
label_1.set_xalign(0)
label_2 = Gtk.Label()
label_2.set_text(
- "\n".join([f'- {row}' for row in description.split('\n')]))
+ "\n".join([f"- {row}" for row in description.split("\n")])
+ )
label_2.set_margin_start(20)
label_2.set_xalign(0)
label_3 = Gtk.Label()
@@ -525,12 +607,16 @@ def _ask_unsaved(self, description: str) -> Gtk.ResponseType:
box.pack_start(label_3, False, False, 10)
response = show_dialog_with_icon(
- parent=self.main_window, title=_("Unsaved changes"), text=box,
+ parent=self.main_window,
+ title=_("Unsaved changes"),
+ text=box,
buttons={
_("_Save changes"): Gtk.ResponseType.YES,
_("_Discard changes"): Gtk.ResponseType.NO,
_("_Cancel"): Gtk.ResponseType.CANCEL,
- }, icon_name="qubes-ask")
+ },
+ icon_name="qubes-ask",
+ )
return response
@@ -578,5 +664,5 @@ def main():
app.run(sys.argv)
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qubes_config/global_config/page_handler.py b/qubes_config/global_config/page_handler.py
index 499cc599..f0ad9032 100644
--- a/qubes_config/global_config/page_handler.py
+++ b/qubes_config/global_config/page_handler.py
@@ -20,8 +20,10 @@
"""Abstract class representing Settings pages."""
import abc
+
class PageHandler(abc.ABC):
"""abstract class for page handlers"""
+
@abc.abstractmethod
def save(self):
"""Save all changes. This should raise exceptions if save was
diff --git a/qubes_config/global_config/policy_exceptions_handler.py b/qubes_config/global_config/policy_exceptions_handler.py
index b9fe382e..9724b217 100644
--- a/qubes_config/global_config/policy_exceptions_handler.py
+++ b/qubes_config/global_config/policy_exceptions_handler.py
@@ -37,24 +37,30 @@
from ..widgets.gtk_widgets import QubeName
from ..widgets.utils import compare_rule_lists
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
+
class PolicyExceptionsHandler:
"""
Class for handling a list of policy exceptions
"""
- def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
- policy_manager,
- row_func: Callable[[Rule, bool], Gtk.ListBoxRow],
- new_rule: Callable[..., Rule],
- exclude_rule: Optional[Callable[[Rule], bool]] = None,
- enable_raw: bool = True
- ):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ prefix: str,
+ policy_manager,
+ row_func: Callable[[Rule, bool], Gtk.ListBoxRow],
+ new_rule: Callable[..., Rule],
+ exclude_rule: Optional[Callable[[Rule], bool]] = None,
+ enable_raw: bool = True,
+ ):
"""
:param gtk_builder: Gtk.Builder instance
:param prefix: prefix for widgets; expects a prefix_exception_list
@@ -73,28 +79,32 @@ def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
self.initial_rules: List[Rule] = []
self.rule_list: Gtk.ListBox = gtk_builder.get_object(
- f'{prefix}_exception_list')
+ f"{prefix}_exception_list"
+ )
self.add_button: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_add_rule_button')
+ f"{prefix}_add_rule_button"
+ )
self.error_handler = ErrorHandler(gtk_builder, prefix)
if enable_raw:
- self.raw_handler: Optional[RawPolicyTextHandler] = \
+ self.raw_handler: Optional[RawPolicyTextHandler] = (
RawPolicyTextHandler(
gtk_builder=gtk_builder,
prefix=prefix,
policy_manager=self.policy_manager,
error_handler=self.error_handler,
callback_on_open_raw=self.close_all_edits,
- callback_on_save_raw=self.populate_rule_lists)
+ callback_on_save_raw=self.populate_rule_lists,
+ )
+ )
else:
self.raw_handler = None
# connect events
- self.rule_list.connect('row-activated', self._rule_clicked)
- self.rule_list.connect('rules-changed', self._fill_raw_rules)
+ self.rule_list.connect("row-activated", self._rule_clicked)
+ self.rule_list.connect("rules-changed", self._fill_raw_rules)
self.add_button.connect("clicked", self.add_new_rule)
def initialize_with_rules(self, rules: List[Rule]):
@@ -159,8 +169,9 @@ def current_rows(self) -> List[RuleListBoxRow]:
@property
def current_rules(self) -> List[Rule]:
- rules = [row.rule.raw_rule for row in self.current_rows if
- not row.is_new_row]
+ rules = [
+ row.rule.raw_rule for row in self.current_rows if not row.is_new_row
+ ]
return rules
def close_all_edits(self, *_args):
@@ -168,10 +179,13 @@ def close_all_edits(self, *_args):
PolicyHandler.close_rows_in_list(self.rule_list.get_children())
@staticmethod
- def verify_rule_against_rows(other_rows: List[RuleListBoxRow],
- row: RuleListBoxRow,
- new_source: str, new_target: str,
- new_action: str) -> Optional[str]:
+ def verify_rule_against_rows(
+ other_rows: List[RuleListBoxRow],
+ row: RuleListBoxRow,
+ new_source: str,
+ new_target: str,
+ new_action: str,
+ ) -> Optional[str]:
"""
Verify correctness of a rule with new_source, new_target and new_action
if it was to be associated with provided row. Return None if rule would
@@ -180,31 +194,39 @@ def verify_rule_against_rows(other_rows: List[RuleListBoxRow],
for other_row in other_rows:
if other_row == row:
continue
- if other_row.rule.is_rule_conflicting(new_source, new_target,
- new_action):
+ if other_row.rule.is_rule_conflicting(
+ new_source, new_target, new_action
+ ):
return str(other_row)
return None
- def verify_new_rule(self, row: RuleListBoxRow,
- new_source: str, new_target: str,
- new_action: str) -> Optional[str]:
+ def verify_new_rule(
+ self,
+ row: RuleListBoxRow,
+ new_source: str,
+ new_target: str,
+ new_action: str,
+ ) -> Optional[str]:
"""
Verify correctness of a rule with new_source, new_target and new_action
if it was to be associated with provided row. Return None if rule would
be correct, and string description of error otherwise.
"""
- return self.verify_rule_against_rows(self.current_rows, row,
- new_source, new_target, new_action)
+ return self.verify_rule_against_rows(
+ self.current_rows, row, new_source, new_target, new_action
+ )
class DispvmExceptionHandler(PageHandler):
- def __init__(self, gtk_builder: Gtk.Builder,
- qapp: qubesadmin.Qubes,
- service_name: str,
- prefix: str,
- policy_manager,
- policy_file_name: str
- ):
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ qapp: qubesadmin.Qubes,
+ service_name: str,
+ prefix: str,
+ policy_manager,
+ policy_file_name: str,
+ ):
"""
Handler for various dispvm-related exception lists
:param gtk_builder: Gtk.Builder instance
@@ -231,15 +253,19 @@ def __init__(self, gtk_builder: Gtk.Builder,
prefix=prefix,
policy_manager=self.policy_manager,
row_func=self._get_row,
- new_rule=self._new_rule)
+ new_rule=self._new_rule,
+ )
self.current_state_box: Gtk.Box = gtk_builder.get_object(
- f'{prefix}_current_state_box')
+ f"{prefix}_current_state_box"
+ )
self.current_state_widget: Optional[Gtk.Box] = None
- self.initial_rules, self.current_token = \
+ self.initial_rules, self.current_token = (
self.policy_manager.get_rules_from_filename(
- self.policy_file_name, "")
+ self.policy_file_name, ""
+ )
+ )
self.initialize()
@@ -258,19 +284,23 @@ def _get_row(self, rule: Rule, new: bool = False):
parent_handler=self.list_handler,
rule=RuleDispVM(rule),
qapp=self.qapp,
- verb_description=SimpleVerbDescription({
- "ask": _("and default to"),
- "allow": _("use"),
- "deny": _("open in disposable qube")
- }),
+ verb_description=SimpleVerbDescription(
+ {
+ "ask": _("and default to"),
+ "allow": _("use"),
+ "deny": _("open in disposable qube"),
+ }
+ ),
is_new_row=new,
)
def _new_rule(self) -> Rule:
return self.policy_manager.new_rule(
- service=self.service_name, source='@anyvm',
- target='@dispvm',
- action='allow target=@dispvm')
+ service=self.service_name,
+ source="@anyvm",
+ target="@dispvm",
+ action="allow target=@dispvm",
+ )
def get_unsaved(self) -> str:
if self.list_handler.get_unsaved() != "":
@@ -283,10 +313,14 @@ def reset(self):
def save(self):
self.policy_manager.save_rules(
self.policy_file_name,
- self.list_handler.current_rules, self.current_token)
+ self.list_handler.current_rules,
+ self.current_token,
+ )
- _rules, self.current_token = \
+ _rules, self.current_token = (
self.policy_manager.get_rules_from_filename(
- self.policy_file_name, "")
+ self.policy_file_name, ""
+ )
+ )
self.initial_rules = deepcopy(self.list_handler.current_rules)
diff --git a/qubes_config/global_config/policy_handler.py b/qubes_config/global_config/policy_handler.py
index 5d7d419a..f5dbdb41 100644
--- a/qubes_config/global_config/policy_handler.py
+++ b/qubes_config/global_config/policy_handler.py
@@ -32,8 +32,12 @@
from .page_handler import PageHandler
from .policy_rules import AbstractRuleWrapper, AbstractVerbDescription
from .policy_manager import PolicyManager
-from .rule_list_widgets import RuleListBoxRow, FilteredListBoxRow, \
- ErrorRuleRow, LIMITED_CATEGORIES
+from .rule_list_widgets import (
+ RuleListBoxRow,
+ FilteredListBoxRow,
+ ErrorRuleRow,
+ LIMITED_CATEGORIES,
+)
from .conflict_handler import ConflictFileHandler
import gi
@@ -42,26 +46,31 @@
import qubesadmin.vm
from ..widgets.utils import compare_rule_lists
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
+
class PolicyHandler(PageHandler):
"""Handler for a single page with Policy settings."""
- def __init__(self,
- qapp: qubesadmin.Qubes,
- gtk_builder: Gtk.Builder,
- prefix: str,
- policy_manager: PolicyManager,
- default_policy: str,
- service_name: str,
- policy_file_name: str,
- verb_description: AbstractVerbDescription,
- rule_class: Type[AbstractRuleWrapper],
- include_admin_vm: bool = False):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ gtk_builder: Gtk.Builder,
+ prefix: str,
+ policy_manager: PolicyManager,
+ default_policy: str,
+ service_name: str,
+ policy_file_name: str,
+ verb_description: AbstractVerbDescription,
+ rule_class: Type[AbstractRuleWrapper],
+ include_admin_vm: bool = False,
+ ):
"""
:param qapp: Qubes object
:param gtk_builder: gtk_builder; to avoid inelegant design, this should
@@ -89,30 +98,35 @@ def __init__(self,
self.include_adminvm = include_admin_vm
# main widgets
- self.main_list_box: Gtk.ListBox = \
- gtk_builder.get_object(f'{prefix}_main_list')
- self.exception_list_box: Gtk.ListBox = \
- gtk_builder.get_object(f'{prefix}_exception_list')
+ self.main_list_box: Gtk.ListBox = gtk_builder.get_object(
+ f"{prefix}_main_list"
+ )
+ self.exception_list_box: Gtk.ListBox = gtk_builder.get_object(
+ f"{prefix}_exception_list"
+ )
# add new rule button
- self.add_button: Gtk.Button = \
- gtk_builder.get_object(f'{prefix}_add_rule_button')
+ self.add_button: Gtk.Button = gtk_builder.get_object(
+ f"{prefix}_add_rule_button"
+ )
# enable/disable custom policy
self.enable_radio: Gtk.RadioButton = gtk_builder.get_object(
- f'{prefix}_enable_radio')
+ f"{prefix}_enable_radio"
+ )
self.disable_radio: Gtk.RadioButton = gtk_builder.get_object(
- f'{prefix}_disable_radio')
+ f"{prefix}_disable_radio"
+ )
# connect events
self.add_button.connect("clicked", self.add_new_rule)
- self.exception_list_box.connect('row-activated', self._rule_clicked)
- self.main_list_box.connect('row-activated', self._rule_clicked)
- self.exception_list_box.connect('rules-changed',
- self._populate_raw_rules)
- self.main_list_box.connect('rules-changed',
- self._populate_raw_rules)
+ self.exception_list_box.connect("row-activated", self._rule_clicked)
+ self.main_list_box.connect("row-activated", self._rule_clicked)
+ self.exception_list_box.connect(
+ "rules-changed", self._populate_raw_rules
+ )
+ self.main_list_box.connect("rules-changed", self._populate_raw_rules)
self.enable_radio.connect("toggled", self._custom_toggled)
self.disable_radio.connect("toggled", self._custom_toggled)
@@ -121,26 +135,30 @@ def __init__(self,
self.main_list_box.set_sort_func(self.rule_sorting_function)
self.conflict_handler = ConflictFileHandler(
- gtk_builder=gtk_builder, prefix=prefix,
+ gtk_builder=gtk_builder,
+ prefix=prefix,
service_names=[self.service_name],
own_file_name=self.policy_file_name,
- policy_manager=self.policy_manager)
+ policy_manager=self.policy_manager,
+ )
self.error_handler = ErrorHandler(gtk_builder, prefix)
self.raw_handler = RawPolicyTextHandler(
- gtk_builder=gtk_builder, prefix=prefix,
+ gtk_builder=gtk_builder,
+ prefix=prefix,
policy_manager=self.policy_manager,
error_handler=self.error_handler,
callback_on_save_raw=self.populate_rule_lists,
- callback_on_open_raw=self.close_all_edits)
+ callback_on_open_raw=self.close_all_edits,
+ )
# fill data
self.initial_rules: List[Rule] = []
self.current_token: Optional[str] = None
self.initialize_data()
- self.main_list_box.connect('map', self.on_switch)
+ self.main_list_box.connect("map", self.on_switch)
def _populate_raw_rules(self, *_args):
self.raw_handler.fill_raw(self.current_rules)
@@ -149,12 +167,19 @@ def add_new_rule(self, *_args):
"""Add a new rule."""
self.close_all_edits()
deny_all_rule = self.policy_manager.new_rule(
- service=self.service_name, source='@anyvm',
- target='@anyvm', action='deny')
- new_row = RuleListBoxRow(self,
- self.rule_class(deny_all_rule), self.qapp, self.verb_description,
- is_new_row=True,
- enable_adminvm=self.include_adminvm)
+ service=self.service_name,
+ source="@anyvm",
+ target="@anyvm",
+ action="deny",
+ )
+ new_row = RuleListBoxRow(
+ self,
+ self.rule_class(deny_all_rule),
+ self.qapp,
+ self.verb_description,
+ is_new_row=True,
+ enable_adminvm=self.include_adminvm,
+ )
self.exception_list_box.add(new_row)
new_row.activate()
@@ -165,21 +190,24 @@ def current_rules(self) -> List[Rule]:
"""
if self.disable_radio.get_active():
return self.policy_manager.text_to_rules(self.default_policy)
- return [row.rule.raw_rule for row in self.current_rows if
- not row.is_new_row]
+ return [
+ row.rule.raw_rule for row in self.current_rows if not row.is_new_row
+ ]
@property
def current_rows(self) -> List[RuleListBoxRow]:
"""
Get the current list of all RuleListBoxRows
"""
- return self.exception_list_box.get_children() + \
- self.main_list_box.get_children()
+ return (
+ self.exception_list_box.get_children()
+ + self.main_list_box.get_children()
+ )
def initialize_data(self):
- rules, self.current_token = \
- self.policy_manager.get_rules_from_filename(
- self.policy_file_name, self.default_policy)
+ rules, self.current_token = self.policy_manager.get_rules_from_filename(
+ self.policy_file_name, self.default_policy
+ )
# fill data
self.populate_rule_lists(rules)
@@ -202,33 +230,53 @@ def populate_rule_lists(self, rules: List[Rule]):
if wrapped_rule.is_rule_fundamental():
self.main_list_box.add(
RuleListBoxRow(
- self, wrapped_rule, self.qapp,
- self.verb_description, enable_delete=False,
+ self,
+ wrapped_rule,
+ self.qapp,
+ self.verb_description,
+ enable_delete=False,
enable_vm_edit=False,
- enable_adminvm=self.include_adminvm))
+ enable_adminvm=self.include_adminvm,
+ )
+ )
continue
- fundamental = self.include_adminvm and \
- rule.source == '@adminvm' and \
- rule.target == '@anyvm'
- self.exception_list_box.add(RuleListBoxRow(self,
- rule=wrapped_rule, qapp=self.qapp,
- verb_description=self.verb_description,
- enable_delete=not fundamental,
- enable_vm_edit=not fundamental,
- enable_adminvm=self.include_adminvm))
+ fundamental = (
+ self.include_adminvm
+ and rule.source == "@adminvm"
+ and rule.target == "@anyvm"
+ )
+ self.exception_list_box.add(
+ RuleListBoxRow(
+ self,
+ rule=wrapped_rule,
+ qapp=self.qapp,
+ verb_description=self.verb_description,
+ enable_delete=not fundamental,
+ enable_vm_edit=not fundamental,
+ enable_adminvm=self.include_adminvm,
+ )
+ )
except Exception: # pylint: disable=broad-except
self.error_handler.add_error(rule)
if not self.main_list_box.get_children():
deny_all_rule = self.policy_manager.new_rule(
- service=self.service_name, source='@anyvm',
- target='@anyvm', action='deny')
+ service=self.service_name,
+ source="@anyvm",
+ target="@anyvm",
+ action="deny",
+ )
self.main_list_box.add(
- RuleListBoxRow(self,
- self.rule_class(deny_all_rule), self.qapp,
+ RuleListBoxRow(
+ self,
+ self.rule_class(deny_all_rule),
+ self.qapp,
self.verb_description,
- enable_delete=False, enable_vm_edit=False,
- enable_adminvm=self.include_adminvm))
+ enable_delete=False,
+ enable_vm_edit=False,
+ enable_adminvm=self.include_adminvm,
+ )
+ )
self.check_custom_rules(rules)
@@ -245,12 +293,12 @@ def cmp_token(token_1, token_2):
# otherwise compare lexically
if token_1 == token_2:
return 0
- if token_1 == '@anyvm':
+ if token_1 == "@anyvm":
return 1
- if token_2 == '@anyvm':
+ if token_2 == "@anyvm":
return -1
- is_token_1_keyword = token_1.startswith('@')
- is_token_2_keyword = token_2.startswith('@')
+ is_token_1_keyword = token_1.startswith("@")
+ is_token_2_keyword = token_2.startswith("@")
if is_token_1_keyword == is_token_2_keyword:
if token_1 < token_2:
return -1
@@ -259,8 +307,9 @@ def cmp_token(token_1, token_2):
return 1
return -1
- def rule_sorting_function(self,
- row_1: RuleListBoxRow, row_2: RuleListBoxRow):
+ def rule_sorting_function(
+ self, row_1: RuleListBoxRow, row_2: RuleListBoxRow
+ ):
"""Sorting function for exceptions."""
source_cmp = self.cmp_token(row_1.rule.source, row_2.rule.source)
if source_cmp != 0:
@@ -272,8 +321,9 @@ def check_custom_rules(self, rules: List[Rule]):
Check if the provided set of rules is the same as the default set,
set radio buttons accordingly.
"""
- if self.policy_manager.compare_rules_to_text(rules,
- self.default_policy):
+ if self.policy_manager.compare_rules_to_text(
+ rules, self.default_policy
+ ):
self.disable_radio.set_active(True)
else:
self.enable_radio.set_active(True)
@@ -292,10 +342,13 @@ def _rule_clicked(self, _list_box, row: RuleListBoxRow, *_args):
row.set_edit_mode(True)
@staticmethod
- def verify_rule_against_rows(other_rows: List[RuleListBoxRow],
- row: RuleListBoxRow,
- new_source: str, new_target: str,
- new_action: str) -> Optional[str]:
+ def verify_rule_against_rows(
+ other_rows: List[RuleListBoxRow],
+ row: RuleListBoxRow,
+ new_source: str,
+ new_target: str,
+ new_action: str,
+ ) -> Optional[str]:
"""
Verify correctness of a rule with new_source, new_target and new_action
if it was to be associated with provided row. Return None if rule would
@@ -304,21 +357,27 @@ def verify_rule_against_rows(other_rows: List[RuleListBoxRow],
for other_row in other_rows:
if other_row == row:
continue
- if other_row.rule.is_rule_conflicting(new_source, new_target,
- new_action):
+ if other_row.rule.is_rule_conflicting(
+ new_source, new_target, new_action
+ ):
return str(other_row)
return None
- def verify_new_rule(self, row: RuleListBoxRow,
- new_source: str, new_target: str,
- new_action: str) -> Optional[str]:
+ def verify_new_rule(
+ self,
+ row: RuleListBoxRow,
+ new_source: str,
+ new_target: str,
+ new_action: str,
+ ) -> Optional[str]:
"""
Verify correctness of a rule with new_source, new_target and new_action
if it was to be associated with provided row. Return None if rule would
be correct, and string description of error otherwise.
"""
- return self.verify_rule_against_rows(self.current_rows, row,
- new_source, new_target, new_action)
+ return self.verify_rule_against_rows(
+ self.current_rows, row, new_source, new_target, new_action
+ )
@staticmethod
def close_rows_in_list(row_list: List[RuleListBoxRow]):
@@ -329,14 +388,20 @@ def close_rows_in_list(row_list: List[RuleListBoxRow]):
row.set_edit_mode(False)
continue
response = show_dialog_with_icon(
- parent=row.get_toplevel(), title=_("Unsaved changes"),
- text=_("A rule is currently being edited. \n"
- "Do you want to save changes to the following"
- "rule?\n") + str(row),
+ parent=row.get_toplevel(),
+ title=_("Unsaved changes"),
+ text=_(
+ "A rule is currently being edited. \n"
+ "Do you want to save changes to the following"
+ "rule?\n"
+ )
+ + str(row),
buttons={
_("_Save changes"): Gtk.ResponseType.YES,
_("_Discard changes"): Gtk.ResponseType.NO,
- }, icon_name="qubes-ask")
+ },
+ icon_name="qubes-ask",
+ )
if response == Gtk.ResponseType.YES:
if not row.validate_and_save():
@@ -357,10 +422,12 @@ def reset(self):
def save(self):
"""Save current rules, whatever they are - custom or default."""
rules = self.current_rules
- self.policy_manager.save_rules(self.policy_file_name,
- rules, self.current_token)
+ self.policy_manager.save_rules(
+ self.policy_file_name, rules, self.current_token
+ )
_r, self.current_token = self.policy_manager.get_rules_from_filename(
- self.policy_file_name, self.default_policy)
+ self.policy_file_name, self.default_policy
+ )
self.initial_rules = deepcopy(rules)
@@ -378,25 +445,34 @@ def get_unsaved(self) -> str:
def on_switch(self, *_args):
if self.error_handler.get_errors():
- rule_text = "\n".join(str(rule) for rule in
- self.error_handler.get_errors())
+ rule_text = "\n".join(
+ str(rule) for rule in self.error_handler.get_errors()
+ )
show_error(
parent=self.main_list_box.get_toplevel(),
title=_("Unknown rule found in police file"),
- text=_("The following rules could not be parsed:\n") +
- rule_text + _("\nThis has probably happened due to "
- "manual editing of the policy file. The "
- "rule will be discarded.")
+ text=_("The following rules could not be parsed:\n")
+ + rule_text
+ + _(
+ "\nThis has probably happened due to "
+ "manual editing of the policy file. The "
+ "rule will be discarded."
+ ),
)
class RawPolicyTextHandler:
"""Class that handles raw text widgets"""
- def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
- policy_manager: PolicyManager,
- error_handler: 'ErrorHandler',
- callback_on_save_raw: Optional[Callable[[List[Rule]], None]],
- callback_on_open_raw: Optional[Callable]):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ prefix: str,
+ policy_manager: PolicyManager,
+ error_handler: "ErrorHandler",
+ callback_on_save_raw: Optional[Callable[[List[Rule]], None]],
+ callback_on_open_raw: Optional[Callable],
+ ):
"""
:param gtk_builder: Gtk.Builder
:param prefix: prefix for widgets
@@ -410,18 +486,20 @@ def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
self.error_handler = error_handler
self.callback_on_save_raw = callback_on_save_raw
- self.raw_event_button: Gtk.Button = \
- gtk_builder.get_object(f'{prefix}_raw_event')
- self.raw_box: Gtk.Box = \
- gtk_builder.get_object(f'{prefix}_raw_box')
- self.raw_expander_icon: Gtk.Image = \
- gtk_builder.get_object(f'{prefix}_raw_expander')
+ self.raw_event_button: Gtk.Button = gtk_builder.get_object(
+ f"{prefix}_raw_event"
+ )
+ self.raw_box: Gtk.Box = gtk_builder.get_object(f"{prefix}_raw_box")
+ self.raw_expander_icon: Gtk.Image = gtk_builder.get_object(
+ f"{prefix}_raw_expander"
+ )
self.raw_text: Gtk.TextView = gtk_builder.get_object(
- f'{prefix}_raw_text')
- self.raw_save: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_raw_save')
+ f"{prefix}_raw_text"
+ )
+ self.raw_save: Gtk.Button = gtk_builder.get_object(f"{prefix}_raw_save")
self.raw_cancel: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_raw_cancel')
+ f"{prefix}_raw_cancel"
+ )
self.text_buffer: Gtk.TextBuffer = self.raw_text.get_buffer()
self.raw_save.connect("clicked", self._save_raw)
@@ -431,7 +509,7 @@ def __init__(self, gtk_builder: Gtk.Builder, prefix: str,
event_button=self.raw_event_button,
data_container=self.raw_box,
icon=self.raw_expander_icon,
- event_callback=callback_on_open_raw
+ event_callback=callback_on_open_raw,
)
self.last_known_rules: List[Rule] = []
@@ -441,23 +519,28 @@ def _save_raw(self, _widget):
rules: List[Rule] = self.policy_manager.text_to_rules(
self.text_buffer.get_text(
self.text_buffer.get_start_iter(),
- self.text_buffer.get_end_iter(), False))
+ self.text_buffer.get_end_iter(),
+ False,
+ )
+ )
if self.callback_on_save_raw:
self.callback_on_save_raw(rules)
if self.error_handler.get_errors():
- rule_text = "\n".join(str(rule) for rule in
- self.error_handler.get_errors())
+ rule_text = "\n".join(
+ str(rule) for rule in self.error_handler.get_errors()
+ )
raise PolicySyntaxError(None, None, rule_text)
self.expander_handler.set_state(False)
except PolicySyntaxError as ex:
- error_message = str(ex).split(':', 3)[2]
+ error_message = str(ex).split(":", 3)[2]
show_error(
parent=self.raw_box.get_toplevel(),
title=_("Error parsing rules"),
- text=_("The following rules could not be parsed:\n") +
- error_message + "\n"
+ text=_("The following rules could not be parsed:\n")
+ + error_message
+ + "\n",
)
def _cancel_raw(self, _widget):
@@ -481,18 +564,21 @@ class VMSubsetPolicyHandler(PolicyHandler):
of VMs, that is, SplitGPG. Currently makes SplitGPG assumptions, such
as networked qube is dangerous.
"""
- def __init__(self,
- qapp: qubesadmin.Qubes,
- gtk_builder: Gtk.Builder,
- prefix: str,
- policy_manager: PolicyManager,
- default_policy: str,
- service_name: str,
- policy_file_name: str,
- main_verb_description: AbstractVerbDescription,
- main_rule_class: Type[AbstractRuleWrapper],
- exception_verb_description: AbstractVerbDescription,
- exception_rule_class: Type[AbstractRuleWrapper]):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ gtk_builder: Gtk.Builder,
+ prefix: str,
+ policy_manager: PolicyManager,
+ default_policy: str,
+ service_name: str,
+ policy_file_name: str,
+ main_verb_description: AbstractVerbDescription,
+ main_rule_class: Type[AbstractRuleWrapper],
+ exception_verb_description: AbstractVerbDescription,
+ exception_rule_class: Type[AbstractRuleWrapper],
+ ):
"""
:param qapp: Qubes object
:param gtk_builder: gtk_builder; to avoid inelegant design, this should
@@ -520,52 +606,70 @@ def __init__(self,
# main widgets
self.add_select_box: Gtk.Box = gtk_builder.get_object(
- f'{prefix}_add_select_box')
+ f"{prefix}_add_select_box"
+ )
self.add_select_button: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_add_select_button')
+ f"{prefix}_add_select_button"
+ )
self.add_select_confirm: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_add_select_confirm')
+ f"{prefix}_add_select_confirm"
+ )
self.add_select_cancel: Gtk.Button = gtk_builder.get_object(
- f'{prefix}_add_select_cancel')
+ f"{prefix}_add_select_cancel"
+ )
self.select_qube_combo: Gtk.ComboBox = gtk_builder.get_object(
- f'{prefix}_select_qube_combo')
+ f"{prefix}_select_qube_combo"
+ )
super().__init__(
- qapp, gtk_builder, prefix, policy_manager, default_policy,
- service_name, policy_file_name, exception_verb_description,
- exception_rule_class)
+ qapp,
+ gtk_builder,
+ prefix,
+ policy_manager,
+ default_policy,
+ service_name,
+ policy_file_name,
+ exception_verb_description,
+ exception_rule_class,
+ )
# populate combo
self.select_qube_model = VMListModeler(
- combobox=self.select_qube_combo,
- qapp=self.qapp)
+ combobox=self.select_qube_combo, qapp=self.qapp
+ )
# connect events
- self.add_select_button.connect('clicked', self._add_select_qube)
- self.add_select_confirm.connect('clicked', self._add_select_confirm)
- self.add_select_cancel.connect('clicked', self._add_select_cancel)
+ self.add_select_button.connect("clicked", self._add_select_qube)
+ self.add_select_confirm.connect("clicked", self._add_select_confirm)
+ self.add_select_cancel.connect("clicked", self._add_select_cancel)
- self.main_list_box.connect('rules-changed', self._select_qubes_changed)
+ self.main_list_box.connect("rules-changed", self._select_qubes_changed)
def _select_qubes_changed(self, *_args):
self.close_all_edits()
- self.select_qubes = {row.rule.target for row in
- self.main_list_box.get_children()}
+ self.select_qubes = {
+ row.rule.target for row in self.main_list_box.get_children()
+ }
self.populate_rule_lists(self.current_rules, drop_obsolete=True)
def _add_main_rule(self, rule):
- self.main_list_box.add(RuleListBoxRow(
- parent_handler=self,
- rule=self.main_rule_class(rule),
- qapp=self.qapp,
- verb_description=self.main_verb_description,
- enable_delete=True,
- enable_vm_edit=False, initial_verb="",
- custom_deletion_warning=_("Are you sure you want to delete this "
- "rule? All related exceptions will also "
- "be deleted."),
- enable_adminvm=self.include_adminvm
- ))
+ self.main_list_box.add(
+ RuleListBoxRow(
+ parent_handler=self,
+ rule=self.main_rule_class(rule),
+ qapp=self.qapp,
+ verb_description=self.main_verb_description,
+ enable_delete=True,
+ enable_vm_edit=False,
+ initial_verb="",
+ custom_deletion_warning=_(
+ "Are you sure you want to delete this "
+ "rule? All related exceptions will also "
+ "be deleted."
+ ),
+ enable_adminvm=self.include_adminvm,
+ )
+ )
def _add_exception_rule(self, rule):
row = FilteredListBoxRow(
@@ -575,7 +679,7 @@ def _add_exception_rule(self, rule):
initial_verb=_("will"),
verb_description=self.exception_verb_description,
filter_target=lambda x: str(x) in self.select_qubes,
- source_categories=LIMITED_CATEGORIES
+ source_categories=LIMITED_CATEGORIES,
)
self.exception_list_box.add(row)
return row
@@ -586,16 +690,21 @@ def _has_partial_duplicate(rule: Rule, rules: List[Rule]) -> bool:
# there exists a rule with @default and that target as target
# or default target
for other_rule in rules:
- if other_rule.source == rule.source and \
- other_rule.target == '@default' and (
+ if (
+ other_rule.source == rule.source
+ and other_rule.target == "@default"
+ and (
getattr(other_rule.action, "target", None) == rule.target
- or getattr(other_rule.action, "default_target", None) ==
- rule.target):
+ or getattr(other_rule.action, "default_target", None)
+ == rule.target
+ )
+ ):
return True
return False
- def populate_rule_lists(self, rules: List[Rule],
- drop_obsolete: bool = False):
+ def populate_rule_lists(
+ self, rules: List[Rule], drop_obsolete: bool = False
+ ):
"""
Populate the rule lists.
:param rules: List of Rule objects
@@ -603,8 +712,10 @@ def populate_rule_lists(self, rules: List[Rule],
in key qube list be silently discarded?
:return:
"""
- for child in self.main_list_box.get_children() + \
- self.exception_list_box.get_children():
+ for child in (
+ self.main_list_box.get_children()
+ + self.exception_list_box.get_children()
+ ):
child.get_parent().remove(child)
# rules with source = '@anyvm' go to main list and their
# qubes are key qubes
@@ -614,32 +725,33 @@ def populate_rule_lists(self, rules: List[Rule],
for rule in reversed(rules):
try:
- if rule.source == '@anyvm':
- if rule.target.type == 'keyword':
+ if rule.source == "@anyvm":
+ if rule.target.type == "keyword":
# we do not support this
self.error_handler.add_error(rule)
continue
self._add_main_rule(rule)
- except Exception: # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
self.error_handler.add_error(rule)
continue
- self.select_qubes = {row.rule.target for row in
- self.main_list_box.get_children()}
+ self.select_qubes = {
+ row.rule.target for row in self.main_list_box.get_children()
+ }
for rule in reversed(rules):
try:
- if rule.target != '@default':
+ if rule.target != "@default":
if self._has_partial_duplicate(rule, rules):
continue
- if rule.source != '@anyvm':
+ if rule.source != "@anyvm":
wrapped_exception_rule = self.exception_rule_class(rule)
if wrapped_exception_rule.target not in self.select_qubes:
if not drop_obsolete:
self.error_handler.add_error(rule)
continue
self._add_exception_rule(rule)
- except Exception: # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
self.error_handler.add_error(rule)
continue
self.add_button.set_sensitive(bool(self.main_list_box.get_children()))
@@ -657,27 +769,38 @@ def _add_select_qube(self, *_args):
def _add_select_confirm(self, *_args):
new_qube = self.select_qube_model.get_selected()
if not new_qube or not isinstance(new_qube, qubesadmin.vm.QubesVM):
- show_error(self.main_list_box, _('Invalid selection'),
- _('Invalid object was selected. {new_qube} is not a '
- 'valid Qubes qube.').format(new_qube=new_qube))
+ show_error(
+ self.main_list_box,
+ _("Invalid selection"),
+ _(
+ "Invalid object was selected. {new_qube} is not a "
+ "valid Qubes qube."
+ ).format(new_qube=new_qube),
+ )
return
if new_qube.is_networked():
response = ask_question(
self.main_list_box,
_("Add new key qube"),
- _("Are you sure you want to add {new_qube} as a key qube? It "
- "has network access, which may lead to decreased "
- "security.").format(new_qube=new_qube))
+ _(
+ "Are you sure you want to add {new_qube} as a key qube? It "
+ "has network access, which may lead to decreased "
+ "security."
+ ).format(new_qube=new_qube),
+ )
if response == Gtk.ResponseType.NO:
self._add_select_cancel()
return
new_qube = str(self.select_qube_model.get_selected())
new_rule = self.policy_manager.new_rule(
- service=self.service_name, source='@anyvm',
- target=new_qube, action='ask')
+ service=self.service_name,
+ source="@anyvm",
+ target=new_qube,
+ action="ask",
+ )
self._add_main_rule(new_rule)
self.add_select_box.set_visible(False)
- self.main_list_box.emit('rules-changed', None)
+ self.main_list_box.emit("rules-changed", None)
def _add_select_cancel(self, *_args):
self.add_select_box.set_visible(False)
@@ -687,8 +810,11 @@ def add_new_rule(self, *_args):
self.close_all_edits()
for qube in self.select_qubes:
rule = self.policy_manager.new_rule(
- service=self.service_name, source=str(qube),
- target=str(qube), action='deny')
+ service=self.service_name,
+ source=str(qube),
+ target=str(qube),
+ action="deny",
+ )
new_row = self._add_exception_rule(rule)
new_row.is_new_row = True
new_row.activate()
@@ -716,7 +842,7 @@ def current_rules(self) -> List[Rule]:
continue
rules.append(new_rule)
- if new_rule.target == '@default':
+ if new_rule.target == "@default":
if getattr(new_rule.action, "default_target", None):
new_target = new_rule.action.default_target
elif getattr(new_rule.action, "target", None):
@@ -724,20 +850,24 @@ def current_rules(self) -> List[Rule]:
else:
continue
another_rule = self.policy_manager.new_rule(
- service=self.service_name, source=new_rule.source,
+ service=self.service_name,
+ source=new_rule.source,
target=new_target,
- action=type(new_rule.action).__name__.lower())
+ action=type(new_rule.action).__name__.lower(),
+ )
if str(another_rule) in [str(rule) for rule in rules]:
# do not save duplicates
continue
rules.append(another_rule)
- rules.extend([row.rule.raw_rule for row in
- self.main_list_box.get_children()])
+ rules.extend(
+ [row.rule.raw_rule for row in self.main_list_box.get_children()]
+ )
return rules
class ErrorHandler:
"""A helper class to manage boxes with error lists."""
+
def __init__(self, gtk_builder, prefix: str):
"""
:param gtk_builder: Gtk.Builder
@@ -745,9 +875,10 @@ def __init__(self, gtk_builder, prefix: str):
- prefix_error_box that contains all error widgets
- prefix_error_list, a ListBox that contains erroneous rules
"""
- self.error_box: Gtk.Box = gtk_builder.get_object(f'{prefix}_error_box')
+ self.error_box: Gtk.Box = gtk_builder.get_object(f"{prefix}_error_box")
self.error_list: Gtk.ListBox = gtk_builder.get_object(
- f'{prefix}_error_list')
+ f"{prefix}_error_list"
+ )
self._errors: List[Rule] = []
diff --git a/qubes_config/global_config/policy_manager.py b/qubes_config/global_config/policy_manager.py
index 6b383234..10392ba0 100644
--- a/qubes_config/global_config/policy_manager.py
+++ b/qubes_config/global_config/policy_manager.py
@@ -26,6 +26,7 @@
from qubes_config.widgets.utils import compare_rule_lists
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -35,13 +36,16 @@ class PolicyManager:
Single manager for interacting with Qubes Policy.
Should be used as a singleton.
"""
+
def __init__(self):
self.policy_client = PolicyClient()
- self.policy_disclaimer = _("""
+ self.policy_disclaimer = _(
+ """
# THIS IS AN AUTOMATICALLY GENERATED POLICY FILE.
# Any changes made manually may be overwritten by Qubes Configuration Tools.
-""")
+"""
+ )
def get_all_policy_files(self, service: str) -> List[str]:
"""Just get a straightforward list of all relevant policy files."""
@@ -50,8 +54,9 @@ def get_all_policy_files(self, service: str) -> List[str]:
except subprocess.CalledProcessError:
return []
- def get_conflicting_policy_files(self, service: str,
- own_file: str) -> List[str]:
+ def get_conflicting_policy_files(
+ self, service: str, own_file: str
+ ) -> List[str]:
"""
Get a list of policy files (as str) that apply to the selected service
and are before it in load order.
@@ -71,8 +76,9 @@ def get_conflicting_policy_files(self, service: str,
conflicting_files.append(f)
return conflicting_files
- def get_rules_from_filename(self, filename: str, default_policy: str) -> \
- Tuple[List[Rule], Optional[str]]:
+ def get_rules_from_filename(
+ self, filename: str, default_policy: str
+ ) -> Tuple[List[Rule], Optional[str]]:
"""Get rules contained in a provided file. If the file does not exist,
return default policy.
Return list of Rule objects and str of the PolicyClient's token
@@ -94,16 +100,21 @@ def compare_rules_to_text(self, rules, file_text) -> bool:
return compare_rule_lists(rules, second_rules)
@staticmethod
- def new_rule(service: str, source: str, target: str, action: str,
- argument: str = "*") -> Rule:
+ def new_rule(
+ service: str, source: str, target: str, action: str, argument: str = "*"
+ ) -> Rule:
"""Create a new Rule object from given parameters: service, source,
target and action should be provided according to policy file specs."""
return Rule.from_line(
- None, f"{service}\t{argument}\t{source}\t{target}\t{action}",
- filepath=None, lineno=0)
+ None,
+ f"{service}\t{argument}\t{source}\t{target}\t{action}",
+ filepath=None,
+ lineno=0,
+ )
- def save_rules(self, file_name: str, rules_list: List[Rule],
- token: Optional[str]):
+ def save_rules(
+ self, file_name: str, rules_list: List[Rule], token: Optional[str]
+ ):
"""Save provided list of rules to a file. Must provide
a token corresponding to last file access, to avoid unexpected
overwriting."""
@@ -112,10 +123,13 @@ def save_rules(self, file_name: str, rules_list: List[Rule],
def rules_to_text(self, rules_list: List[Rule]) -> str:
"""Convert list of Rules to text ready to be stored in a file."""
- return self.policy_disclaimer + \
- '\n'.join([str(rule) for rule in rules_list]) + '\n'
+ return (
+ self.policy_disclaimer
+ + "\n".join([str(rule) for rule in rules_list])
+ + "\n"
+ )
@staticmethod
def text_to_rules(text: str) -> List[Rule]:
"""Convert policy file text to a list of Rules."""
- return StringPolicy(policy={'__main__': text}).rules
+ return StringPolicy(policy={"__main__": text}).rules
diff --git a/qubes_config/global_config/policy_rules.py b/qubes_config/global_config/policy_rules.py
index cf215695..07baaa11 100644
--- a/qubes_config/global_config/policy_rules.py
+++ b/qubes_config/global_config/policy_rules.py
@@ -21,17 +21,28 @@
import abc
from typing import Dict, Optional
-from qrexec.policy.parser import Rule, Allow, Ask, Source, Target, Action, \
- DispVM, DispVMTemplate
+from qrexec.policy.parser import (
+ Rule,
+ Allow,
+ Ask,
+ Source,
+ Target,
+ Action,
+ DispVM,
+ DispVMTemplate,
+)
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
+
class AbstractRuleWrapper(abc.ABC):
"""Wrapper for Rule objects.
ACTION_CHOICES provides human-understandable names for choosing
action value in dropdowns."""
+
ACTION_CHOICES: Dict[str, str] = {}
def __init__(self, rule: Rule):
@@ -83,24 +94,25 @@ def is_rule_fundamental(self) -> bool:
Return True if the rule should be placed in the main list, False if it's
an exception.
"""
- return self.source == '@anyvm' and self.target == '@anyvm'
+ return self.source == "@anyvm" and self.target == "@anyvm"
- def is_rule_conflicting(self, other_source: str, other_target: str,
- other_action: str) -> bool:
+ def is_rule_conflicting(
+ self, other_source: str, other_target: str, other_action: str
+ ) -> bool:
# pylint: disable=unused-argument
"""
Return True if rule with other_source and other_target would conflict
with self.
"""
- return self.source == other_source and \
- self.target == other_target
+ return self.source == other_source and self.target == other_target
@staticmethod
- def get_rule_errors(source: str, target: str, action: str) -> \
- Optional[str]: # pylint: disable=unused-argument
+ # pylint: disable=unused-argument
+ def get_rule_errors(source: str, target: str, action: str) -> Optional[str]:
"""Return None if rule is valid and str describing error if not."""
return None
+
class RuleSimple(AbstractRuleWrapper):
"""
Simple Rule wrapper, where:
@@ -109,20 +121,20 @@ class RuleSimple(AbstractRuleWrapper):
action = just action, without params.
Returns and accepts strings as target/source/action.
"""
- ACTION_CHOICES = {
- "ask": _("ask"),
- "allow": _("always"),
- "deny": _("never")
- }
+
+ ACTION_CHOICES = {"ask": _("ask"), "allow": _("always"), "deny": _("never")}
+
def __init__(self, rule: Rule):
"""
:param rule: Rule object
"""
super().__init__(rule)
- if str(rule.action) not in ['ask', 'deny', 'allow'] \
- or rule.argument is not None:
- raise ValueError(_('Unrecognized action: ') + str(rule.action))
+ if (
+ str(rule.action) not in ["ask", "deny", "allow"]
+ or rule.argument is not None
+ ):
+ raise ValueError(_("Unrecognized action: ") + str(rule.action))
@property
def target(self):
@@ -158,36 +170,34 @@ def action(self, new_value):
def raw_rule(self):
return self._rule
+
class RuleSimpleAskIsAllow(RuleSimple):
"""Simple rule where there is no Allow and Ask is pretending to be Allow.
Used chiefly by Clipboard rules."""
- ACTION_CHOICES = {
- "ask": _("always"),
- "deny": _("never")
- }
+
+ ACTION_CHOICES = {"ask": _("always"), "deny": _("never")}
+
def __init__(self, rule: Rule):
"""
:param rule: Rule object
"""
super().__init__(rule)
- if str(rule.action) not in ['ask', 'deny'] or rule.argument is not None:
- raise ValueError(_('Unrecognized action: ') + str(rule.action))
+ if str(rule.action) not in ["ask", "deny"] or rule.argument is not None:
+ raise ValueError(_("Unrecognized action: ") + str(rule.action))
@staticmethod
- def get_rule_errors(source: str, target: str, action: str) -> \
- Optional[str]:
+ def get_rule_errors(source: str, target: str, action: str) -> Optional[str]:
# action must be a simple ask/allow/deny
- if action not in ['ask', 'deny']:
- return _('Unrecognized action: ') + action
+ if action not in ["ask", "deny"]:
+ return _("Unrecognized action: ") + action
return None
+
class RuleSimpleNoAllow(RuleSimple):
"""Simple rule that has no Allow option"""
- ACTION_CHOICES = {
- "ask": _("can"),
- "deny": _("can not")
- }
+
+ ACTION_CHOICES = {"ask": _("can"), "deny": _("can not")}
def __init__(self, rule: Rule):
"""
@@ -195,15 +205,14 @@ def __init__(self, rule: Rule):
"""
super().__init__(rule)
- if str(rule.action) not in ['ask', 'deny'] or rule.argument is not None:
- raise ValueError(_('Unrecognized action: ') + str(rule.action))
+ if str(rule.action) not in ["ask", "deny"] or rule.argument is not None:
+ raise ValueError(_("Unrecognized action: ") + str(rule.action))
@staticmethod
- def get_rule_errors(source: str, target: str, action: str) -> \
- Optional[str]:
+ def get_rule_errors(source: str, target: str, action: str) -> Optional[str]:
# action must be a simple ask/allow/deny
- if action not in ['ask', 'deny']:
- return _('Unrecognized action: ') + action
+ if action not in ["ask", "deny"]:
+ return _("Unrecognized action: ") + action
return None
@@ -216,10 +225,11 @@ class RuleTargetedAdminVM(AbstractRuleWrapper):
- if action is ask, wrapped rule must have default_target=@adminvm
Returns strings as target/source/action. Target is not settable.
"""
+
ACTION_CHOICES = {
"ask": _("enable"),
"allow": _("allow"),
- "deny": _("disable")
+ "deny": _("disable"),
}
def __init__(self, rule: Rule):
@@ -229,24 +239,26 @@ def __init__(self, rule: Rule):
super().__init__(rule)
if rule.argument is not None:
raise ValueError(_("Rule cannot have an argument"))
- if rule.target != '@adminvm':
- raise ValueError(_('Target must be @adminvm'))
+ if rule.target != "@adminvm":
+ raise ValueError(_("Target must be @adminvm"))
if isinstance(rule.action, Ask):
- if rule.action.default_target != '@adminvm':
- raise ValueError(_('If action is ask, '
- 'default_target must be @adminvm'))
+ if rule.action.default_target != "@adminvm":
+ raise ValueError(
+ _("If action is ask, default_target must be @adminvm")
+ )
if isinstance(rule.action, Allow):
if rule.action.target:
- raise ValueError(_('If action is allow, no '
- 'parameters are allowed'))
+ raise ValueError(
+ _("If action is allow, no parameters are allowed")
+ )
@property
def target(self):
- return '@adminvm'
+ return "@adminvm"
@target.setter
def target(self, new_value):
- raise ValueError(_('Cannot set target on this type of rule.'))
+ raise ValueError(_("Cannot set target on this type of rule."))
@property
def source(self):
@@ -265,8 +277,8 @@ def action(self):
def action(self, new_value):
new_action = Action[new_value].value(self._rule)
self._rule.action = new_action
- if new_value == 'ask':
- self._rule.action.default_target = '@adminvm'
+ if new_value == "ask":
+ self._rule.action.default_target = "@adminvm"
@property
def raw_rule(self):
@@ -285,10 +297,11 @@ class RuleTargeted(AbstractRuleWrapper):
- else use original rule's normal target
Returns and accepts strings as target/source/action.
"""
+
ACTION_CHOICES = {
"ask": _("ask"),
"allow": _("automatically"),
- "deny": _("never")
+ "deny": _("never"),
}
def __init__(self, rule: Rule):
@@ -298,18 +311,19 @@ def __init__(self, rule: Rule):
super().__init__(rule)
if rule.argument is not None:
raise ValueError(_("Rule cannot have an argument"))
- errors = self.get_rule_errors(str(rule.source), str(rule.target),
- str(rule.action))
+ errors = self.get_rule_errors(
+ str(rule.source), str(rule.target), str(rule.action)
+ )
if errors:
raise ValueError(errors)
@property
def target(self):
if isinstance(self._rule.action, Ask):
- if self._rule.target == '@default':
+ if self._rule.target == "@default":
return str(self._rule.action.default_target)
if isinstance(self._rule.action, Allow):
- if self._rule.target == '@default':
+ if self._rule.target == "@default":
return str(self._rule.action.target)
return str(self._rule.target)
@@ -317,20 +331,20 @@ def target(self):
def target(self, new_value):
new_target = Target(new_value)
- if new_value.startswith('@'):
+ if new_value.startswith("@"):
self._rule.target = new_target
- if hasattr(self._rule.action, 'target'):
+ if hasattr(self._rule.action, "target"):
self._rule.action.target = None
- if hasattr(self._rule.action, 'default_target'):
+ if hasattr(self._rule.action, "default_target"):
self._rule.action.default_target = None
return
if isinstance(self._rule.action, Ask):
- self._rule.target = Target('@default')
+ self._rule.target = Target("@default")
self._rule.action.default_target = new_target
return
if isinstance(self._rule.action, Allow):
- self._rule.target = Target('@default')
+ self._rule.target = Target("@default")
self._rule.action.target = new_target
return
@@ -363,27 +377,31 @@ def raw_rule(self):
def is_rule_fundamental(self) -> bool:
if super().is_rule_fundamental():
return True
- return self.source == '@anyvm' and self.raw_rule.target == '@dispvm'
+ return self.source == "@anyvm" and self.raw_rule.target == "@dispvm"
@staticmethod
def get_rule_errors(source: str, target: str, action: str) -> Optional[str]:
- if not source.startswith('@'):
- if target.startswith('@') and target != '@dispvm':
- if action in ('ask', 'allow'):
- return _('This type of action supports only single-qube '
- 'destination qubes for single-qube source qubes.')
+ if not source.startswith("@"):
+ if target.startswith("@") and target != "@dispvm":
+ if action in ("ask", "allow"):
+ return _(
+ "This type of action supports only single-qube "
+ "destination qubes for single-qube source qubes."
+ )
return None
- def is_rule_conflicting(self, other_source: str, other_target: str,
- other_action: str) -> bool:
+ def is_rule_conflicting(
+ self, other_source: str, other_target: str, other_action: str
+ ) -> bool:
"""
Return True if rule with other_source and other_target would conflict
with self.
"""
- if super().is_rule_conflicting(other_source, other_target,
- other_action):
+ if super().is_rule_conflicting(
+ other_source, other_target, other_action
+ ):
return True
- if self.action == 'allow' and other_source == self.source:
+ if self.action == "allow" and other_source == self.source:
return True
return False
@@ -400,11 +418,9 @@ class RuleDispVM(AbstractRuleWrapper):
@dispvm:target_name, or @dispvm if it's default dispvm
Returns and accepts strings as target/source/action.
"""
- ACTION_CHOICES = {
- "ask": _("ask"),
- "allow": _("always"),
- "deny": _("never")
- }
+
+ ACTION_CHOICES = {"ask": _("ask"), "allow": _("always"), "deny": _("never")}
+
def __init__(self, rule: Rule):
"""
:param rule: Rule object
@@ -414,33 +430,34 @@ def __init__(self, rule: Rule):
if rule.argument is not None:
raise ValueError(_("Rule cannot have an argument"))
- if str(rule.target) != '@dispvm':
- raise ValueError('Target must be @dispvm')
+ if str(rule.target) != "@dispvm":
+ raise ValueError("Target must be @dispvm")
- if isinstance(rule.action, Ask) and \
- not isinstance(rule.action.default_target,
- (DispVM, DispVMTemplate)):
+ if isinstance(rule.action, Ask) and not isinstance(
+ rule.action.default_target, (DispVM, DispVMTemplate)
+ ):
raise ValueError("default_target must include @dispvm")
- if isinstance(rule.action, Allow) and \
- not isinstance(rule.action.target, (DispVM, DispVMTemplate)):
+ if isinstance(rule.action, Allow) and not isinstance(
+ rule.action.target, (DispVM, DispVMTemplate)
+ ):
raise ValueError("target must include @dispvm")
@property
def target(self):
target = ""
if isinstance(self._rule.action, Ask):
- target = str(self._rule.action.default_target or '')
+ target = str(self._rule.action.default_target or "")
if isinstance(self._rule.action, Allow):
- target = str(self._rule.action.target or '')
+ target = str(self._rule.action.target or "")
if target.startswith("@dispvm:"):
- target = target[len("@dispvm:"):]
+ target = target[len("@dispvm:") :]
return target
@target.setter
def target(self, new_value):
# when setting to a non-@dispvm, append @dispvm: at the start
- if not new_value.startswith('@dispvm'):
- new_value = '@dispvm:' + new_value
+ if not new_value.startswith("@dispvm"):
+ new_value = "@dispvm:" + new_value
new_target = Target(new_value)
if isinstance(self._rule.action, Ask):
@@ -449,7 +466,7 @@ def target(self, new_value):
if isinstance(self._rule.action, Allow):
self._rule.action.target = new_target
return
- return # deny has no target
+ return # deny has no target
@property
def source(self):
@@ -466,13 +483,13 @@ def action(self):
@action.setter
def action(self, new_value):
- if self.action == 'deny':
- old_target = '@dispvm'
+ if self.action == "deny":
+ old_target = "@dispvm"
else:
old_target = self.target
new_action = Action[new_value].value(self._rule)
self._rule.action = new_action
- if new_value != 'deny':
+ if new_value != "deny":
# when switching from anything to deny,
# there is no need to change target
self.target = old_target
@@ -484,8 +501,9 @@ def raw_rule(self):
def is_rule_fundamental(self) -> bool:
return False
- def is_rule_conflicting(self, other_source: str, other_target: str,
- other_action: str) -> bool:
+ def is_rule_conflicting(
+ self, other_source: str, other_target: str, other_action: str
+ ) -> bool:
"""
Return True if rule with other_source and other_target would conflict
with self.
@@ -497,7 +515,8 @@ def is_rule_conflicting(self, other_source: str, other_target: str,
class AbstractVerbDescription(abc.ABC):
"""Class used to represent human-readable verb descriptions:
- Qube1 (will) ACTION (verb_description) Qube2"""
+ Qube1 (will) ACTION (verb_description) Qube2"""
+
@abc.abstractmethod
def get_verb_for_action_and_target(self, action: str, target: str) -> str:
"""
@@ -508,6 +527,7 @@ def get_verb_for_action_and_target(self, action: str, target: str) -> str:
class SimpleVerbDescription(AbstractVerbDescription):
"""Simplest verb description, where a given Action has one corresponding
description"""
+
def __init__(self, descr: Dict[str, str]):
"""
:param descr: Dict of action: description, where action is one of
@@ -526,8 +546,12 @@ def get_verb_for_action_and_target(self, action: str, target: str) -> str:
class TargetedVerbDescription(AbstractVerbDescription):
"""Verb description for more complex cases using target= and
default_target."""
- def __init__(self, single_target_descr: Dict[str, str],
- multi_target_descr: Dict[str, str]):
+
+ def __init__(
+ self,
+ single_target_descr: Dict[str, str],
+ multi_target_descr: Dict[str, str],
+ ):
"""
Both parameters are dicts of action: description, where action is one of
ask, allow, deny
@@ -539,13 +563,20 @@ def __init__(self, single_target_descr: Dict[str, str],
self.multi_target_descr = multi_target_descr
self.max_length = max(
- (len(x) for x in [*self.single_target_descr.values(),
- *self.multi_target_descr.values()]))
-
+ (
+ len(x)
+ for x in [
+ *self.single_target_descr.values(),
+ *self.multi_target_descr.values(),
+ ]
+ )
+ )
def get_verb_for_action_and_target(self, action: str, target: str) -> str:
- if target.startswith('@') and target != '@dispvm':
+ if target.startswith("@") and target != "@dispvm":
return self.multi_target_descr.get(action, "").rjust(
- self.max_length, " ")
+ self.max_length, " "
+ )
return self.single_target_descr.get(action, "").rjust(
- self.max_length, " ")
+ self.max_length, " "
+ )
diff --git a/qubes_config/global_config/rule_list_widgets.py b/qubes_config/global_config/rule_list_widgets.py
index a81e2548..ef86762f 100644
--- a/qubes_config/global_config/rule_list_widgets.py
+++ b/qubes_config/global_config/rule_list_widgets.py
@@ -20,8 +20,12 @@
"""Widgets used by various list of policy rules."""
from typing import Optional, Dict, Callable
-from ..widgets.gtk_widgets import VMListModeler, TextModeler,\
- ImageTextButton, TokenName
+from ..widgets.gtk_widgets import (
+ VMListModeler,
+ TextModeler,
+ ImageTextButton,
+ TokenName,
+)
from ..widgets.gtk_utils import show_error, ask_question
from ..widgets.utils import BiDictionary
from .policy_rules import AbstractRuleWrapper, AbstractVerbDescription
@@ -31,10 +35,11 @@
import qubesadmin
import qubesadmin.vm
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -50,7 +55,7 @@
"@type:AppVM": _("TYPE: APP"),
"@type:TemplateVM": _("TYPE: TEMPLATES"),
"@type:DispVM": _("TYPE: DISPOSABLE"),
- "@adminvm": _("TYPE: ADMINVM")
+ "@adminvm": _("TYPE: ADMINVM"),
}
TARGET_CATEGORIES = {
@@ -74,15 +79,19 @@
class VMWidget(Gtk.Box):
"""VM/category selection widget."""
- def __init__(self,
- qapp: qubesadmin.Qubes,
- categories: Optional[Dict[str, str]],
- initial_value: str,
- additional_text: Optional[str] = None,
- additional_widget: Optional[Gtk.Widget] = None,
- filter_function: Optional[Callable[[qubesadmin.vm.QubesVM],
- bool]] = None,
- change_callback: Optional[Callable] = None):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ categories: Optional[Dict[str, str]],
+ initial_value: str,
+ additional_text: Optional[str] = None,
+ additional_widget: Optional[Gtk.Widget] = None,
+ filter_function: Optional[
+ Callable[[qubesadmin.vm.QubesVM], bool]
+ ] = None,
+ change_callback: Optional[Callable] = None,
+ ):
"""
:param qapp: Qubes object
:param categories: list of additional categories available for this
@@ -98,20 +107,24 @@ def __init__(self,
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.qapp = qapp
self.selected_value = initial_value
- self.filter_function = filter_function if filter_function else \
- lambda x: str(x) != 'dom0'
+ self.filter_function = (
+ filter_function if filter_function else lambda x: str(x) != "dom0"
+ )
self.combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
self.combobox.get_child().set_width_chars(24)
- self.model = VMListModeler(combobox=self.combobox,
- qapp=self.qapp,
- filter_function=self.filter_function,
- event_callback=change_callback,
- current_value=str(self.selected_value),
- additional_options=categories)
+ self.model = VMListModeler(
+ combobox=self.combobox,
+ qapp=self.qapp,
+ filter_function=self.filter_function,
+ event_callback=change_callback,
+ current_value=str(self.selected_value),
+ additional_options=categories,
+ )
- self.name_widget = TokenName(self.selected_value, self.qapp,
- categories=categories)
+ self.name_widget = TokenName(
+ self.selected_value, self.qapp, categories=categories
+ )
self.selectors_hidden: bool = False # in some rare cases,
# combobox and name widget should be hidden
@@ -124,11 +137,9 @@ def __init__(self,
self.combobox.set_halign(Gtk.Align.START)
if additional_text:
- additional_text_widget = \
- Gtk.Label()
+ additional_text_widget = Gtk.Label()
additional_text_widget.set_text(additional_text)
- additional_text_widget.get_style_context().add_class(
- 'didascalia')
+ additional_text_widget.get_style_context().add_class("didascalia")
additional_text_widget.set_halign(Gtk.Align.END)
self.pack_end(additional_text_widget, False, False, 0)
if additional_widget:
@@ -159,7 +170,9 @@ def save(self):
if not self.model.get_selected():
raise ValueError(
_("{name} is not a valid qube name.").format(
- name=self.model.entry_box.get_text()))
+ name=self.model.entry_box.get_text()
+ )
+ )
new_value = str(self.model.get_selected())
self.selected_value = new_value
self.name_widget.set_token(new_value)
@@ -181,13 +194,17 @@ def show_selectors(self):
self.selectors_hidden = False
self.set_editable(True)
+
class ActionWidget(Gtk.Box):
"""Action selection widget."""
- def __init__(self,
- choices: Dict[str, str],
- verb_description: Optional[AbstractVerbDescription],
- rule: AbstractRuleWrapper,
- action_style_class: str = 'action_text'):
+
+ def __init__(
+ self,
+ choices: Dict[str, str],
+ verb_description: Optional[AbstractVerbDescription],
+ rule: AbstractRuleWrapper,
+ action_style_class: str = "action_text",
+ ):
"""
:param verb_description: AbstractVerbDescription object to get
additional text
@@ -205,13 +222,15 @@ def __init__(self,
self.model = TextModeler(
self.combobox,
self.choices.inverted,
- selected_value=self.selected_value)
+ selected_value=self.selected_value,
+ )
self.name_widget = Gtk.Label()
self.name_widget.get_style_context().add_class(action_style_class)
if self.verb_description:
self.additional_text_widget = Gtk.Label()
self.additional_text_widget.get_style_context().add_class(
- 'didascalia')
+ "didascalia"
+ )
else:
self.additional_text_widget = None
@@ -228,23 +247,27 @@ def __init__(self,
self._format_new_value(self.selected_value)
self.callback: Optional[Callable] = None
- self.combobox.connect('changed', self._combobox_changed)
+ self.combobox.connect("changed", self._combobox_changed)
self.set_editable(False)
def _combobox_changed(self, *_args):
if self.verb_description:
self.additional_text_widget.set_text(
self.verb_description.get_verb_for_action_and_target(
- self.get_selected(), self.rule.target))
+ self.get_selected(), self.rule.target
+ )
+ )
if self.callback:
self.callback()
def _format_new_value(self, new_value):
- self.name_widget.set_markup(f'{self.choices[new_value]}')
+ self.name_widget.set_markup(f"{self.choices[new_value]}")
if self.verb_description:
self.additional_text_widget.set_text(
self.verb_description.get_verb_for_action_and_target(
- new_value, self.rule.target))
+ new_value, self.rule.target
+ )
+ )
def set_editable(self, editable: bool):
"""Change state between editable and non-editable."""
@@ -280,18 +303,22 @@ def set_callback(self, callback: Callable):
class RuleListBoxRow(Gtk.ListBoxRow):
"""Row in a listbox representing a policy rule"""
- def __init__(self,
- parent_handler,
- rule: AbstractRuleWrapper,
- qapp: qubesadmin.Qubes,
- verb_description: Optional[AbstractVerbDescription] = None,
- enable_delete: bool = True,
- enable_vm_edit: bool = True,
- initial_verb: str = _("will"),
- custom_deletion_warning: str = _("Are you sure you want to "
- "delete this rule?"),
- is_new_row: bool = False,
- enable_adminvm: bool = False):
+
+ def __init__(
+ self,
+ parent_handler,
+ rule: AbstractRuleWrapper,
+ qapp: qubesadmin.Qubes,
+ verb_description: Optional[AbstractVerbDescription] = None,
+ enable_delete: bool = True,
+ enable_vm_edit: bool = True,
+ initial_verb: str = _("will"),
+ custom_deletion_warning: str = _(
+ "Are you sure you want to delete this rule?"
+ ),
+ is_new_row: bool = False,
+ enable_adminvm: bool = False,
+ ):
"""
:param parent_handler: PolicyHandler object this rule belongs to, or
other owner object that implements verify_new_rule method.
@@ -327,7 +354,7 @@ def __init__(self,
self.title_label = Gtk.Label()
self.title_label.set_text(_("Editing rule:"))
self.title_label.set_no_show_all(True)
- self.title_label.get_style_context().add_class('small_title')
+ self.title_label.get_style_context().add_class("small_title")
self.title_label.set_halign(Gtk.Align.START)
self.outer_box.pack_start(self.title_label, False, False, 0)
@@ -344,15 +371,20 @@ def __init__(self,
self.outer_box.pack_start(self.main_widget_box, False, False, 0)
self.additional_widget_box = Gtk.Box(
- orientation=Gtk.Orientation.HORIZONTAL)
+ orientation=Gtk.Orientation.HORIZONTAL
+ )
save_button = ImageTextButton(
- icon_name="qubes-ok", label=_("ACCEPT"),
+ icon_name="qubes-ok",
+ label=_("ACCEPT"),
click_function=self.validate_and_save,
- style_classes=["button_save", "flat_button"])
+ style_classes=["button_save", "flat_button"],
+ )
cancel_button = ImageTextButton(
- icon_name="qubes-delete", label=_("CANCEL"),
+ icon_name="qubes-delete",
+ label=_("CANCEL"),
click_function=self.revert,
- style_classes=["button_cancel", "flat_button"])
+ style_classes=["button_cancel", "flat_button"],
+ )
self.additional_widget_box.pack_end(save_button, False, False, 10)
self.additional_widget_box.pack_end(cancel_button, False, False, 10)
@@ -370,44 +402,53 @@ def get_source_widget(self) -> VMWidget:
else:
cat = SOURCE_CATEGORIES
return VMWidget(
- self.qapp, cat, self.rule.source,
- additional_text=self.initial_verb)
+ self.qapp, cat, self.rule.source, additional_text=self.initial_verb
+ )
def get_target_widget(self) -> VMWidget:
"""Widget to be used for target VM"""
return VMWidget(
- self.qapp, TARGET_CATEGORIES, self.rule.target,
- additional_widget=self._get_delete_button())
+ self.qapp,
+ TARGET_CATEGORIES,
+ self.rule.target,
+ additional_widget=self._get_delete_button(),
+ )
def get_action_widget(self) -> ActionWidget:
"""Widget to be used for Action"""
- return ActionWidget(self.rule.ACTION_CHOICES,
- self.verb_description, self.rule)
+ return ActionWidget(
+ self.rule.ACTION_CHOICES, self.verb_description, self.rule
+ )
def _get_delete_button(self) -> Gtk.Button:
"""Get a delete button appropriate for the class."""
if self.enable_delete:
- delete_button = ImageTextButton(icon_name='qubes-delete',
- label=None,
- click_function=self._delete_self,
- style_classes=["flat"])
+ delete_button = ImageTextButton(
+ icon_name="qubes-delete",
+ label=None,
+ click_function=self._delete_self,
+ style_classes=["flat"],
+ )
else:
- delete_button = ImageTextButton(icon_name='qubes-padlock',
- label=None,
- click_function=None,
- style_classes=["flat"])
+ delete_button = ImageTextButton(
+ icon_name="qubes-padlock",
+ label=None,
+ click_function=None,
+ style_classes=["flat"],
+ )
return delete_button
def _do_delete_self(self, force: bool = False):
"""Delete self; if force=True, do not ask user if sure,"""
if not force:
- response = ask_question(self, _("Delete rule"),
- self.custom_deletion_warning)
+ response = ask_question(
+ self, _("Delete rule"), self.custom_deletion_warning
+ )
if response == Gtk.ResponseType.NO:
return
parent_widget = self.get_parent()
parent_widget.remove(self)
- parent_widget.emit('rules-changed', None)
+ parent_widget.emit("rules-changed", None)
def _delete_self(self, *_args):
"""Remove self from parent. Used to delete the rule."""
@@ -421,7 +462,7 @@ def set_edit_mode(self, editing: bool = True, setup: bool = False):
an action
"""
if editing:
- self.get_style_context().add_class('edited_row')
+ self.get_style_context().add_class("edited_row")
self.title_label.set_visible(True)
self.additional_widget_box.set_visible(True)
else:
@@ -429,7 +470,7 @@ def set_edit_mode(self, editing: bool = True, setup: bool = False):
if not setup and self.is_new_row:
self._do_delete_self(force=True)
return
- self.get_style_context().remove_class('edited_row')
+ self.get_style_context().remove_class("edited_row")
self.title_label.set_visible(False)
self.additional_widget_box.set_visible(False)
@@ -454,9 +495,11 @@ def is_changed(self) -> bool:
"""Return True if rule was changed."""
if not self.editing:
return False
- return self.source_widget.is_changed() or \
- self.action_widget.is_changed() or \
- self.target_widget.is_changed()
+ return (
+ self.source_widget.is_changed()
+ or self.action_widget.is_changed()
+ or self.target_widget.is_changed()
+ )
def revert(self, *_args):
"""Revert all changes to the Rule."""
@@ -471,17 +514,25 @@ def validate_and_save(self, *_args) -> bool:
error = self.rule.get_rule_errors(new_source, new_target, new_action)
if error:
- show_error(self.source_widget, _("Invalid rule"),
- _('This rule is not valid: {error}').format(
- error=error))
+ show_error(
+ self.source_widget,
+ _("Invalid rule"),
+ _("This rule is not valid: {error}").format(error=error),
+ )
return False
- error = self.parent_handler.verify_new_rule(self, new_source,
- new_target, new_action)
+ error = self.parent_handler.verify_new_rule(
+ self, new_source, new_target, new_action
+ )
if error:
- show_error(self.source_widget, _("Cannot save rule"),
- _('This rule conflicts with the following existing'
- ' rule:\n{error}\n').format(error=error))
+ show_error(
+ self.source_widget,
+ _("Cannot save rule"),
+ _(
+ "This rule conflicts with the following existing"
+ " rule:\n{error}\n"
+ ).format(error=error),
+ )
return False
try:
@@ -500,84 +551,116 @@ def validate_and_save(self, *_args) -> bool:
self.set_edit_mode(False)
self.get_parent().invalidate_sort()
- self.get_parent().emit('rules-changed', None)
+ self.get_parent().emit("rules-changed", None)
return True
class FilteredListBoxRow(RuleListBoxRow):
"""Row with limited set of source and target qubes"""
- def __init__(self,
- parent_handler,
- rule: AbstractRuleWrapper,
- qapp: qubesadmin.Qubes,
- verb_description: Optional[AbstractVerbDescription] = None,
- enable_delete: bool = True,
- enable_vm_edit: bool = True,
- initial_verb: str = _("uses"),
- filter_target: Optional[Callable] = None,
- filter_source: Optional[Callable] = None,
- is_new_row: bool = False,
- enable_adminvm: bool = False,
- source_categories: Optional[Dict] = None,
- target_categories: Optional[Dict] = None):
+
+ def __init__(
+ self,
+ parent_handler,
+ rule: AbstractRuleWrapper,
+ qapp: qubesadmin.Qubes,
+ verb_description: Optional[AbstractVerbDescription] = None,
+ enable_delete: bool = True,
+ enable_vm_edit: bool = True,
+ initial_verb: str = _("uses"),
+ filter_target: Optional[Callable] = None,
+ filter_source: Optional[Callable] = None,
+ is_new_row: bool = False,
+ enable_adminvm: bool = False,
+ source_categories: Optional[Dict] = None,
+ target_categories: Optional[Dict] = None,
+ ):
self.filter_target = filter_target
self.filter_source = filter_source
self.source_categories = source_categories
self.target_categories = target_categories
- super().__init__(parent_handler, rule, qapp, verb_description,
- enable_delete, enable_vm_edit, initial_verb,
- is_new_row=is_new_row, enable_adminvm=enable_adminvm)
+ super().__init__(
+ parent_handler,
+ rule,
+ qapp,
+ verb_description,
+ enable_delete,
+ enable_vm_edit,
+ initial_verb,
+ is_new_row=is_new_row,
+ enable_adminvm=enable_adminvm,
+ )
def get_source_widget(self) -> VMWidget:
"""Widget to be used for source VM"""
return VMWidget(
- self.qapp, self.source_categories, self.rule.source,
+ self.qapp,
+ self.source_categories,
+ self.rule.source,
additional_text=self.initial_verb,
- filter_function=self.filter_source
+ filter_function=self.filter_source,
)
def get_target_widget(self) -> VMWidget:
"""Widget to be used for target VM"""
return VMWidget(
- self.qapp, self.target_categories, self.rule.target,
+ self.qapp,
+ self.target_categories,
+ self.rule.target,
additional_widget=self._get_delete_button(),
- filter_function=self.filter_target)
+ filter_function=self.filter_target,
+ )
class LimitedRuleListBoxRow(FilteredListBoxRow):
"""Row for a rule with limited set of target VMs."""
- def __init__(self,
- parent_handler,
- rule: AbstractRuleWrapper,
- qapp: qubesadmin.Qubes,
- verb_description: Optional[AbstractVerbDescription] = None,
- enable_delete: bool = True,
- enable_vm_edit: bool = True,
- initial_verb: str = _("will"),
- filter_function: Optional[Callable] = None,
- enable_adminvm: bool = False
- ):
+
+ def __init__(
+ self,
+ parent_handler,
+ rule: AbstractRuleWrapper,
+ qapp: qubesadmin.Qubes,
+ verb_description: Optional[AbstractVerbDescription] = None,
+ enable_delete: bool = True,
+ enable_vm_edit: bool = True,
+ initial_verb: str = _("will"),
+ filter_function: Optional[Callable] = None,
+ enable_adminvm: bool = False,
+ ):
self.filter_function = filter_function
- super().__init__(parent_handler, rule, qapp, verb_description,
- enable_delete, enable_vm_edit, initial_verb,
- enable_adminvm=enable_adminvm)
+ super().__init__(
+ parent_handler,
+ rule,
+ qapp,
+ verb_description,
+ enable_delete,
+ enable_vm_edit,
+ initial_verb,
+ enable_adminvm=enable_adminvm,
+ )
def get_source_widget(self) -> VMWidget:
"""Widget to be used for source VM"""
return VMWidget(
- self.qapp, LIMITED_CATEGORIES, self.rule.source,
- additional_text=self.initial_verb)
+ self.qapp,
+ LIMITED_CATEGORIES,
+ self.rule.source,
+ additional_text=self.initial_verb,
+ )
def get_target_widget(self) -> VMWidget:
"""Widget to be used for target VM"""
return VMWidget(
- self.qapp, None, self.rule.target,
+ self.qapp,
+ None,
+ self.rule.target,
additional_widget=self._get_delete_button(),
- filter_function=self.filter_function)
+ filter_function=self.filter_function,
+ )
class NoActionListBoxRow(FilteredListBoxRow):
"""Row for a rule where we do not want to set or see Action."""
+
def get_action_widget(self) -> ActionWidget:
action_widget = super().get_action_widget()
action_widget.set_no_show_all(True)
@@ -586,30 +669,36 @@ def get_action_widget(self) -> ActionWidget:
class DispvmRuleRow(FilteredListBoxRow):
- def __init__(self,
- parent_handler,
- rule: AbstractRuleWrapper,
- qapp: qubesadmin.Qubes,
- verb_description: Optional[AbstractVerbDescription] = None,
- is_new_row: bool = False):
- super().__init__(parent_handler=parent_handler, rule=rule, qapp=qapp,
- verb_description=verb_description,
- initial_verb=_("will"),
- filter_target=self._dvm_template_filter,
- is_new_row=is_new_row,
- source_categories=SOURCE_CATEGORIES,
- target_categories=DISPVM_CATEGORIES)
+ def __init__(
+ self,
+ parent_handler,
+ rule: AbstractRuleWrapper,
+ qapp: qubesadmin.Qubes,
+ verb_description: Optional[AbstractVerbDescription] = None,
+ is_new_row: bool = False,
+ ):
+ super().__init__(
+ parent_handler=parent_handler,
+ rule=rule,
+ qapp=qapp,
+ verb_description=verb_description,
+ initial_verb=_("will"),
+ filter_target=self._dvm_template_filter,
+ is_new_row=is_new_row,
+ source_categories=SOURCE_CATEGORIES,
+ target_categories=DISPVM_CATEGORIES,
+ )
self.action_widget.set_callback(self._hide_target_on_deny)
- if self.action_widget.get_selected() == 'deny':
+ if self.action_widget.get_selected() == "deny":
self.target_widget.hide_selectors()
@staticmethod
def _dvm_template_filter(vm: qubesadmin.vm.QubesVM):
- return getattr(vm, 'template_for_dispvms', False)
+ return getattr(vm, "template_for_dispvms", False)
def _hide_target_on_deny(self):
- if self.action_widget.get_selected() == 'deny':
+ if self.action_widget.get_selected() == "deny":
self.target_widget.hide_selectors()
else:
self.target_widget.show_selectors()
@@ -617,16 +706,17 @@ def _hide_target_on_deny(self):
class ErrorRuleRow(Gtk.ListBoxRow):
"""A ListBox row representing an error-ed out rule."""
+
def __init__(self, rule):
super().__init__()
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.add(self.box)
- self.get_style_context().add_class('problem_row')
+ self.get_style_context().add_class("problem_row")
self.label = Gtk.Label()
self.label.set_text(str(rule))
- self.label.get_style_context().add_class('red_code')
+ self.label.get_style_context().add_class("red_code")
self.box.pack_start(self.label, False, False, 0)
def __str__(self):
diff --git a/qubes_config/global_config/thisdevice_handler.py b/qubes_config/global_config/thisdevice_handler.py
index 1212803d..c6e4f2ef 100644
--- a/qubes_config/global_config/thisdevice_handler.py
+++ b/qubes_config/global_config/thisdevice_handler.py
@@ -28,80 +28,106 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-logger = logging.getLogger('qubes-global-config')
+logger = logging.getLogger("qubes-global-config")
+
class ThisDeviceHandler(PageHandler):
"""Handler for the ThisDevice page."""
- INPUT_SERVICE = 'qubes.InputKeyboard'
- def __init__(self,
- qapp: qubesadmin.Qubes,
- gtk_builder: Gtk.Builder,
- policy_manager: PolicyManager,
- ):
+ INPUT_SERVICE = "qubes.InputKeyboard"
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ gtk_builder: Gtk.Builder,
+ policy_manager: PolicyManager,
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
self.model_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_model_label')
+ "thisdevice_model_label"
+ )
self.data_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_data_label')
+ "thisdevice_data_label"
+ )
self.certified_box_yes: Gtk.Box = gtk_builder.get_object(
- 'thisdevice_certified_box_yes')
+ "thisdevice_certified_box_yes"
+ )
self.compat_hvm_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_hvm_image')
+ "thisdevice_hvm_image"
+ )
self.compat_hvm_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_hvm_label')
+ "thisdevice_hvm_label"
+ )
self.compat_iommu_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_iommu_image')
+ "thisdevice_iommu_image"
+ )
self.compat_iommu_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_iommu_label')
+ "thisdevice_iommu_label"
+ )
self.compat_hap_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_hap_image')
+ "thisdevice_hap_image"
+ )
self.compat_hap_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_hap_label')
+ "thisdevice_hap_label"
+ )
self.compat_tpm_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_tpm_image')
+ "thisdevice_tpm_image"
+ )
self.compat_tpm_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_tpm_label')
+ "thisdevice_tpm_label"
+ )
self.compat_remapping_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_remapping_image')
+ "thisdevice_remapping_image"
+ )
self.compat_remapping_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_remapping_label')
+ "thisdevice_remapping_label"
+ )
self.compat_usbk_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_usbk_image')
+ "thisdevice_usbk_image"
+ )
self.compat_usbk_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_usbk_label')
+ "thisdevice_usbk_label"
+ )
self.compat_pv_image: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_pv_image')
+ "thisdevice_pv_image"
+ )
self.compat_pv_label: Gtk.Label = gtk_builder.get_object(
- 'thisdevice_pv_label')
+ "thisdevice_pv_label"
+ )
self.compat_pv_tooltip: Gtk.Image = gtk_builder.get_object(
- 'thisdevice_pv_tooltip')
+ "thisdevice_pv_tooltip"
+ )
- self.copy_button: Gtk.Button = \
- gtk_builder.get_object('thisdevice_copy_button')
- self.copy_hcl_button: Gtk.Button = \
- gtk_builder.get_object('thisdevice_copy_hcl_button')
+ self.copy_button: Gtk.Button = gtk_builder.get_object(
+ "thisdevice_copy_button"
+ )
+ self.copy_hcl_button: Gtk.Button = gtk_builder.get_object(
+ "thisdevice_copy_hcl_button"
+ )
label_text = ""
self.hcl_yaml = {}
try:
self.hcl_check = subprocess.check_output(
- ['qubes-hcl-report', '-y']).decode()
+ ["qubes-hcl-report", "-y"]
+ ).decode()
except subprocess.CalledProcessError as ex:
label_text += _("Failed to load system data: {ex}\n").format(
- ex=str(ex))
+ ex=str(ex)
+ )
self.hcl_check = ""
try:
@@ -113,9 +139,10 @@ def __init__(self,
except (yaml.YAMLError, ValueError):
self.hcl_yaml = {}
label_text += _("Failed to load system data.\n")
- self.data_label.get_style_context().add_class('red_code')
+ self.data_label.get_style_context().add_class("red_code")
- label_text += _("""Brand: {brand}
+ label_text += _(
+ """Brand: {brand}
Model: {model}
CPU: {cpu}
@@ -128,141 +155,159 @@ def __init__(self,
BIOS: {bios}
Kernel: {kernel_ver}
Xen: {xen_ver}
-""").format(brand=self._get_data('brand'),
- model=self._get_data('model'),
- cpu=self._get_data('cpu'),
- chipset=self._get_data('chipset'),
- gpu=self._get_data('gpu'),
- memory=self._get_data('memory'),
- qubes_ver=self._get_version('qubes'),
- bios=self._get_data('bios'),
- kernel_ver=self._get_version('kernel'),
- xen_ver=self._get_version('xen'))
- self.set_state(self.compat_hvm_image, self._get_data('hvm'))
+"""
+ ).format(
+ brand=self._get_data("brand"),
+ model=self._get_data("model"),
+ cpu=self._get_data("cpu"),
+ chipset=self._get_data("chipset"),
+ gpu=self._get_data("gpu"),
+ memory=self._get_data("memory"),
+ qubes_ver=self._get_version("qubes"),
+ bios=self._get_data("bios"),
+ kernel_ver=self._get_version("kernel"),
+ xen_ver=self._get_version("xen"),
+ )
+ self.set_state(self.compat_hvm_image, self._get_data("hvm"))
self.compat_hvm_label.set_markup(f"HVM: {self._get_data('hvm')}")
- self.set_state(self.compat_iommu_image, self._get_data('iommu'))
+ self.set_state(self.compat_iommu_image, self._get_data("iommu"))
self.compat_iommu_label.set_markup(
- f"I/O MMU: {self._get_data('iommu')}")
+ f"I/O MMU: {self._get_data('iommu')}"
+ )
- self.set_state(self.compat_hap_image, self._get_data('slat'))
+ self.set_state(self.compat_hap_image, self._get_data("slat"))
self.compat_hap_label.set_markup(
- f"HAP/SLAT: {self._get_data('slat')}")
-
- self.set_state(self.compat_tpm_image,
- 'yes' if self._get_data('tpm') == '1.2' else 'maybe')
- if self._get_data('tpm') == '2.0':
- self.set_state(self.compat_tpm_image, 'maybe')
- self.compat_tpm_label.set_markup(
- _("TPM version: 2.0 (not yet supported)"))
- elif self._get_data('tpm') == '1.2':
- self.set_state(self.compat_tpm_image, 'yes')
+ f"HAP/SLAT: {self._get_data('slat')}"
+ )
+
+ self.set_state(
+ self.compat_tpm_image,
+ "yes" if self._get_data("tpm") == "1.2" else "maybe",
+ )
+ if self._get_data("tpm") == "2.0":
+ self.set_state(self.compat_tpm_image, "maybe")
self.compat_tpm_label.set_markup(
- _("TPM version: 1.2"))
+ _("TPM version: 2.0 (not yet supported)")
+ )
+ elif self._get_data("tpm") == "1.2":
+ self.set_state(self.compat_tpm_image, "yes")
+ self.compat_tpm_label.set_markup(_("TPM version: 1.2"))
else:
- self.set_state(self.compat_tpm_image, 'no')
+ self.set_state(self.compat_tpm_image, "no")
self.compat_tpm_label.set_markup(
- _("TPM version: device not found"))
+ _("TPM version: device not found")
+ )
- self.set_state(self.compat_remapping_image, self._get_data('remap'))
+ self.set_state(self.compat_remapping_image, self._get_data("remap"))
self.compat_remapping_label.set_markup(
- f"Remapping: {self._get_data('remap')}")
+ f"Remapping: {self._get_data('remap')}"
+ )
self.set_policy_state()
- pv_vms = [vm for vm in self.qapp.domains
- if getattr(vm, 'virt_mode', None) == 'pv']
+ pv_vms = [
+ vm
+ for vm in self.qapp.domains
+ if getattr(vm, "virt_mode", None) == "pv"
+ ]
- self.set_state(self.compat_pv_image, 'no' if pv_vms else 'yes')
+ self.set_state(self.compat_pv_image, "no" if pv_vms else "yes")
self.compat_pv_label.set_markup(
- _("PV qubes: {num_pvs} found").format(num_pvs=len(pv_vms)))
+ _("PV qubes: {num_pvs} found").format(num_pvs=len(pv_vms))
+ )
self.compat_pv_tooltip.set_tooltip_markup(
- _("The following qubes have PV virtualization mode:\n - ") +
- '\n - '.join([vm.name for vm in pv_vms]))
+ _("The following qubes have PV virtualization mode:\n - ")
+ + "\n - ".join([vm.name for vm in pv_vms])
+ )
self.compat_pv_tooltip.set_visible(bool(pv_vms))
self.data_label.set_markup(label_text)
self.certified_box_yes.set_visible(self.is_certified())
- self.copy_button.connect('clicked', self._copy_to_clipboard)
- self.copy_hcl_button.connect('clicked', self._copy_to_clipboard)
+ self.copy_button.connect("clicked", self._copy_to_clipboard)
+ self.copy_hcl_button.connect("clicked", self._copy_to_clipboard)
- self.data_label.get_toplevel().connect('page-changed',
- self._page_saved)
+ self.data_label.get_toplevel().connect("page-changed", self._page_saved)
def _get_data(self, name) -> str:
data = self.hcl_yaml.get(name, _("unknown")).strip()
- return data if data else _('unknown')
+ return data if data else _("unknown")
def _get_version(self, name) -> str:
try:
- data = self.hcl_yaml['versions'][0].get(name, _("unknown")).strip()
+ data = self.hcl_yaml["versions"][0].get(name, _("unknown")).strip()
except (KeyError, AttributeError):
return _("unknown")
- return data if data else _('unknown')
+ return data if data else _("unknown")
def _copy_to_clipboard(self, widget: Gtk.Button):
- if widget.get_name() == 'copy_button':
+ if widget.get_name() == "copy_button":
text = self.data_label.get_text()
- elif widget.get_name() == 'copy_hcl_button':
+ elif widget.get_name() == "copy_hcl_button":
text = self.hcl_check
else:
raise ValueError
try:
copy_to_global_clipboard(text)
except Exception: # pylint: disable=broad-except
- show_error(self.copy_button.get_toplevel(),
- _("Failed to copy to Global Clipboard"),
- _("An error occurred while trying to access"
- " Global Clipboard"))
+ show_error(
+ self.copy_button.get_toplevel(),
+ _("Failed to copy to Global Clipboard"),
+ _(
+ "An error occurred while trying to access"
+ " Global Clipboard"
+ ),
+ )
@staticmethod
def set_state(image_widget: Gtk.Image, value: str):
"""Set state of provided widget according to value;
for 'yes', show a green checkmark, for 'maybe' yellow one,
and for all others red X."""
- if value == 'yes':
- image_widget.set_from_pixbuf(load_icon('check_yes', 22, 22))
- elif value == 'maybe':
- image_widget.set_from_pixbuf(load_icon('check_maybe', 22, 22))
+ if value == "yes":
+ image_widget.set_from_pixbuf(load_icon("check_yes", 22, 22))
+ elif value == "maybe":
+ image_widget.set_from_pixbuf(load_icon("check_maybe", 22, 22))
else:
- image_widget.set_from_pixbuf(load_icon('check_no', 20, 20))
+ image_widget.set_from_pixbuf(load_icon("check_no", 20, 20))
def _get_policy_state(self) -> str:
policy_files = sorted(
- self.policy_manager.get_all_policy_files(self.INPUT_SERVICE))
+ self.policy_manager.get_all_policy_files(self.INPUT_SERVICE)
+ )
for f in policy_files:
- if f.startswith('/etc/qubes-rpc'):
- return 'legacy'
+ if f.startswith("/etc/qubes-rpc"):
+ return "legacy"
rules, _token = self.policy_manager.get_rules_from_filename(f, "")
for rule in rules:
if rule.service == self.INPUT_SERVICE:
- if 'allow' in str(rule.action):
- return 'allow'
- return 'deny'
+ if "allow" in str(rule.action):
+ return "allow"
+ return "deny"
def is_certified(self) -> bool:
"""Is this device Qubes certified?"""
return self._get_data("certified") == "yes"
def _page_saved(self, _page: PageHandler, page_name: str):
- if page_name == 'usb':
+ if page_name == "usb":
self.set_policy_state()
def set_policy_state(self):
"""Refresh policy state, because it might have changed since
- we last were here"""
+ we last were here"""
policy_state = self._get_policy_state()
label_text = _("USB keyboards: ")
- if policy_state == 'legacy':
+ if policy_state == "legacy":
self.set_state(self.compat_usbk_image, "maybe")
label_text += _("unknown (legacy policy found)")
- elif policy_state == 'allow':
+ elif policy_state == "allow":
self.set_state(self.compat_usbk_image, "no")
label_text += _("insecure policy")
- elif policy_state == 'deny':
+ elif policy_state == "deny":
self.set_state(self.compat_usbk_image, "yes")
label_text += _("secure policy")
self.compat_usbk_label.set_markup(label_text)
diff --git a/qubes_config/global_config/updates_handler.py b/qubes_config/global_config/updates_handler.py
index d578509a..cce195b3 100644
--- a/qubes_config/global_config/updates_handler.py
+++ b/qubes_config/global_config/updates_handler.py
@@ -44,54 +44,69 @@
import qubesadmin.vm
import qubesadmin.exc
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
class RepoHandler:
"""Handler for repository settings."""
+
def __init__(self, gtk_builder: Gtk.Builder):
- self.dom0_stable_radio: Gtk.RadioButton = \
- gtk_builder.get_object('updates_dom0_stable_radio')
- self.dom0_testing_sec_radio: Gtk.RadioButton = \
- gtk_builder.get_object('updates_dom0_testing_sec_radio')
- self.dom0_testing_radio: Gtk.RadioButton = \
- gtk_builder.get_object('updates_dom0_testing_radio')
-
- self.template_official: Gtk.CheckButton = \
- gtk_builder.get_object('updates_template_official')
- self.template_official_testing: Gtk.CheckButton = \
- gtk_builder.get_object('updates_template_official_testing')
- self.template_community: Gtk.CheckButton = \
- gtk_builder.get_object('updates_template_community')
- self.template_community_testing: Gtk.CheckButton = \
- gtk_builder.get_object('updates_template_community_testing')
-
- self.problems_repo_box: Gtk.Box = \
- gtk_builder.get_object('updates_problem_repo')
- self.problems_label: Gtk.Label = \
- gtk_builder.get_object('updates_problem_label')
+ self.dom0_stable_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "updates_dom0_stable_radio"
+ )
+ self.dom0_testing_sec_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "updates_dom0_testing_sec_radio"
+ )
+ self.dom0_testing_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "updates_dom0_testing_radio"
+ )
+
+ self.template_official: Gtk.CheckButton = gtk_builder.get_object(
+ "updates_template_official"
+ )
+ self.template_official_testing: Gtk.CheckButton = (
+ gtk_builder.get_object("updates_template_official_testing")
+ )
+ self.template_community: Gtk.CheckButton = gtk_builder.get_object(
+ "updates_template_community"
+ )
+ self.template_community_testing: Gtk.CheckButton = (
+ gtk_builder.get_object("updates_template_community_testing")
+ )
+
+ self.problems_repo_box: Gtk.Box = gtk_builder.get_object(
+ "updates_problem_repo"
+ )
+ self.problems_label: Gtk.Label = gtk_builder.get_object(
+ "updates_problem_label"
+ )
# the code below relies on dicts in Python 3.6+ keeping the
# order of items
- self.repo_to_widget_mapping = [{
- 'qubes-dom0-current': self.dom0_stable_radio,
- 'qubes-dom0-security-testing': self.dom0_testing_sec_radio,
- 'qubes-dom0-current-testing': self.dom0_testing_radio,
+ self.repo_to_widget_mapping = [
+ {
+ "qubes-dom0-current": self.dom0_stable_radio,
+ "qubes-dom0-security-testing": self.dom0_testing_sec_radio,
+ "qubes-dom0-current-testing": self.dom0_testing_radio,
},
- {'qubes-templates-itl': self.template_official,
- 'qubes-templates-itl-testing': self.template_official_testing},
- {'qubes-templates-community': self.template_community,
- 'qubes-templates-community-testing':
- self.template_community_testing,
- }]
+ {
+ "qubes-templates-itl": self.template_official,
+ "qubes-templates-itl-testing": self.template_official_testing,
+ },
+ {
+ "qubes-templates-community": self.template_community,
+ "qubes-templates-community-testing": self.template_community_testing, # pylint: disable=line-too-long
+ },
+ ]
self.initial_state: Dict[str, bool] = {}
- self.template_community.connect('toggled', self._community_toggled)
+ self.template_community.connect("toggled", self._community_toggled)
self.repos: Dict[str, Dict] = {}
self._load_data()
@@ -108,12 +123,12 @@ def _community_toggled(self, _widget=None):
def _load_data(self):
# pylint: disable=superfluous-parens
try:
- for row in self._run_qrexec_repo('qubes.repos.List').split('\n'):
- lst = row.split('\0')
+ for row in self._run_qrexec_repo("qubes.repos.List").split("\n"):
+ lst = row.split("\0")
repo_name = lst[0]
self.repos[repo_name] = {}
- self.repos[repo_name]['prettyname'] = lst[1]
- self.repos[repo_name]['enabled'] = lst[2] == 'enabled'
+ self.repos[repo_name]["prettyname"] = lst[1]
+ self.repos[repo_name]["enabled"] = lst[2] == "enabled"
except (RuntimeError, IndexError) as ex:
# disable all repo-related stuff
self.dom0_stable_radio.set_sensitive(False)
@@ -125,36 +140,39 @@ def _load_data(self):
self.repos = {}
self.problems_repo_box.set_visible(True)
self.problems_label.set_text(
- self.problems_label.get_text() +
- _(' Encountered error: ') + str(ex))
+ self.problems_label.get_text()
+ + _(" Encountered error: ")
+ + str(ex)
+ )
def _load_state(self):
for repo_dict in self.repo_to_widget_mapping:
for repo, widget in repo_dict.items():
if repo not in self.repos:
continue
- if self.repos[repo]['enabled']:
- widget.set_active(self.repos[repo]['enabled'])
+ if self.repos[repo]["enabled"]:
+ widget.set_active(self.repos[repo]["enabled"])
for repo_dict in self.repo_to_widget_mapping:
for repo, widget in repo_dict.items():
self.initial_state[repo] = widget.get_active()
@staticmethod
- def _run_qrexec_repo(service, arg=''):
+ def _run_qrexec_repo(service, arg=""):
try:
- return qrexec_call('dom0', service, arg)
+ return qrexec_call("dom0", service, arg)
except subprocess.CalledProcessError as ex:
- raise RuntimeError('qrexec call failed: ' + str(ex.stderr)) from ex
+ raise RuntimeError("qrexec call failed: " + str(ex.stderr)) from ex
except Exception as ex:
- raise RuntimeError('qrexec call failed: ' + str(ex)) from ex
+ raise RuntimeError("qrexec call failed: " + str(ex)) from ex
def _set_repository(self, repository, state):
- action = 'Enable' if state else 'Disable'
- result = self._run_qrexec_repo(f'qubes.repos.{action}', repository)
- if result != 'ok\n':
- raise RuntimeError('qrexec call stdout did not contain "ok"'
- ' as expected')
+ action = "Enable" if state else "Disable"
+ result = self._run_qrexec_repo(f"qubes.repos.{action}", repository)
+ if result != "ok\n":
+ raise RuntimeError(
+ "qrexec call stdout did not contain 'ok' as expected"
+ )
def get_unsaved(self) -> str:
"""Get human-readable description of unsaved changes, or
@@ -169,11 +187,11 @@ def get_unsaved(self) -> str:
for repo_dict in self.repo_to_widget_mapping:
for repo, widget in repo_dict.items():
if self.initial_state[repo] != widget.get_active():
- if 'dom0' in repo:
+ if "dom0" in repo:
dom0_changed = True
- elif 'community' in repo:
+ elif "community" in repo:
community_changed = True
- elif 'itl' in repo:
+ elif "itl" in repo:
itl_changed = True
unsaved = []
if dom0_changed:
@@ -200,8 +218,8 @@ def save(self):
self._set_repository(repo, not found)
except RuntimeError as ex:
raise qubesadmin.exc.QubesException(
- 'Failed to set repository data: '
- f'{escape(str(ex))}') from ex
+ "Failed to set repository data: " f"{escape(str(ex))}"
+ ) from ex
self._load_data()
self._load_state()
@@ -214,42 +232,51 @@ def reset(self):
class UpdateCheckerHandler:
"""Handler for checking for updates settings."""
- FEATURE_NAME = 'service.qubes-update-check'
+
+ FEATURE_NAME = "service.qubes-update-check"
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
self.qapp = qapp
# check for updates dom0 checkbutton
- self.dom0_update_check: Gtk.CheckButton = \
- gtk_builder.get_object('updates_dom0_update_check')
+ self.dom0_update_check: Gtk.CheckButton = gtk_builder.get_object(
+ "updates_dom0_update_check"
+ )
- self.enable_radio: Gtk.RadioButton = \
- gtk_builder.get_object('updates_enable_radio')
- self.disable_radio: Gtk.RadioButton = \
- gtk_builder.get_object('updates_disable_radio')
+ self.enable_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "updates_enable_radio"
+ )
+ self.disable_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "updates_disable_radio"
+ )
# check for if there are exceptions for check upd
- self.exceptions_check: Gtk.CheckButton = \
- gtk_builder.get_object('updates_exceptions_check')
+ self.exceptions_check: Gtk.CheckButton = gtk_builder.get_object(
+ "updates_exceptions_check"
+ )
- self.exception_label: Gtk.Label = \
- gtk_builder.get_object('updates_check_exception_label')
+ self.exception_label: Gtk.Label = gtk_builder.get_object(
+ "updates_check_exception_label"
+ )
- self.initial_dom0 = get_boolean_feature(self.qapp.domains['dom0'],
- self.FEATURE_NAME, True)
+ self.initial_dom0 = get_boolean_feature(
+ self.qapp.domains["dom0"], self.FEATURE_NAME, True
+ )
self.dom0_update_check.set_active(self.initial_dom0)
self.initial_default = get_boolean_feature(
- self.qapp.domains['dom0'],
- 'config.default.qubes-update-check', True)
+ self.qapp.domains["dom0"], "config.default.qubes-update-check", True
+ )
self.initial_exceptions: List[qubesadmin.vm.QubesVM] = []
for vm in self.qapp.domains:
- if vm.klass == 'AdminVM':
+ if vm.klass == "AdminVM":
continue
- if get_boolean_feature(vm, self.FEATURE_NAME, True) != \
- self.initial_default:
+ if (
+ get_boolean_feature(vm, self.FEATURE_NAME, True)
+ != self.initial_default
+ ):
self.initial_exceptions.append(vm)
if self.initial_default:
@@ -260,27 +287,37 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
self.exceptions_check.set_active(bool(self.initial_exceptions))
self.flowbox_handler = VMFlowboxHandler(
- gtk_builder, qapp, "updates_exception",
+ gtk_builder,
+ qapp,
+ "updates_exception",
initial_vms=self.initial_exceptions,
- filter_function=lambda vm: vm.klass != 'AdminVM')
+ filter_function=lambda vm: vm.klass != "AdminVM",
+ )
self._set_label()
- self.enable_radio.connect('toggled', self._enable_disable_toggled)
+ self.enable_radio.connect("toggled", self._enable_disable_toggled)
self.flowbox_handler.set_visible(self.exceptions_check.get_active())
- self.exceptions_check.connect("toggled",
- self._enable_exceptions_clicked)
+ self.exceptions_check.connect(
+ "toggled", self._enable_exceptions_clicked
+ )
def _set_label(self):
if self.enable_radio.get_active():
self.exception_label.set_markup(
- _('Except the following qubes, for which checking for updates'
- ' will be disabled'))
+ _(
+ "Except the following qubes, for which checking for updates"
+ " will be disabled"
+ )
+ )
else:
self.exception_label.set_markup(
- _('Except the following qubes, for which checking for updates'
- ' will be enabled'))
+ _(
+ "Except the following qubes, for which checking for updates"
+ " will be enabled"
+ )
+ )
def _enable_disable_toggled(self, *_args):
self._set_label()
@@ -298,20 +335,24 @@ def get_unsaved(self) -> str:
unsaved.append(_('dom0 "check for updates" setting'))
if self.initial_default != self.enable_radio.get_active():
unsaved.append(_('Default "check for updates" setting'))
- if self.exceptions_check.get_active() != \
- bool(self.initial_exceptions) or \
- self.flowbox_handler.is_changed():
- unsaved.append(_("Qubes selected for unusual 'check for updates'"
- " behaviors"))
+ if (
+ self.exceptions_check.get_active() != bool(self.initial_exceptions)
+ or self.flowbox_handler.is_changed()
+ ):
+ unsaved.append(
+ _("Qubes selected for unusual 'check for updates' behaviors")
+ )
return "\n".join(unsaved)
def save(self):
"""Save any changes."""
# FUTURE: this is fairly slow
if self.initial_dom0 != self.dom0_update_check.get_active():
- apply_feature_change(self.qapp.domains['dom0'],
- self.FEATURE_NAME,
- self.dom0_update_check.get_active())
+ apply_feature_change(
+ self.qapp.domains["dom0"],
+ self.FEATURE_NAME,
+ self.dom0_update_check.get_active(),
+ )
self.initial_dom0 = self.dom0_update_check.get_active()
default_state = self.enable_radio.get_active()
@@ -319,24 +360,30 @@ def save(self):
if self.initial_default != default_state:
apply_feature_change(
- self.qapp.domains['dom0'], 'config.default.qubes-update-check',
- default_state)
+ self.qapp.domains["dom0"],
+ "config.default.qubes-update-check",
+ default_state,
+ )
changed_default = True
self.initial_default = default_state
exceptions = self.flowbox_handler.selected_vms
if changed_default or self.flowbox_handler.is_changed():
for vm in self.qapp.domains:
- if vm.klass == 'AdminVM':
+ if vm.klass == "AdminVM":
continue
- vm_desired_state = default_state if vm not in exceptions else \
- not default_state
+ vm_desired_state = (
+ default_state if vm not in exceptions else not default_state
+ )
vm_value = get_boolean_feature(vm, self.FEATURE_NAME, True)
if vm_value != vm_desired_state:
# if we want False, we need to explicitly set it, else
# we just need to erase the feature
- apply_feature_change(vm, self.FEATURE_NAME,
- None if vm_desired_state else False)
+ apply_feature_change(
+ vm,
+ self.FEATURE_NAME,
+ None if vm_desired_state else False,
+ )
self.flowbox_handler.save()
@@ -350,9 +397,15 @@ def reset(self):
class UpdateProxy:
"""Handler for the rules connected to UpdateProxy policy."""
- def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
- policy_manager: PolicyManager, policy_file_name: str,
- service_name: str):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ qapp: qubesadmin.Qubes,
+ policy_manager: PolicyManager,
+ policy_file_name: str,
+ service_name: str,
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
self.policy_file_name = policy_file_name
@@ -360,42 +413,53 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
self.has_whonix = self._check_for_whonix()
- self.default_updatevm = self.qapp.domains.get('sys-net', None)
- self.default_whonix_updatevm = self.qapp.domains.get('sys-whonix', None)
+ self.default_updatevm = self.qapp.domains.get("sys-net", None)
+ self.default_whonix_updatevm = self.qapp.domains.get("sys-whonix", None)
self.first_eligible_vm = None
for vm in self.qapp.domains:
- if vm.klass != 'AdminVM' and not vm.is_networked():
+ if vm.klass != "AdminVM" and not vm.is_networked():
self.first_eligible_vm = vm
break
- self.def_updatevm_combo: Gtk.ComboBox = \
- gtk_builder.get_object('updates_def_updatevm_combo')
- self.whonix_updatevm_combo: Gtk.ComboBox = \
- gtk_builder.get_object('updates_whonix_updatevm_combo')
- self.whonix_updatevm_box: Gtk.Box = \
- gtk_builder.get_object('updates_whonix_updatevm_box')
+ self.def_updatevm_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "updates_def_updatevm_combo"
+ )
+ self.whonix_updatevm_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "updates_whonix_updatevm_combo"
+ )
+ self.whonix_updatevm_box: Gtk.Box = gtk_builder.get_object(
+ "updates_whonix_updatevm_box"
+ )
self.rules: List[Rule] = []
self.current_token: Optional[str] = None
self.exception_list_handler = PolicyExceptionsHandler(
- gtk_builder=gtk_builder, prefix='updates_updatevm',
+ gtk_builder=gtk_builder,
+ prefix="updates_updatevm",
policy_manager=self.policy_manager,
- row_func=self._get_row, new_rule=self._new_rule,
- exclude_rule=self._rule_filter, enable_raw=False)
+ row_func=self._get_row,
+ new_rule=self._new_rule,
+ exclude_rule=self._rule_filter,
+ enable_raw=False,
+ )
self.updatevm_model = VMListModeler(
- combobox=self.def_updatevm_combo, qapp=self.qapp,
+ combobox=self.def_updatevm_combo,
+ qapp=self.qapp,
filter_function=self._updatevm_filter,
- current_value=None, additional_options={
- "None": _("(none)")})
+ current_value=None,
+ additional_options={"None": _("(none)")},
+ )
self.whonix_updatevm_model = VMListModeler(
- combobox=self.whonix_updatevm_combo, qapp=self.qapp,
+ combobox=self.whonix_updatevm_combo,
+ qapp=self.qapp,
filter_function=self._whonixupdatevm_filter,
- current_value=None, additional_options={
- "None": _("(none)")})
+ current_value=None,
+ additional_options={"None": _("(none)")},
+ )
self.load_rules()
@@ -403,40 +467,45 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
def _check_for_whonix(self) -> bool:
for vm in self.qapp.domains:
- if 'whonix-updatevm' in vm.tags or 'anon-gateway' in vm.tags:
+ if "whonix-updatevm" in vm.tags or "anon-gateway" in vm.tags:
return True
return False
@staticmethod
def _updatevm_filter(vm):
- return getattr(vm, 'provides_network', False)
+ return getattr(vm, "provides_network", False)
@staticmethod
def _whonixupdatevm_filter(vm):
- return 'anon-gateway' in vm.tags
+ return "anon-gateway" in vm.tags
@staticmethod
def _rule_filter(rule):
- if rule.source == '@type:TemplateVM':
+ if rule.source == "@type:TemplateVM":
return True
- if rule.source == '@tag:whonix-updatevm':
+ if rule.source == "@tag:whonix-updatevm":
return True
return False
@staticmethod
def _needs_updatevm_filter(vm):
- if vm.klass in ('AdminVM', 'AppVM'):
+ if vm.klass in ("AdminVM", "AppVM"):
return False
- if getattr(vm, 'template', None):
+ if getattr(vm, "template", None):
return False
- return bool(vm.features.check_with_template(
- 'service.updates-proxy-setup', vm.klass == 'TemplateVM'))
+ return bool(
+ vm.features.check_with_template(
+ "service.updates-proxy-setup", vm.klass == "TemplateVM"
+ )
+ )
def load_rules(self):
"""Load rules into widgets."""
- self.rules, self.current_token = \
+ self.rules, self.current_token = (
self.policy_manager.get_rules_from_filename(
- self.policy_file_name, "")
+ self.policy_file_name, ""
+ )
+ )
def_updatevm = self.default_updatevm
def_whonix_updatevm = None
@@ -444,9 +513,9 @@ def load_rules(self):
def_whonix_updatevm = self.default_whonix_updatevm
for rule in reversed(self.rules):
- if rule.source == '@type:TemplateVM':
+ if rule.source == "@type:TemplateVM":
def_updatevm = rule.action.target
- elif rule.source == '@tag:whonix-updatevm':
+ elif rule.source == "@tag:whonix-updatevm":
def_whonix_updatevm = rule.action.target
if def_updatevm:
@@ -468,18 +537,24 @@ def _get_row(self, rule: Rule, new: bool = False):
initial_verb="uses",
filter_target=self._updatevm_filter,
filter_source=self._needs_updatevm_filter,
- is_new_row=new
+ is_new_row=new,
)
def _new_rule(self) -> Rule:
return self.policy_manager.new_rule(
- service=self.service_name, source=str(self.first_eligible_vm),
- target='@default',
- action=f'allow target={self.default_updatevm}')
+ service=self.service_name,
+ source=str(self.first_eligible_vm),
+ target="@default",
+ action=f"allow target={self.default_updatevm}",
+ )
- def verify_new_rule(self, row: NoActionListBoxRow,
- new_source: str, new_target: str,
- new_action: str) -> Optional[str]:
+ def verify_new_rule(
+ self,
+ row: NoActionListBoxRow,
+ new_source: str,
+ new_target: str,
+ new_action: str,
+ ) -> Optional[str]:
"""
Verify correctness of a rule with new_source, new_target and new_action
if it was to be associated with provided row. Return None if rule would
@@ -487,13 +562,19 @@ def verify_new_rule(self, row: NoActionListBoxRow,
"""
simple_verify = PolicyHandler.verify_rule_against_rows(
self.exception_list_handler.current_rows,
- row, new_source, new_target, new_action)
+ row,
+ new_source,
+ new_target,
+ new_action,
+ )
if simple_verify:
return simple_verify
new_target_vm = self.qapp.domains[new_target]
new_source_vm = self.qapp.domains[new_source]
- if 'whonix-updatevm' in new_source_vm.tags and \
- 'anon-gateway' not in new_target_vm.tags:
+ if (
+ "whonix-updatevm" in new_source_vm.tags
+ and "anon-gateway" not in new_target_vm.tags
+ ):
return _("Whonix qubes can only use Whonix update proxies!")
return None
@@ -511,8 +592,9 @@ def is_changed(self) -> bool:
return True
if self.whonix_updatevm_model.is_changed():
return True
- if [rule.raw_rule for rule in self.current_exception_rules] != \
- self.rules[:-2]:
+ if [
+ rule.raw_rule for rule in self.current_exception_rules
+ ] != self.rules[:-2]:
return True
return False
@@ -533,33 +615,45 @@ def save(self):
if self.has_whonix:
raw_rules.append(
- self.policy_manager.new_rule(service=self.service_name,
- source="@tag:whonix-updatevm", target="@default",
+ self.policy_manager.new_rule(
+ service=self.service_name,
+ source="@tag:whonix-updatevm",
+ target="@default",
action="allow "
- f"target={self.whonix_updatevm_model.get_selected()}"))
+ f"target={self.whonix_updatevm_model.get_selected()}",
+ )
+ )
new_update_proxies.add(self.whonix_updatevm_model.get_selected())
if self.updatevm_model.get_selected():
raw_rules.append(
- self.policy_manager.new_rule(service=self.service_name,
- source="@type:TemplateVM", target="@default",
+ self.policy_manager.new_rule(
+ service=self.service_name,
+ source="@type:TemplateVM",
+ target="@default",
action="allow "
- f"target={self.updatevm_model.get_selected()}"))
+ f"target={self.updatevm_model.get_selected()}",
+ )
+ )
new_update_proxies.add(self.updatevm_model.get_selected())
- self.policy_manager.save_rules(self.policy_file_name,
- raw_rules, self.current_token)
+ self.policy_manager.save_rules(
+ self.policy_file_name, raw_rules, self.current_token
+ )
_r, self.current_token = self.policy_manager.get_rules_from_filename(
- self.policy_file_name, "")
+ self.policy_file_name, ""
+ )
self.rules = self.current_exception_rules
for vm in self.qapp.domains:
- if 'service.qubes-updates-proxy' in vm.features:
- apply_feature_change(vm, 'service.qubes-updates-proxy',
- True if vm in new_update_proxies else None)
+ if "service.qubes-updates-proxy" in vm.features:
+ apply_feature_change(
+ vm,
+ "service.qubes-updates-proxy",
+ True if vm in new_update_proxies else None,
+ )
elif vm in new_update_proxies:
- apply_feature_change(vm, 'service.qubes-updates-proxy',
- True)
+ apply_feature_change(vm, "service.qubes-updates-proxy", True)
def close_all_edits(self):
"""Close all edited rows."""
@@ -568,11 +662,13 @@ def close_all_edits(self):
class UpdatesHandler(PageHandler):
"""Handler for all the disparate Updates functions."""
- def __init__(self,
- qapp: qubesadmin.Qubes,
- policy_manager: PolicyManager,
- gtk_builder: Gtk.Builder
- ):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ policy_manager: PolicyManager,
+ gtk_builder: Gtk.Builder,
+ ):
"""
:param qapp: Qubes object
:param policy_manager: PolicyManager object
@@ -580,39 +676,46 @@ def __init__(self,
self.qapp = qapp
self.policy_manager = policy_manager
- self.service_name = 'qubes.UpdatesProxy'
- self.policy_file_name = '50-config-updates'
-
- self.dom0_updatevm_combo: Gtk.ComboBox = \
- gtk_builder.get_object('updates_dom0_updatevm_combo')
+ self.service_name = "qubes.UpdatesProxy"
+ self.policy_file_name = "50-config-updates"
+ self.dom0_updatevm_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "updates_dom0_updatevm_combo"
+ )
self.dom0_updatevm_model = VMListModeler(
combobox=self.dom0_updatevm_combo,
qapp=self.qapp,
filter_function=(
- lambda vm: vm.klass != 'TemplateVM' and vm.klass != 'AdminVM'
- and vm.is_networked()),
+ lambda vm: vm.klass != "TemplateVM"
+ and vm.klass != "AdminVM"
+ and vm.is_networked()
+ ),
current_value=self.qapp.updatevm,
additional_options=NONE_CATEGORY,
- style_changes=True
+ style_changes=True,
)
# repo handler
self.repo_handler = RepoHandler(gtk_builder=gtk_builder)
- self.update_checker = UpdateCheckerHandler(gtk_builder=gtk_builder,
- qapp=self.qapp)
- self.update_proxy = UpdateProxy(gtk_builder=gtk_builder, qapp=self.qapp,
- policy_manager=policy_manager,
- policy_file_name=self.policy_file_name,
- service_name=self.service_name)
+ self.update_checker = UpdateCheckerHandler(
+ gtk_builder=gtk_builder, qapp=self.qapp
+ )
+ self.update_proxy = UpdateProxy(
+ gtk_builder=gtk_builder,
+ qapp=self.qapp,
+ policy_manager=policy_manager,
+ policy_file_name=self.policy_file_name,
+ service_name=self.service_name,
+ )
self.conflict_handler = ConflictFileHandler(
- gtk_builder=gtk_builder, prefix="updates",
+ gtk_builder=gtk_builder,
+ prefix="updates",
service_names=[self.service_name],
own_file_name=self.policy_file_name,
- policy_manager=self.policy_manager)
-
+ policy_manager=self.policy_manager,
+ )
def close_all_edits(self):
"""Close all edited rows"""
@@ -622,8 +725,10 @@ def get_unsaved(self) -> str:
"""Get list of unsaved changes."""
self.close_all_edits()
- unsaved = [self.repo_handler.get_unsaved(),
- self.update_checker.get_unsaved()]
+ unsaved = [
+ self.repo_handler.get_unsaved(),
+ self.update_checker.get_unsaved(),
+ ]
if self.dom0_updatevm_model.is_changed():
unsaved.append(_("dom0 Update Proxy"))
@@ -632,7 +737,6 @@ def get_unsaved(self) -> str:
unsaved = [x for x in unsaved if x]
return "\n".join(unsaved)
-
def reset(self):
"""Reset state to initial or last saved state, whichever is newer."""
self.dom0_updatevm_model.reset()
@@ -644,8 +748,11 @@ def save(self):
"""Save current rules, whatever they are - custom or default.
Return True if successful, False otherwise"""
- for handler in [self.repo_handler, self.update_checker,
- self.update_proxy]:
+ for handler in [
+ self.repo_handler,
+ self.update_checker,
+ self.update_proxy,
+ ]:
handler.save() # type: ignore
if self.dom0_updatevm_model.is_changed():
diff --git a/qubes_config/global_config/usb_devices.py b/qubes_config/global_config/usb_devices.py
index 57c1722a..db3d834f 100644
--- a/qubes_config/global_config/usb_devices.py
+++ b/qubes_config/global_config/usb_devices.py
@@ -42,18 +42,21 @@
import qubesadmin.vm
import qubesadmin.exc
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
class InputActionWidget(Gtk.Box):
"""A simple widget for a combobox for policy actions."""
- def __init__(self, rule: RuleTargetedAdminVM,
- action_choices: Dict[str, str]):
+
+ def __init__(
+ self, rule: RuleTargetedAdminVM, action_choices: Dict[str, str]
+ ):
"""
:param rule: wrapped policy rule
:param action_choices: Dictionary of "nice rule name": "actual action"
@@ -66,7 +69,8 @@ def __init__(self, rule: RuleTargetedAdminVM,
combobox=self.combobox,
values=action_choices,
selected_value=self.rule.action,
- style_changes=True)
+ style_changes=True,
+ )
self.add(self.combobox)
self.show_all()
@@ -85,29 +89,31 @@ def update_changed(self):
class InputDeviceHandler:
"""Handler for various qubes.Input policies."""
+
ACTION_CHOICES = {
_("always ask"): "ask",
_("enable"): "allow",
- _("disable"): "deny"
+ _("disable"): "deny",
}
- def __init__(self,
- qapp: qubesadmin.Qubes,
- policy_manager: PolicyManager,
- gtk_builder: Gtk.Builder,
- usb_qubes: Set[qubesadmin.vm.QubesVM]
- ):
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ policy_manager: PolicyManager,
+ gtk_builder: Gtk.Builder,
+ usb_qubes: Set[qubesadmin.vm.QubesVM],
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
- self.policy_file_name = '50-config-input'
+ self.policy_file_name = "50-config-input"
- self.warn_box = gtk_builder.get_object('usb_input_problem_box_warn')
+ self.warn_box = gtk_builder.get_object("usb_input_problem_box_warn")
self.warn_label: Gtk.Label = gtk_builder.get_object(
- 'usb_input_problem_warn_label')
+ "usb_input_problem_warn_label"
+ )
self.warn_box.set_visible(False)
- self.policy_grid: Gtk.Grid = \
- gtk_builder.get_object('usb_input_grid')
+ self.policy_grid: Gtk.Grid = gtk_builder.get_object("usb_input_grid")
self.default_policy = ""
@@ -119,25 +125,31 @@ def __init__(self,
# widgets indexed via tuples: service_name, usb-qube-name
self.widgets: Dict[Tuple[str, str], InputActionWidget] = {}
- self.policy_order = ['qubes.InputKeyboard',
- 'qubes.InputMouse',
- 'qubes.InputTablet']
+ self.policy_order = [
+ "qubes.InputKeyboard",
+ "qubes.InputMouse",
+ "qubes.InputTablet",
+ ]
self.load_rules()
self.conflict_file_handler = ConflictFileHandler(
- gtk_builder=gtk_builder, prefix="usb_input",
+ gtk_builder=gtk_builder,
+ prefix="usb_input",
service_names=self.policy_order,
own_file_name=self.policy_file_name,
- policy_manager=self.policy_manager)
+ policy_manager=self.policy_manager,
+ )
def load_rules(self):
self.default_policy = ""
if not self.usb_qubes:
- self._warn('No USB qubes found: to apply policy to USB input '
- 'devices, connect your USB controller to a '
- 'dedicated USB qube.')
+ self._warn(
+ "No USB qubes found: to apply policy to USB input "
+ "devices, connect your USB controller to a "
+ "dedicated USB qube."
+ )
return
for vm in self.usb_qubes:
@@ -147,47 +159,59 @@ def load_rules(self):
qubes.InputTablet * {vm.name} @adminvm deny"""
for col_num, vm in enumerate(self.usb_qubes):
- self.policy_grid.attach(TokenName(vm.name, self.qapp),
- 1 + col_num, 0, 1, 1)
+ self.policy_grid.attach(
+ TokenName(vm.name, self.qapp), 1 + col_num, 0, 1, 1
+ )
- self.rules, self.current_token = \
+ self.rules, self.current_token = (
self.policy_manager.get_rules_from_filename(
- self.policy_file_name, self.default_policy)
+ self.policy_file_name, self.default_policy
+ )
+ )
for rule in self.rules:
try:
wrapped_rule = RuleTargetedAdminVM(rule)
except ValueError:
- self._warn('Unexpected policy rule: ' + str(rule))
+ self._warn("Unexpected policy rule: " + str(rule))
continue
if wrapped_rule.source not in self.usb_qubes:
# non-fatal
- self._warn('Unexpected policy rule: ' + str(rule))
+ self._warn("Unexpected policy rule: " + str(rule))
designation = (rule.service, str(wrapped_rule.source))
if designation in self.widgets:
- self._warn('Unexpected policy rules')
+ self._warn("Unexpected policy rules")
continue
- self.widgets[designation] = InputActionWidget(wrapped_rule,
- self.ACTION_CHOICES)
+ self.widgets[designation] = InputActionWidget(
+ wrapped_rule, self.ACTION_CHOICES
+ )
for service in self.policy_order:
for vm in self.usb_qubes:
if (service, vm.name) not in self.widgets:
rule = self.policy_manager.text_to_rules(
- f"{service} * {vm.name} @adminvm deny")[0]
+ f"{service} * {vm.name} @adminvm deny"
+ )[0]
self.widgets[(service, vm.name)] = InputActionWidget(
- RuleTargetedAdminVM(rule), self.ACTION_CHOICES)
+ RuleTargetedAdminVM(rule), self.ACTION_CHOICES
+ )
for row_num, service in enumerate(self.policy_order):
for col_num, vm in enumerate(self.usb_qubes):
- self.policy_grid.attach(self.widgets[(service, vm.name)],
- 1 + col_num, 1 + row_num, 1, 1)
+ self.policy_grid.attach(
+ self.widgets[(service, vm.name)],
+ 1 + col_num,
+ 1 + row_num,
+ 1,
+ 1,
+ )
self.policy_grid.show_all()
def _warn(self, error_descr: str):
self.warn_label.set_text(
- self.warn_label.get_text() + '\n' + error_descr)
+ self.warn_label.get_text() + "\n" + error_descr
+ )
self.warn_box.set_visible(True)
def save(self):
@@ -197,14 +221,15 @@ def save(self):
# no point in changing anything, there are no USB qubes
return
for widget in self.widgets.values():
- widget.rule.action = \
- widget.model.get_selected()
+ widget.rule.action = widget.model.get_selected()
rules.append(widget.rule.raw_rule)
- self.policy_manager.save_rules(self.policy_file_name, rules,
- self.current_token)
+ self.policy_manager.save_rules(
+ self.policy_file_name, rules, self.current_token
+ )
_r, self.current_token = self.policy_manager.get_rules_from_filename(
- self.policy_file_name, self.default_policy)
+ self.policy_file_name, self.default_policy
+ )
for widget in self.widgets.values():
widget.update_changed()
@@ -215,34 +240,36 @@ def get_unsaved(self) -> str:
unsaved = []
for widget in self.widgets.values():
if widget.is_changed():
- name = widget.service[len('qubes.Input'):]
- unsaved.append(_('{name} input settings').format(name=name))
+ name = widget.service[len("qubes.Input") :]
+ unsaved.append(_("{name} input settings").format(name=name))
return "\n".join(unsaved)
def reset(self):
"""Reset changes to the initial state."""
for widget in self.widgets.values():
widget.reset()
- self.warn_label.set_text('Unexpected policy file contents:')
+ self.warn_label.set_text("Unexpected policy file contents:")
class U2FPolicyHandler:
"""Handler for u2f policy and services."""
- SERVICE_FEATURE = 'service.qubes-u2f-proxy'
- SUPPORTED_SERVICE_FEATURE = 'supported-service.qubes-u2f-proxy'
- AUTH_POLICY = 'u2f.Authenticate'
- REGISTER_POLICY = 'u2f.Register'
- POLICY_REGISTER_POLICY = 'policy.RegisterArgument'
-
- def __init__(self,
- qapp: qubesadmin.Qubes,
- policy_manager: PolicyManager,
- gtk_builder: Gtk.Builder,
- usb_qubes: Set[qubesadmin.vm.QubesVM]
- ):
+
+ SERVICE_FEATURE = "service.qubes-u2f-proxy"
+ SUPPORTED_SERVICE_FEATURE = "supported-service.qubes-u2f-proxy"
+ AUTH_POLICY = "u2f.Authenticate"
+ REGISTER_POLICY = "u2f.Register"
+ POLICY_REGISTER_POLICY = "policy.RegisterArgument"
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ policy_manager: PolicyManager,
+ gtk_builder: Gtk.Builder,
+ usb_qubes: Set[qubesadmin.vm.QubesVM],
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
- self.policy_filename = '50-config-u2f'
+ self.policy_filename = "50-config-u2f"
self.usb_qubes = usb_qubes
self.default_policy = ""
@@ -252,30 +279,40 @@ def __init__(self,
policy.RegisterArgument +u2f.Register @anyvm @anyvm deny
"""
- self.problem_fatal_box: Gtk.Box = \
- gtk_builder.get_object('usb_u2f_fatal_problem')
- self.problem_fatal_label: Gtk.Label = \
- gtk_builder.get_object('usb_u2f_fatal_problem_label')
+ self.problem_fatal_box: Gtk.Box = gtk_builder.get_object(
+ "usb_u2f_fatal_problem"
+ )
+ self.problem_fatal_label: Gtk.Label = gtk_builder.get_object(
+ "usb_u2f_fatal_problem_label"
+ )
- self.enable_check: Gtk.CheckButton = \
- gtk_builder.get_object('usb_u2f_enable_check') # general enable
- self.box: Gtk.Box = \
- gtk_builder.get_object('usb_u2f_enable_box') # general box
+ self.enable_check: Gtk.CheckButton = gtk_builder.get_object(
+ "usb_u2f_enable_check"
+ ) # general enable
+ self.box: Gtk.Box = gtk_builder.get_object(
+ "usb_u2f_enable_box"
+ ) # general box
- self.register_check: Gtk.CheckButton = \
- gtk_builder.get_object('usb_u2f_register_check')
- self.register_box: Gtk.Box = \
- gtk_builder.get_object('usb_u2f_register_box')
- self.register_all_radio: Gtk.RadioButton = \
- gtk_builder.get_object('usb_u2f_register_all_radio')
- self.register_some_radio: Gtk.RadioButton = \
- gtk_builder.get_object('usb_u2f_register_some_radio')
+ self.register_check: Gtk.CheckButton = gtk_builder.get_object(
+ "usb_u2f_register_check"
+ )
+ self.register_box: Gtk.Box = gtk_builder.get_object(
+ "usb_u2f_register_box"
+ )
+ self.register_all_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "usb_u2f_register_all_radio"
+ )
+ self.register_some_radio: Gtk.RadioButton = gtk_builder.get_object(
+ "usb_u2f_register_some_radio"
+ )
- self.blanket_check: Gtk.CheckButton = \
- gtk_builder.get_object('usb_u2f_blanket_check')
+ self.blanket_check: Gtk.CheckButton = gtk_builder.get_object(
+ "usb_u2f_blanket_check"
+ )
self.usb_qube_combo: Gtk.ComboBox = gtk_builder.get_object(
- 'u2f_usb_combo')
+ "u2f_usb_combo"
+ )
self.initially_enabled_vms: List[qubesadmin.vm.QubesVM] = []
self.available_vms: List[qubesadmin.vm.QubesVM] = []
@@ -286,63 +323,85 @@ def __init__(self,
self.rules: List[Rule] = []
self.usb_qube_model: Optional[VMListModeler] = None
- self.error_handler = ErrorHandler(gtk_builder, 'usb_u2f')
+ self.error_handler = ErrorHandler(gtk_builder, "usb_u2f")
self._initialize_data()
self.enable_some_handler = VMFlowboxHandler(
- gtk_builder, self.qapp, "usb_u2f_enable_some",
- self.initially_enabled_vms, lambda vm: vm in self.available_vms)
+ gtk_builder,
+ self.qapp,
+ "usb_u2f_enable_some",
+ self.initially_enabled_vms,
+ lambda vm: vm in self.available_vms,
+ )
self.register_some_handler = VMFlowboxHandler(
- gtk_builder, self.qapp, "usb_u2f_register_some",
- self.initial_register_vms, lambda vm: vm in self.available_vms,
- verification_callback=self._verify_additional_vm)
+ gtk_builder,
+ self.qapp,
+ "usb_u2f_register_some",
+ self.initial_register_vms,
+ lambda vm: vm in self.available_vms,
+ verification_callback=self._verify_additional_vm,
+ )
self.blanket_handler = VMFlowboxHandler(
- gtk_builder, self.qapp, "usb_u2f_blanket",
- self.initial_blanket_vms, lambda vm: vm in self.available_vms,
- verification_callback=self._verify_additional_vm)
+ gtk_builder,
+ self.qapp,
+ "usb_u2f_blanket",
+ self.initial_blanket_vms,
+ lambda vm: vm in self.available_vms,
+ verification_callback=self._verify_additional_vm,
+ )
self.widget_to_box = {
self.enable_check: self.box,
self.register_check: self.register_box,
self.blanket_check: self.blanket_handler,
- self.register_some_radio: self.register_some_handler}
+ self.register_some_radio: self.register_some_handler,
+ }
for widget, box in self.widget_to_box.items():
- widget.connect('toggled', partial(self._enable_clicked, box))
+ widget.connect("toggled", partial(self._enable_clicked, box))
self._enable_clicked(box, widget)
self.initial_enable_state: bool = self.enable_check.get_active()
self.initial_register_state: bool = self.register_check.get_active()
- self.initial_register_all_state: bool = \
+ self.initial_register_all_state: bool = (
self.register_all_radio.get_active()
+ )
self.initial_blanket_check_state: bool = self.blanket_check.get_active()
self.conflict_file_handler = ConflictFileHandler(
- gtk_builder=gtk_builder, prefix="usb_u2f",
- service_names=[self.REGISTER_POLICY,
- self.POLICY_REGISTER_POLICY, self.AUTH_POLICY],
+ gtk_builder=gtk_builder,
+ prefix="usb_u2f",
+ service_names=[
+ self.REGISTER_POLICY,
+ self.POLICY_REGISTER_POLICY,
+ self.AUTH_POLICY,
+ ],
own_file_name=self.policy_filename,
- policy_manager=self.policy_manager)
+ policy_manager=self.policy_manager,
+ )
if self.usb_qube_model:
self.usb_qube_model.connect_change_callback(
- self.load_rules_for_usb_qube)
+ self.load_rules_for_usb_qube
+ )
@staticmethod
- def _enable_clicked(related_box: Union[Gtk.Box, VMFlowboxHandler],
- widget: Gtk.CheckButton):
+ def _enable_clicked(
+ related_box: Union[Gtk.Box, VMFlowboxHandler], widget: Gtk.CheckButton
+ ):
related_box.set_visible(widget.get_active())
def _verify_additional_vm(self, vm):
if vm in self.enable_some_handler.selected_vms:
return True
- response = ask_question(self.enable_check,
- _("U2F not enabled in qube"),
- _("U2F is not enabled in this qube. Do you "
- "want to enable it?"))
+ response = ask_question(
+ self.enable_check,
+ _("U2F not enabled in qube"),
+ _("U2F is not enabled in this qube. Do you want to enable it?"),
+ )
if response == Gtk.ResponseType.YES:
self.enable_some_handler.add_selected_vm(vm)
return True
@@ -352,19 +411,24 @@ def _initialize_data(self):
self.problem_fatal_box.set_visible(False)
# guess at the current sys-usb
- self.rules, self.current_token = \
+ self.rules, self.current_token = (
self.policy_manager.get_rules_from_filename(
- self.policy_filename, self.default_policy)
+ self.policy_filename, self.default_policy
+ )
+ )
if not self.usb_qubes:
- self.disable_u2f("No USB qubes found. To use U2F Proxy, you have to"
- "connect your USB controller to a qube.")
+ self.disable_u2f(
+ "No USB qubes found. To use U2F Proxy, you have to"
+ "connect your USB controller to a qube."
+ )
return
usb_qube_candidates = set()
for qube in self.usb_qubes:
if qube.features.check_with_template(
- self.SUPPORTED_SERVICE_FEATURE):
+ self.SUPPORTED_SERVICE_FEATURE
+ ):
usb_qube_candidates.add(qube)
self.usb_qubes = usb_qube_candidates
@@ -373,7 +437,7 @@ def _initialize_data(self):
combobox=self.usb_qube_combo,
qapp=self.qapp,
filter_function=lambda vm: vm in self.usb_qubes,
- style_changes=True
+ style_changes=True,
)
if not usb_qube_candidates:
@@ -381,7 +445,8 @@ def _initialize_data(self):
"The Qubes U2F Proxy service is not installed in the USB qube. "
"If you wish to use this service, install the "
"qubes-u2f package in the template on which "
- "the USB qube is based.")
+ "the USB qube is based."
+ )
return
policy_candidates = set()
@@ -437,21 +502,22 @@ def load_rules_for_usb_qube(self):
self.disable_u2f(
"No qubes with the U2F Proxy service found. If you wish to "
"use this service, install the qubes-u2f package in "
- "the template on which the USB qube is based.")
+ "the template on which the USB qube is based."
+ )
return
self.enable_check.set_active(bool(self.initially_enabled_vms))
for rule in self.rules:
- if rule.target not in [str(usb_qube), '@anyvm']:
+ if rule.target not in [str(usb_qube), "@anyvm"]:
self.error_handler.add_error(rule)
continue
if rule.service == self.REGISTER_POLICY:
if rule.argument is not None:
self.error_handler.add_error(rule)
continue
- if rule.source == '@anyvm' and isinstance(rule.action, Allow):
+ if rule.source == "@anyvm" and isinstance(rule.action, Allow):
self.allow_all_register = True
- elif rule.source != '@anyvm' and isinstance(rule.action, Allow):
+ elif rule.source != "@anyvm" and isinstance(rule.action, Allow):
try:
vm = self.qapp.domains[rule.source]
self.initial_register_vms.append(vm)
@@ -462,7 +528,7 @@ def load_rules_for_usb_qube(self):
if rule.argument is not None:
self.error_handler.add_error(rule)
continue
- if rule.source != '@anyvm' and isinstance(rule.action, Allow):
+ if rule.source != "@anyvm" and isinstance(rule.action, Allow):
try:
vm = self.qapp.domains[rule.source]
self.initial_blanket_vms.append(vm)
@@ -509,18 +575,23 @@ def save(self):
self.policy_manager.save_rules(
self.policy_filename,
self.policy_manager.text_to_rules(self.deny_all_policy),
- self.current_token)
+ self.current_token,
+ )
self._initialize_data()
return
enabled_vms = self.enable_some_handler.selected_vms
if not enabled_vms:
- show_error(self.box.get_toplevel(),
- _("Incorrect configuration found"),
- _("U2F is enabled, but not qubes are selected to be "
- "used with U2F. This is equivalent to disabling U2F "
- "and will be treated as such."))
+ show_error(
+ self.box.get_toplevel(),
+ _("Incorrect configuration found"),
+ _(
+ "U2F is enabled, but not qubes are selected to be "
+ "used with U2F. This is equivalent to disabling U2F "
+ "and will be treated as such."
+ ),
+ )
for vm in self.available_vms:
value = None if vm not in enabled_vms else True
@@ -535,45 +606,76 @@ def save(self):
# register rules
if not self.register_check.get_active():
- rules.append(self.policy_manager.new_rule(
- service=self.REGISTER_POLICY, source="@anyvm",
- target="@anyvm", action="deny"))
- rules.append(self.policy_manager.new_rule(
- service=self.POLICY_REGISTER_POLICY,
- argument=f"+{self.AUTH_POLICY}", source="@anyvm",
- target="@anyvm", action="deny"))
- else:
- if self.register_all_radio.get_active():
- rules.append(self.policy_manager.new_rule(
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.REGISTER_POLICY,
+ source="@anyvm",
+ target="@anyvm",
+ action="deny",
+ )
+ )
+ rules.append(
+ self.policy_manager.new_rule(
service=self.POLICY_REGISTER_POLICY,
argument=f"+{self.AUTH_POLICY}",
- source=str(sys_usb),
- target="@anyvm", action="allow target=dom0"))
- rules.append(self.policy_manager.new_rule(
- service=self.REGISTER_POLICY,
source="@anyvm",
- target=str(sys_usb), action="allow"))
+ target="@anyvm",
+ action="deny",
+ )
+ )
+ else:
+ if self.register_all_radio.get_active():
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.POLICY_REGISTER_POLICY,
+ argument=f"+{self.AUTH_POLICY}",
+ source=str(sys_usb),
+ target="@anyvm",
+ action="allow target=dom0",
+ )
+ )
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.REGISTER_POLICY,
+ source="@anyvm",
+ target=str(sys_usb),
+ action="allow",
+ )
+ )
else:
for vm in self.register_some_handler.selected_vms:
- rules.append(self.policy_manager.new_rule(
- service=self.REGISTER_POLICY,
- source=str(vm),
- target=str(sys_usb), action="allow"))
- rules.append(self.policy_manager.new_rule(
- service=self.POLICY_REGISTER_POLICY,
- argument=f"+{self.AUTH_POLICY}",
- source=str(sys_usb),
- target="@anyvm", action="allow target=dom0"))
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.REGISTER_POLICY,
+ source=str(vm),
+ target=str(sys_usb),
+ action="allow",
+ )
+ )
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.POLICY_REGISTER_POLICY,
+ argument=f"+{self.AUTH_POLICY}",
+ source=str(sys_usb),
+ target="@anyvm",
+ action="allow target=dom0",
+ )
+ )
if self.blanket_check.get_active():
for vm in self.blanket_handler.selected_vms:
- rules.append(self.policy_manager.new_rule(
- service=self.AUTH_POLICY,
- source=str(vm),
- target=str(sys_usb), action="allow"))
+ rules.append(
+ self.policy_manager.new_rule(
+ service=self.AUTH_POLICY,
+ source=str(vm),
+ target=str(sys_usb),
+ action="allow",
+ )
+ )
- self.policy_manager.save_rules(self.policy_filename, rules,
- self.current_token)
+ self.policy_manager.save_rules(
+ self.policy_filename, rules, self.current_token
+ )
self._initialize_data()
def reset(self):
@@ -608,52 +710,63 @@ def get_unsaved(self) -> str:
if self.initial_register_state != self.register_check.get_active():
unsaved.append(_("U2F key registration settings changed"))
- elif self.initial_register_all_state != \
- self.register_all_radio.get_active():
+ elif (
+ self.initial_register_all_state
+ != self.register_all_radio.get_active()
+ ):
unsaved.append(_("U2F key registration settings changed"))
- elif self.register_some_handler.selected_vms != \
- self.initial_register_vms:
+ elif (
+ self.register_some_handler.selected_vms != self.initial_register_vms
+ ):
unsaved.append(_("U2F key registration settings changed"))
- if self.initial_blanket_check_state != \
- self.blanket_check.get_active() or \
- self.blanket_handler.selected_vms != self.initial_blanket_vms:
- unsaved.append(_("List of qubes with unrestricted U2F key "
- "access changed"))
+ if (
+ self.initial_blanket_check_state != self.blanket_check.get_active()
+ or self.blanket_handler.selected_vms != self.initial_blanket_vms
+ ):
+ unsaved.append(
+ _("List of qubes with unrestricted U2F key access changed")
+ )
return "\n".join(unsaved)
class DevicesHandler(PageHandler):
"""Handler for all the disparate Updates functions."""
- def __init__(self,
- qapp: qubesadmin.Qubes,
- policy_manager: PolicyManager,
- gtk_builder: Gtk.Builder
- ):
+
+ def __init__(
+ self,
+ qapp: qubesadmin.Qubes,
+ policy_manager: PolicyManager,
+ gtk_builder: Gtk.Builder,
+ ):
self.qapp = qapp
self.policy_manager = policy_manager
- self.main_window = gtk_builder.get_object('main_window')
+ self.main_window = gtk_builder.get_object("main_window")
usb_qubes: Set[qubesadmin.vm.QubesVM] = set()
for vm in self.qapp.domains:
- for assignment in vm.devices['pci'].get_attached_devices():
+ for assignment in vm.devices["pci"].get_attached_devices():
cats = [infc.category for infc in assignment.device.interfaces]
if DeviceCategory.PCI_USB in cats:
usb_qubes.add(vm)
self.input_handler = InputDeviceHandler(
- qapp, policy_manager, gtk_builder, usb_qubes)
+ qapp, policy_manager, gtk_builder, usb_qubes
+ )
- self.u2f_handler = U2FPolicyHandler(self.qapp, self.policy_manager,
- gtk_builder, usb_qubes)
+ self.u2f_handler = U2FPolicyHandler(
+ self.qapp, self.policy_manager, gtk_builder, usb_qubes
+ )
def get_unsaved(self) -> str:
"""Get human-readable description of unsaved changes, or
empty string if none were found."""
- unsaved = [self.input_handler.get_unsaved(),
- self.u2f_handler.get_unsaved()]
+ unsaved = [
+ self.input_handler.get_unsaved(),
+ self.u2f_handler.get_unsaved(),
+ ]
return "\n".join([x for x in unsaved if x])
def reset(self):
diff --git a/qubes_config/global_config/vm_flowbox.py b/qubes_config/global_config/vm_flowbox.py
index 38bf417a..1f8328c5 100644
--- a/qubes_config/global_config/vm_flowbox.py
+++ b/qubes_config/global_config/vm_flowbox.py
@@ -31,20 +31,23 @@
import qubesadmin.vm
import qubesadmin.exc
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
+
class PlaceholderText(Gtk.FlowBoxChild):
"""Placeholder to be shown if no qubes are selected"""
+
def __init__(self):
super().__init__()
self.label = Gtk.Label()
- self.label.set_text(_('No qubes selected'))
- self.label.get_style_context().add_class('didascalia')
+ self.label.set_text(_("No qubes selected"))
+ self.label.get_style_context().add_class("didascalia")
self.add(self.label)
self.show_all()
@@ -54,35 +57,35 @@ def __str__(self): # pylint: disable=arguments-differ
class VMFlowBoxButton(Gtk.FlowBoxChild):
"""Simple button representing a VM that can be deleted."""
+
def __init__(self, vm: qubesadmin.vm.QubesVM):
super().__init__()
self.vm = vm
token_widget = QubeName(vm)
button = Gtk.Button()
- button.get_style_context().add_class('flat')
+ button.get_style_context().add_class("flat")
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
box.pack_start(token_widget, False, False, 0)
remove_icon = Gtk.Image()
- remove_icon.set_from_pixbuf(load_icon('qubes-delete', 14, 14))
+ remove_icon.set_from_pixbuf(load_icon("qubes-delete", 14, 14))
box.pack_start(remove_icon, False, False, 10)
button.add(box)
- button.connect('clicked', self._remove_self)
+ button.connect("clicked", self._remove_self)
self.add(button)
self.show_all()
-
def _remove_self(self, *_args):
response = ask_question(
- self, _("Delete"),
- _("Are you sure you want to remove this qube?"))
+ self, _("Delete"), _("Are you sure you want to remove this qube?")
+ )
if response == Gtk.ResponseType.NO:
return
parent = self.get_parent()
parent.remove(self)
- parent.emit('child-removed', None)
+ parent.emit("child-removed", None)
def __str__(self): # pylint: disable=arguments-differ
return str(self.vm)
@@ -99,11 +102,18 @@ class VMFlowboxHandler:
- {prefix}_add_confirm - confirm adding a new qube button
- {prefix}_add_button - add new qube button
"""
- def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
- prefix: str, initial_vms: List[qubesadmin.vm.QubesVM],
- filter_function: Optional[Callable] = None,
- verification_callback:
- Optional[Callable[[qubesadmin.vm.QubesVM], bool]] = None):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ qapp: qubesadmin.Qubes,
+ prefix: str,
+ initial_vms: List[qubesadmin.vm.QubesVM],
+ filter_function: Optional[Callable] = None,
+ verification_callback: Optional[
+ Callable[[qubesadmin.vm.QubesVM], bool]
+ ] = None,
+ ):
"""
:param gtk_builder: Gtk.Builder
:param qapp: qubesadmin.Qubes
@@ -117,27 +127,29 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
self.qapp = qapp
self.verification_callback = verification_callback
- self.flowbox: Gtk.FlowBox = \
- gtk_builder.get_object(f'{prefix}_flowbox')
- self.box: Gtk.Box = \
- gtk_builder.get_object(f'{prefix}_box')
- self.add_box: Gtk.Box = \
- gtk_builder.get_object(f'{prefix}_add_box')
+ self.flowbox: Gtk.FlowBox = gtk_builder.get_object(f"{prefix}_flowbox")
+ self.box: Gtk.Box = gtk_builder.get_object(f"{prefix}_box")
+ self.add_box: Gtk.Box = gtk_builder.get_object(f"{prefix}_add_box")
- self.qube_combo: Gtk.ComboBox = \
- gtk_builder.get_object(f'{prefix}_qube_combo')
+ self.qube_combo: Gtk.ComboBox = gtk_builder.get_object(
+ f"{prefix}_qube_combo"
+ )
- self.add_cancel: Gtk.Button = \
- gtk_builder.get_object(f'{prefix}_add_cancel')
- self.add_confirm: Gtk.Button = \
- gtk_builder.get_object(f'{prefix}_add_confirm')
- self.add_button: Gtk.Button = \
- gtk_builder.get_object(f'{prefix}_add_button')
+ self.add_cancel: Gtk.Button = gtk_builder.get_object(
+ f"{prefix}_add_cancel"
+ )
+ self.add_confirm: Gtk.Button = gtk_builder.get_object(
+ f"{prefix}_add_confirm"
+ )
+ self.add_button: Gtk.Button = gtk_builder.get_object(
+ f"{prefix}_add_button"
+ )
self.add_qube_model = VMListModeler(
combobox=self.qube_combo,
qapp=self.qapp,
- filter_function=filter_function)
+ filter_function=filter_function,
+ )
self.flowbox.set_sort_func(self._sort_flowbox)
self.placeholder = PlaceholderText()
@@ -150,14 +162,10 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes,
self.placeholder.set_visible(not bool(self._initial_vms))
self.add_box.set_visible(False)
- self.add_button.connect('clicked',
- self._add_button_clicked)
- self.add_cancel.connect('clicked',
- self._add_cancel_clicked)
- self.add_confirm.connect('clicked',
- self._add_confirm_clicked)
- self.flowbox.connect('child-removed',
- self._vm_removed)
+ self.add_button.connect("clicked", self._add_button_clicked)
+ self.add_cancel.connect("clicked", self._add_cancel_clicked)
+ self.add_confirm.connect("clicked", self._add_confirm_clicked)
+ self.flowbox.connect("child-removed", self._vm_removed)
@staticmethod
def _sort_flowbox(child_1, child_2):
@@ -179,8 +187,11 @@ def _add_confirm_clicked(self, _widget):
if not self.verification_callback(select_vm):
return
if select_vm in self.selected_vms:
- show_error(self.flowbox, _("Cannot add qube"),
- _("This qube is already selected."))
+ show_error(
+ self.flowbox,
+ _("Cannot add qube"),
+ _("This qube is already selected."),
+ )
return
self.flowbox.add(VMFlowBoxButton(select_vm))
self.placeholder.set_visible(False)
diff --git a/qubes_config/new_qube/advanced_handler.py b/qubes_config/new_qube/advanced_handler.py
index a30c29db..06c4d24a 100644
--- a/qubes_config/new_qube/advanced_handler.py
+++ b/qubes_config/new_qube/advanced_handler.py
@@ -26,20 +26,23 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-logger = logging.getLogger('qubes-new-qube')
-WHONIX_QUBE_NAME = 'sys-whonix'
+logger = logging.getLogger("qubes-new-qube")
+WHONIX_QUBE_NAME = "sys-whonix"
+
class AdvancedHandler:
"""
Class that handles advanced configuration.
"""
+
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
"""
:param gtk_builder: Gtk.Builder object
@@ -47,43 +50,49 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
"""
self.qapp = qapp
- self.events: Gtk.Button = \
- gtk_builder.get_object('event_button_advanced')
- self.box: Gtk.Box = \
- gtk_builder.get_object('advanced_box')
- self.expander_icon: Gtk.Image = \
- gtk_builder.get_object('advanced_expander')
- self.expander_label: Gtk.Label = \
- gtk_builder.get_object('advanced_label')
+ self.events: Gtk.Button = gtk_builder.get_object(
+ "event_button_advanced"
+ )
+ self.box: Gtk.Box = gtk_builder.get_object("advanced_box")
+ self.expander_icon: Gtk.Image = gtk_builder.get_object(
+ "advanced_expander"
+ )
+ self.expander_label: Gtk.Label = gtk_builder.get_object(
+ "advanced_label"
+ )
self.expander_handler = ExpanderHandler(
event_button=self.events,
data_container=self.box,
icon=self.expander_icon,
label=self.expander_label,
- text_shown=_('Hide advanced settings'),
- text_hidden=_('Show advanced settings')
+ text_shown=_("Hide advanced settings"),
+ text_hidden=_("Show advanced settings"),
)
- self.main_window: Gtk.Window = gtk_builder.get_object('main_window')
+ self.main_window: Gtk.Window = gtk_builder.get_object("main_window")
- self.provides_network_check: Gtk.CheckButton = \
- gtk_builder.get_object('advanced_provides_network')
- self.install_system_check: Gtk.CheckButton = \
- gtk_builder.get_object('advanced_install_from_device')
- self.launch_settings_check: Gtk.CheckButton = \
- gtk_builder.get_object('check_launch_settings')
+ self.provides_network_check: Gtk.CheckButton = gtk_builder.get_object(
+ "advanced_provides_network"
+ )
+ self.install_system_check: Gtk.CheckButton = gtk_builder.get_object(
+ "advanced_install_from_device"
+ )
+ self.launch_settings_check: Gtk.CheckButton = gtk_builder.get_object(
+ "check_launch_settings"
+ )
- self.pool: Gtk.ComboBoxText = \
- gtk_builder.get_object('storage_pool_combobox')
+ self.pool: Gtk.ComboBoxText = gtk_builder.get_object(
+ "storage_pool_combobox"
+ )
self.initram: Gtk.SpinButton = gtk_builder.get_object(
- 'initram_spin_button')
-
+ "initram_spin_button"
+ )
pools: Dict[str, Any] = {}
for pool in self.qapp.pools.values():
if pool == self.qapp.default_pool:
- pools[f'default ({pool})'] = None
+ pools[f"default ({pool})"] = None
else:
pools[str(pool)] = str(pool)
@@ -91,17 +100,25 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
# discuss: max ram?
self.initram.configure(
- Gtk.Adjustment(value=0, lower=0, upper=100000, step_increment=1,
- page_increment=10, page_size=100),
- climb_rate=10, digits=0)
+ Gtk.Adjustment(
+ value=0,
+ lower=0,
+ upper=100000,
+ step_increment=1,
+ page_increment=10,
+ page_size=100,
+ ),
+ climb_rate=10,
+ digits=0,
+ )
- self.initram.connect('output', self._format_initram)
+ self.initram.connect("output", self._format_initram)
self.install_system_check.set_sensitive(False)
- self.install_system_check.connect('toggled', self._install_changed)
- self.launch_settings_check.connect('toggled', self._settings_changed)
+ self.install_system_check.connect("toggled", self._install_changed)
+ self.launch_settings_check.connect("toggled", self._settings_changed)
- self.main_window.connect('template-changed', self._template_changed)
+ self.main_window.connect("template-changed", self._template_changed)
def _template_changed(self, _widget, vm_name):
if vm_name is None:
@@ -119,14 +136,16 @@ def _install_changed(self, *_args):
self.launch_settings_check.set_active(False)
def _settings_changed(self, *_args):
- if self.launch_settings_check.get_active() and \
- self.install_system_check.get_sensitive():
+ if (
+ self.launch_settings_check.get_active()
+ and self.install_system_check.get_sensitive()
+ ):
self.install_system_check.set_active(False)
def _format_initram(self, _widget):
value = self.initram.get_adjustment().get_value()
if value == 0:
- text = '(default)'
+ text = "(default)"
self.initram.set_text(text)
return True
return False
diff --git a/qubes_config/new_qube/application_selector.py b/qubes_config/new_qube/application_selector.py
index f3412196..575460ed 100644
--- a/qubes_config/new_qube/application_selector.py
+++ b/qubes_config/new_qube/application_selector.py
@@ -29,23 +29,31 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, Pango
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-logger = logging.getLogger('qubes-new-qube')
-WHONIX_QUBE_NAME = 'sys-whonix'
+logger = logging.getLogger("qubes-new-qube")
+WHONIX_QUBE_NAME = "sys-whonix"
+
class ApplicationData:
"""
Class representing information about an available application.
"""
- def __init__(self, name: str, ident: str, comment: Optional[str] = None,
- template: Optional[qubesadmin.vm.QubesVM] = None):
+
+ def __init__(
+ self,
+ name: str,
+ ident: str,
+ comment: Optional[str] = None,
+ template: Optional[qubesadmin.vm.QubesVM] = None,
+ ):
"""
:param name: application name
:param ident: application id (as expected by qvm-appmenus)
@@ -57,10 +65,11 @@ def __init__(self, name: str, ident: str, comment: Optional[str] = None,
self.template = template
additional_description = ".desktop filename: " + str(self.ident)
- file_name_root = self.ident[:-len('.desktop')]
+ file_name_root = self.ident[: -len(".desktop")]
self.icon_path = os.path.expanduser(
- f'~/.local/share/qubes-appmenus/{template}'
- f'/apps.tempicons/{file_name_root}.png')
+ f"~/.local/share/qubes-appmenus/{template}"
+ f"/apps.tempicons/{file_name_root}.png"
+ )
if not comment:
self.comment = additional_description
@@ -72,7 +81,7 @@ def from_line(cls, line, template=None):
"""
Create object from output line of qvm-appmenus, with optional template.
"""
- ident, name, comment = line.split('|', maxsplit=3)
+ ident, name, comment = line.split("|", maxsplit=3)
return cls(name=name, ident=ident, comment=comment, template=template)
@@ -80,6 +89,7 @@ class ApplicationRow(Gtk.ListBoxRow):
"""
Row representing an app in current template.
"""
+
def __init__(self, appdata: ApplicationData, **properties):
"""
:param app_info: ApplicationInfo obj with data about related app file
@@ -106,6 +116,7 @@ class OtherTemplateApplicationRow(Gtk.ListBoxRow):
"""
Row representing an app in another template.
"""
+
def __init__(self, appdata: ApplicationData, **properties):
"""
:param app_info: ApplicationInfo obj with data about related app file
@@ -135,6 +146,7 @@ class ApplicationButton(Gtk.FlowBoxChild):
"""
Button representing a selected application.
"""
+
def __init__(self, appdata: ApplicationData):
"""
:param appdata: ApplicationData object
@@ -159,13 +171,14 @@ def __init__(self, appdata: ApplicationData):
self.box.pack_start(self.label, False, False, 3)
self.remove_icon = Gtk.Image()
- self.remove_icon.set_from_pixbuf(load_icon('qubes-delete', 14, 14))
+ self.remove_icon.set_from_pixbuf(load_icon("qubes-delete", 14, 14))
self.remove_icon.set_tooltip_text(
- _('Click to remove this application from selection'))
+ _("Click to remove this application from selection")
+ )
self.box.pack_end(self.remove_icon, False, False, 3)
- self.connect('activate', self._remove_self)
- self.button.connect('clicked', self._activate_self)
+ self.connect("activate", self._remove_self)
+ self.button.connect("clicked", self._activate_self)
self.show_all()
@@ -184,13 +197,14 @@ class AddButton(Gtk.FlowBoxChild):
"""
Button to open 'select apps' window.
"""
+
def __init__(self):
super().__init__()
self.button = Gtk.Button()
self.button.set_label("+")
self.add(self.button)
- self.button.connect('clicked', self._activate_self)
+ self.button.connect("clicked", self._activate_self)
def _activate_self(self, *_args):
self.activate()
@@ -200,66 +214,80 @@ class ApplicationBoxHandler:
"""
Class to handle popup application box.
"""
+
def __init__(self, gtk_builder: Gtk.Builder, template_selector):
"""
:param gtk_builder: Gtk.Builder to get relevant objects
:param template_selector: TemplateHandler object
"""
self.template_selector = template_selector
- self.flowbox: Gtk.Flowbox = gtk_builder.get_object('applications')
- self.apps_window = gtk_builder.get_object('applications_popup')
- self.apps_list: Gtk.ListBox = gtk_builder.get_object('apps_list')
- self.label_apps: Gtk.Label = gtk_builder.get_object('label_apps')
+ self.flowbox: Gtk.Flowbox = gtk_builder.get_object("applications")
+ self.apps_window = gtk_builder.get_object("applications_popup")
+ self.apps_list: Gtk.ListBox = gtk_builder.get_object("apps_list")
+ self.label_apps: Gtk.Label = gtk_builder.get_object("label_apps")
self.label_apps_explain: Gtk.Label = gtk_builder.get_object(
- 'label_apps_explain')
- self.apps_close: Gtk.Button = gtk_builder.get_object('apps_close')
- self.apps_search: Gtk.SearchEntry = \
- gtk_builder.get_object('apps_search')
- self.apps_list_placeholder: Gtk.Label = \
- gtk_builder.get_object('apps_list_placeholder')
- self.apps_list_other: Gtk.ListBox = \
- gtk_builder.get_object('apps_list_other_templates')
+ "label_apps_explain"
+ )
+ self.apps_close: Gtk.Button = gtk_builder.get_object("apps_close")
+ self.apps_search: Gtk.SearchEntry = gtk_builder.get_object(
+ "apps_search"
+ )
+ self.apps_list_placeholder: Gtk.Label = gtk_builder.get_object(
+ "apps_list_placeholder"
+ )
+ self.apps_list_other: Gtk.ListBox = gtk_builder.get_object(
+ "apps_list_other_templates"
+ )
self.label_other_templates: Gtk.Label = gtk_builder.get_object(
- 'label_other_templates')
- self.load_all_button: Gtk.Button = \
- gtk_builder.get_object('apps_list_load_all')
+ "label_other_templates"
+ )
+ self.load_all_button: Gtk.Button = gtk_builder.get_object(
+ "apps_list_load_all"
+ )
self.change_template_msg: Gtk.Dialog = gtk_builder.get_object(
- 'msg_change_template')
+ "msg_change_template"
+ )
self.change_template_ok: Gtk.Button = gtk_builder.get_object(
- 'change_template_ok')
+ "change_template_ok"
+ )
self.change_template_cancel: Gtk.Button = gtk_builder.get_object(
- 'change_template_cancel')
+ "change_template_cancel"
+ )
self.change_template_box: Gtk.Box = gtk_builder.get_object(
- 'change_template_box')
+ "change_template_box"
+ )
self.target_template_name_widget: Optional[Gtk.Widget] = None
self.change_template_cancel.connect(
- 'clicked', self._hide_template_change)
- self.change_template_ok.connect('clicked', self._do_template_change)
+ "clicked", self._hide_template_change
+ )
+ self.change_template_ok.connect("clicked", self._do_template_change)
self.change_template_msg.connect(
- 'key_press_event', self._keypress_change_template)
+ "key_press_event", self._keypress_change_template
+ )
- self.apps_window.connect('key_press_event', self._keypress_event)
- self.apps_list.connect('row-activated', self._row_activated)
+ self.apps_window.connect("key_press_event", self._keypress_event)
+ self.apps_list.connect("row-activated", self._row_activated)
self.fill_app_list(default=True)
self._fill_flow_list()
- self.apps_close.connect('clicked', self._hide_window)
+ self.apps_close.connect("clicked", self._hide_window)
self.apps_list.set_sort_func(self._sort_func_app_list)
- self.apps_search.connect('search-changed', self._do_search)
+ self.apps_search.connect("search-changed", self._do_search)
self.template_selector.main_window.connect(
- 'template-changed', self.template_change_registered)
+ "template-changed", self.template_change_registered
+ )
self.apps_list.set_filter_func(self._filter_func_app_list)
self.apps_list.set_sort_func(self._sort_func_app_list)
self.apps_list_other.set_sort_func(self._sort_func_app_list)
self.apps_list_other.set_filter_func(self._filter_func_other_list)
- self.load_all_button.connect('clicked', self._load_all_apps)
+ self.load_all_button.connect("clicked", self._load_all_apps)
self.flowbox.set_sort_func(self._sort_flowbox)
- self.apps_window.connect('delete-event', self._hide_window)
+ self.apps_window.connect("delete-event", self._hide_window)
self._fill_others_list()
@staticmethod
@@ -274,8 +302,9 @@ def _cmp(a, b):
def _sort_func_app_list(self, x: ApplicationRow, y: ApplicationRow):
# negation because True > False, and we want the selected rows to be
# at the top
- selection_comparison = self._cmp(not x.is_selected(),
- not y.is_selected())
+ selection_comparison = self._cmp(
+ not x.is_selected(), not y.is_selected()
+ )
if selection_comparison == 0:
return self._cmp(x.appdata.name, y.appdata.name)
return selection_comparison
@@ -297,7 +326,8 @@ def _filter_func_other_list(self, x: ApplicationRow):
if not self.apps_list_placeholder.get_mapped():
return False
if not self.template_selector.is_given_template_available(
- x.appdata.template):
+ x.appdata.template
+ ):
return False
return self._filter_func_app_list(x)
@@ -355,7 +385,8 @@ def fill_app_list(self, default=False):
return
available_applications = self.template_selector.get_available_apps(
- template_vm)
+ template_vm
+ )
selected = []
if default:
selected = self.template_selector.get_default_apps(template_vm)
@@ -379,7 +410,7 @@ def _fill_others_list(self):
row = OtherTemplateApplicationRow(app)
self.apps_list_other.add(row)
self.apps_list_other.set_visible(False)
- self.apps_list_other.connect('row-activated', self._ask_template_change)
+ self.apps_list_other.connect("row-activated", self._ask_template_change)
def _hide_template_change(self, *_args):
self.change_template_msg.hide()
@@ -387,7 +418,8 @@ def _hide_template_change(self, *_args):
def _do_template_change(self, *_args):
if self.target_template_name_widget:
self.template_selector.select_template(
- self.target_template_name_widget.vm)
+ self.target_template_name_widget.vm
+ )
self._hide_template_change()
self._hide_window()
@@ -397,7 +429,8 @@ def _ask_template_change(self, _widget, row, *_args):
self.target_template_name_widget = QubeName(row.appdata.template)
self.change_template_box.pack_start(
- self.target_template_name_widget, False, False, 0)
+ self.target_template_name_widget, False, False, 0
+ )
self.change_template_msg.show()
@@ -424,7 +457,7 @@ def _fill_flow_list(self):
button = ApplicationButton(child.appdata)
self.flowbox.add(button)
plus_button = AddButton()
- plus_button.connect('activate', self._choose_apps)
+ plus_button.connect("activate", self._choose_apps)
# need interaction with Template object
self.flowbox.add(plus_button)
self.flowbox.show_all()
diff --git a/qubes_config/new_qube/network_selector.py b/qubes_config/new_qube/network_selector.py
index 096fe995..6658995d 100644
--- a/qubes_config/new_qube/network_selector.py
+++ b/qubes_config/new_qube/network_selector.py
@@ -28,16 +28,18 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
-logger = logging.getLogger('qubes-new-qube')
-WHONIX_QUBE_NAME = 'sys-whonix'
+logger = logging.getLogger("qubes-new-qube")
+WHONIX_QUBE_NAME = "sys-whonix"
+
class NetworkSelector:
"""
Class that handles network configuration.
"""
+
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
"""
:param gtk_builder: Gtk.Builder object
@@ -50,63 +52,81 @@ def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
self.network_tor_icon: Optional[Gtk.Image] = None
self.network_tor_name: Optional[Gtk.Label] = None
- self.network_default_box: Gtk.Box = \
- gtk_builder.get_object('network_default_box')
+ self.network_default_box: Gtk.Box = gtk_builder.get_object(
+ "network_default_box"
+ )
self.network_default_box.pack_start(
- QubeName(self.qapp.default_netvm), False, False, 0)
-
- self.network_tor_box: Gtk.Box = \
- gtk_builder.get_object('network_tor_box')
- self.network_custom: Gtk.RadioButton = \
- gtk_builder.get_object('network_custom')
- self.network_custom_combo: Gtk.ComboBox = \
- gtk_builder.get_object('network_custom_combo')
- self.network_custom.connect('toggled', self._custom_toggled)
-
- self.network_none: Gtk.RadioButton = \
- gtk_builder.get_object('network_none')
- self.network_tor: Gtk.RadioButton = \
- gtk_builder.get_object('network_tor')
- self.network_default: Gtk.RadioButton = \
- gtk_builder.get_object('network_default')
+ QubeName(self.qapp.default_netvm), False, False, 0
+ )
+
+ self.network_tor_box: Gtk.Box = gtk_builder.get_object(
+ "network_tor_box"
+ )
+ self.network_custom: Gtk.RadioButton = gtk_builder.get_object(
+ "network_custom"
+ )
+ self.network_custom_combo: Gtk.ComboBox = gtk_builder.get_object(
+ "network_custom_combo"
+ )
+ self.network_custom.connect("toggled", self._custom_toggled)
+
+ self.network_none: Gtk.RadioButton = gtk_builder.get_object(
+ "network_none"
+ )
+ self.network_tor: Gtk.RadioButton = gtk_builder.get_object(
+ "network_tor"
+ )
+ self.network_default: Gtk.RadioButton = gtk_builder.get_object(
+ "network_default"
+ )
if WHONIX_QUBE_NAME in self.qapp.domains:
self.network_tor_box.pack_start(
- QubeName(self.qapp.domains[WHONIX_QUBE_NAME]), False, False, 0)
+ QubeName(self.qapp.domains[WHONIX_QUBE_NAME]), False, False, 0
+ )
else:
self.network_tor_box.set_visible(False)
self.network_tor_box.set_no_show_all(True)
self.network_modeler = VMListModeler(
- self.network_custom_combo, self.qapp,
- lambda x: getattr(x, 'provides_network', False))
-
- self.network_tor.connect('toggled', self._netvm_changed)
- self.network_none.connect('toggled', self._netvm_changed)
- self.network_default.connect('toggled', self._netvm_changed)
- self.network_custom.connect('toggled', self._netvm_changed)
- self.network_custom_combo.connect('changed', self._netvm_changed_combo)
-
- self.network_current_box: Gtk.Box = \
- gtk_builder.get_object('box_network_current')
- self.network_current_none: Gtk.Label = \
- gtk_builder.get_object('label_current_network_none')
+ self.network_custom_combo,
+ self.qapp,
+ lambda x: getattr(x, "provides_network", False),
+ )
+
+ self.network_tor.connect("toggled", self._netvm_changed)
+ self.network_none.connect("toggled", self._netvm_changed)
+ self.network_default.connect("toggled", self._netvm_changed)
+ self.network_custom.connect("toggled", self._netvm_changed)
+ self.network_custom_combo.connect("changed", self._netvm_changed_combo)
+
+ self.network_current_box: Gtk.Box = gtk_builder.get_object(
+ "box_network_current"
+ )
+ self.network_current_none: Gtk.Label = gtk_builder.get_object(
+ "label_current_network_none"
+ )
self.network_current_widget = QubeName(self.qapp.default_netvm)
self.network_current_box.pack_start(
- self.network_current_widget, False, False, 3)
+ self.network_current_widget, False, False, 3
+ )
self.network_current_none.set_visible(False)
- self.button_toggle_settings: Gtk.Button = \
- gtk_builder.get_object('event_button_network_current')
- self.box_network_settings: Gtk.Box = \
- gtk_builder.get_object('box_network_settings')
- self.expander_image: Gtk.Image = \
- gtk_builder.get_object('network_settings_expander_icon')
+ self.button_toggle_settings: Gtk.Button = gtk_builder.get_object(
+ "event_button_network_current"
+ )
+ self.box_network_settings: Gtk.Box = gtk_builder.get_object(
+ "box_network_settings"
+ )
+ self.expander_image: Gtk.Image = gtk_builder.get_object(
+ "network_settings_expander_icon"
+ )
self.expander_handler = ExpanderHandler(
event_button=self.button_toggle_settings,
data_container=self.box_network_settings,
- icon=self.expander_image)
+ icon=self.expander_image,
+ )
def _custom_toggled(self, widget):
self.network_custom_combo.set_sensitive(widget.get_active())
@@ -127,8 +147,9 @@ def _netvm_changed(self, widget: Optional[Gtk.RadioButton]):
if current_netvm:
self.network_current_widget = QubeName(current_netvm)
- self.network_current_box.pack_start(self.network_current_widget,
- False, False, 3)
+ self.network_current_box.pack_start(
+ self.network_current_widget, False, False, 3
+ )
def get_selected_netvm(self) -> Optional[qubesadmin.vm.QubesVM]:
"""Get which vm (if any) is selected as netvm"""
diff --git a/qubes_config/new_qube/new_qube_app.py b/qubes_config/new_qube/new_qube_app.py
index f0a3f7c1..6d68cfb4 100644
--- a/qubes_config/new_qube/new_qube_app.py
+++ b/qubes_config/new_qube/new_qube_app.py
@@ -36,32 +36,37 @@
from .network_selector import NetworkSelector
from .advanced_handler import AdvancedHandler
from ..widgets.gtk_utils import load_icon, show_error, load_theme
-from ..widgets.gtk_widgets import ProgressBarDialog, ImageListModeler,\
- ViewportHandler
+from ..widgets.gtk_widgets import (
+ ProgressBarDialog,
+ ImageListModeler,
+ ViewportHandler,
+)
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GObject
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-logger = logging.getLogger('qubes-new-qube')
-WHONIX_QUBE_NAME = 'sys-whonix'
+logger = logging.getLogger("qubes-new-qube")
+WHONIX_QUBE_NAME = "sys-whonix"
class CreateNewQube(Gtk.Application):
"""
Main Gtk.Application for new qube widget.
"""
+
def __init__(self, qapp):
"""
:param qapp: qubesadmin.Qubes object
"""
- super().__init__(application_id='org.qubesos.newqube')
+ super().__init__(application_id="org.qubesos.newqube")
self.qapp: qubesadmin.Qubes = qapp
self.builder: Optional[Gtk.Builder] = None
@@ -69,7 +74,8 @@ def __init__(self, qapp):
self.template_selector: Optional[TemplateSelector] = None
self.progress_bar_dialog = ProgressBarDialog(
- self, _("Loading available applications..."))
+ self, _("Loading available applications...")
+ )
def do_activate(self, *args, **kwargs):
"""
@@ -81,15 +87,20 @@ def do_activate(self, *args, **kwargs):
self.perform_setup()
assert self.main_window
self.main_window.show()
- if self.main_window.get_allocated_width() > \
- self.main_window.get_screen().get_width():
+ if (
+ self.main_window.get_allocated_width()
+ > self.main_window.get_screen().get_width()
+ ):
width = int(self.main_window.get_screen().get_width() * 0.9)
else:
# try to have at least 1100 pixels
- width = min(int(self.main_window.get_screen().get_width() * 0.9),
- 800)
- if self.main_window.get_allocated_height() > \
- self.main_window.get_screen().get_height() * 0.9:
+ width = min(
+ int(self.main_window.get_screen().get_width() * 0.9), 800
+ )
+ if (
+ self.main_window.get_allocated_height()
+ > self.main_window.get_screen().get_height() * 0.9
+ ):
height = int(self.main_window.get_screen().get_height() * 0.9)
else:
height = self.main_window.get_allocated_height()
@@ -104,20 +115,22 @@ def perform_setup(self):
be only called once, in the main instance of this application.
"""
self.builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'new_qube.glade')
+ glade_ref = importlib.resources.files("qubes_config") / "new_qube.glade"
with importlib.resources.as_file(glade_ref) as path:
self.builder.add_from_file(str(path))
- self.main_window = self.builder.get_object('main_window')
- self.qube_name: Gtk.Entry = self.builder.get_object('qube_name')
- self.qube_label_combo: Gtk.ComboBox = \
- self.builder.get_object('qube_label')
+ self.main_window = self.builder.get_object("main_window")
+ self.qube_name: Gtk.Entry = self.builder.get_object("qube_name")
+ self.qube_label_combo: Gtk.ComboBox = self.builder.get_object(
+ "qube_label"
+ )
- load_theme(widget=self.main_window,
- package_name='qubes_config',
- light_file_name='qubes-new-qube-light.css',
- dark_file_name='qubes-new-qube-dark.css')
+ load_theme(
+ widget=self.main_window,
+ package_name="qubes_config",
+ light_file_name="qubes-new-qube-light.css",
+ dark_file_name="qubes-new-qube-dark.css",
+ )
self.progress_bar_dialog.show_all()
self.progress_bar_dialog.update_progress(0.1)
@@ -126,43 +139,49 @@ def perform_setup(self):
self.progress_bar_dialog.update_progress(0.1)
- self.qube_type_app: Gtk.RadioButton = \
- self.builder.get_object('qube_type_app')
- self.qube_type_template: Gtk.RadioButton = \
- self.builder.get_object('qube_type_template')
- self.qube_type_standalone: Gtk.RadioButton = \
- self.builder.get_object('qube_type_standalone')
- self.qube_type_disposable: Gtk.RadioButton = \
- self.builder.get_object('qube_type_disposable')
+ self.qube_type_app: Gtk.RadioButton = self.builder.get_object(
+ "qube_type_app"
+ )
+ self.qube_type_template: Gtk.RadioButton = self.builder.get_object(
+ "qube_type_template"
+ )
+ self.qube_type_standalone: Gtk.RadioButton = self.builder.get_object(
+ "qube_type_standalone"
+ )
+ self.qube_type_disposable: Gtk.RadioButton = self.builder.get_object(
+ "qube_type_disposable"
+ )
self.tooltips = {
- 'qube_type_app': self.builder.get_object('qube_type_app_q'),
- 'qube_type_template':
- self.builder.get_object('qube_type_template_q'),
- 'qube_type_standalone':
- self.builder.get_object('qube_type_standalone_q'),
- 'qube_type_disposable':
- self.builder.get_object('qube_type_disposable_q')
+ "qube_type_app": self.builder.get_object("qube_type_app_q"),
+ "qube_type_template": self.builder.get_object(
+ "qube_type_template_q"
+ ),
+ "qube_type_standalone": self.builder.get_object(
+ "qube_type_standalone_q"
+ ),
+ "qube_type_disposable": self.builder.get_object(
+ "qube_type_disposable_q"
+ ),
}
- self.qube_type_app.connect('toggled', self._type_selected)
- self.qube_type_template.connect('toggled', self._type_selected)
- self.qube_type_standalone.connect('toggled', self._type_selected)
- self.qube_type_disposable.connect('toggled', self._type_selected)
+ self.qube_type_app.connect("toggled", self._type_selected)
+ self.qube_type_template.connect("toggled", self._type_selected)
+ self.qube_type_standalone.connect("toggled", self._type_selected)
+ self.qube_type_disposable.connect("toggled", self._type_selected)
label_dict = {}
for label in self.qapp.labels:
label_dict[str(label)] = {
- 'icon': f'appvm-{label}',
- 'object': str(label)
+ "icon": f"appvm-{label}",
+ "object": str(label),
}
self.qube_label_modeler = ImageListModeler(
- combobox=self.qube_label_combo,
- value_list=label_dict
+ combobox=self.qube_label_combo, value_list=label_dict
)
- self.qube_name.connect('changed', self._name_changed)
+ self.qube_name.connect("changed", self._name_changed)
self.progress_bar_dialog.update_progress(0.1)
@@ -171,7 +190,8 @@ def perform_setup(self):
self.progress_bar_dialog.update_progress(0.1)
self.app_box_handler = ApplicationBoxHandler(
- self.builder, self.template_handler)
+ self.builder, self.template_handler
+ )
self.progress_bar_dialog.update_progress(0.1)
@@ -179,18 +199,21 @@ def perform_setup(self):
self.progress_bar_dialog.update_progress(0.1)
- self.create_button: Gtk.Button = \
- self.builder.get_object('create_button')
- self.create_button.connect('clicked', self._do_create_qube)
+ self.create_button: Gtk.Button = self.builder.get_object(
+ "create_button"
+ )
+ self.create_button.connect("clicked", self._do_create_qube)
- self.cancel_button: Gtk.Button = \
- self.builder.get_object('cancel_button')
- self.cancel_button.connect('clicked', self._quit)
+ self.cancel_button: Gtk.Button = self.builder.get_object(
+ "cancel_button"
+ )
+ self.cancel_button.connect("clicked", self._quit)
self.viewport_handler = ViewportHandler(
- self.main_window, [self.builder.get_object('main_scrolled_window')])
+ self.main_window, [self.builder.get_object("main_scrolled_window")]
+ )
- self.main_window.connect('delete-event', self._quit)
+ self.main_window.connect("delete-event", self._quit)
self.progress_bar_dialog.update_progress(1)
self.progress_bar_dialog.hide()
@@ -201,9 +224,13 @@ def _quit(self, *_args):
@staticmethod
def register_signals():
"""Register necessary Gtk signals"""
- GObject.signal_new('template-changed',
- Gtk.Window,
- GObject.SignalFlags.RUN_LAST, None, (str,))
+ GObject.signal_new(
+ "template-changed",
+ Gtk.Window,
+ GObject.SignalFlags.RUN_LAST,
+ None,
+ (str,),
+ )
def _name_changed(self, entry: Gtk.Entry):
if not entry.get_text():
@@ -215,17 +242,19 @@ def _type_selected(self, button: Gtk.RadioButton):
button_name = button.get_name()
if not button.get_active():
self.tooltips[button_name].set_from_pixbuf(
- load_icon('qubes-question', 20, 20))
+ load_icon("qubes-question", 20, 20)
+ )
return
self.template_handler.change_vm_type(button_name)
- if button_name == 'qube_type_template':
+ if button_name == "qube_type_template":
self.network_selector.network_none.set_active(True)
else:
self.network_selector.network_default.set_active(True)
- self.tooltips[button_name].set_from_pixbuf(load_icon(
- 'qubes-question-light', 20, 20))
+ self.tooltips[button_name].set_from_pixbuf(
+ load_icon("qubes-question-light", 20, 20)
+ )
def _do_create_qube(self, *_args):
label = self.qube_label_modeler.get_selected()
@@ -235,27 +264,32 @@ def _do_create_qube(self, *_args):
raise ValueError
if self.qube_type_template.get_active():
- klass = 'TemplateVM'
+ klass = "TemplateVM"
elif self.qube_type_standalone.get_active():
- klass = 'StandaloneVM'
+ klass = "StandaloneVM"
elif self.qube_type_disposable.get_active():
- klass = 'DispVM'
+ klass = "DispVM"
else:
- klass = 'AppVM'
+ klass = "AppVM"
- properties: Dict[str, Any] = {'provides_network':
- self.advanced_handler.get_provides_network()}
+ properties: Dict[str, Any] = {
+ "provides_network": self.advanced_handler.get_provides_network()
+ }
selected_netvm = self.network_selector.get_selected_netvm()
- if not (klass == 'TemplateVM' and selected_netvm is None) \
- and selected_netvm != qubesadmin.DEFAULT:
- properties['netvm'] = selected_netvm
- if klass == 'StandaloneVM' and \
- not self.template_handler.get_selected_template():
- properties['virt_mode'] = 'hvm'
- properties['kernel'] = None
+ if (
+ not (klass == "TemplateVM" and selected_netvm is None)
+ and selected_netvm != qubesadmin.DEFAULT
+ ):
+ properties["netvm"] = selected_netvm
+ if (
+ klass == "StandaloneVM"
+ and not self.template_handler.get_selected_template()
+ ):
+ properties["virt_mode"] = "hvm"
+ properties["kernel"] = None
if self.advanced_handler.get_init_ram():
- properties['memory'] = self.advanced_handler.get_init_ram()
+ properties["memory"] = self.advanced_handler.get_init_ram()
vm = None
err = None
@@ -264,35 +298,38 @@ def _do_create_qube(self, *_args):
vm = self._create_qube(
vmclass=klass,
name=name,
- label = label,
+ label=label,
template=self.template_handler.get_selected_template(),
properties=properties,
pool=self.advanced_handler.get_pool(),
)
except qubesadmin.exc.QubesException as qex:
- err = str(qex)
+ err = str(qex)
except Exception as ex: # pylint: disable=broad-except
err = repr(ex)
if err or not vm:
- show_error(self.main_window, _("Could not create qube"),
- _("An error occurred: {error}").format(error=err))
+ show_error(
+ self.main_window,
+ _("Could not create qube"),
+ _("An error occurred: {error}").format(error=err),
+ )
return
apps = self.app_box_handler.get_selected_apps()
if apps:
- with subprocess.Popen([
- 'qvm-appmenus',
- '--set-whitelist', '-',
- '--update', vm.name],
- stdin=subprocess.PIPE) as p:
- p.communicate('\n'.join(apps).encode())
+ with subprocess.Popen(
+ ["qvm-appmenus", "--set-whitelist", "-", "--update", vm.name],
+ stdin=subprocess.PIPE,
+ ) as p:
+ p.communicate("\n".join(apps).encode())
if p.returncode != 0:
show_error(
self.main_window,
_("Failed to select applications"),
- _("An error occurred: {error}").format(error=err))
+ _("An error occurred: {error}").format(error=err),
+ )
return
msg = Gtk.MessageDialog(
transient_for=self.main_window,
@@ -300,37 +337,33 @@ def _do_create_qube(self, *_args):
destroy_with_parent=True,
message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK,
- text=_("Qube created successfully!"))
+ text=_("Qube created successfully!"),
+ )
msg.run()
if self.advanced_handler.get_launch_settings():
- subprocess.check_call(['qubes-vm-settings', str(vm)])
+ subprocess.check_call(["qubes-vm-settings", str(vm)])
if self.advanced_handler.get_install_system():
- subprocess.check_call(['qubes-vm-boot-from-device', str(vm)])
+ subprocess.check_call(["qubes-vm-boot-from-device", str(vm)])
self.quit()
- def _create_qube(self, vmclass, name, label, template,
- properties, pool) -> qubesadmin.vm.QubesVM:
- if vmclass in ['StandaloneVM', 'TemplateVM'] and template is not None:
- args = {
- 'ignore_volumes': ['private']
- }
+ def _create_qube(
+ self, vmclass, name, label, template, properties, pool
+ ) -> qubesadmin.vm.QubesVM:
+ if vmclass in ["StandaloneVM", "TemplateVM"] and template is not None:
+ args = {"ignore_volumes": ["private"]}
if pool:
- args['pool'] = pool
+ args["pool"] = pool
vm = self.qapp.clone_vm(template, name, vmclass, **args)
vm.label = label
for k, v in properties.items():
setattr(vm, k, v)
else:
- args = {
- "name": name,
- "label": label,
- "template": template
- }
+ args = {"name": name, "label": label, "template": template}
if pool:
- args['pool'] = pool
+ args["pool"] = pool
vm = self.qapp.add_new_vm(vmclass, **args)
for k, v in properties.items():
@@ -348,5 +381,5 @@ def main():
app.run(sys.argv)
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qubes_config/new_qube/template_handler.py b/qubes_config/new_qube/template_handler.py
index 567b12b0..a77d2316 100644
--- a/qubes_config/new_qube/template_handler.py
+++ b/qubes_config/new_qube/template_handler.py
@@ -32,24 +32,26 @@
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-logger = logging.getLogger('qubes-new-qube')
-WHONIX_QUBE_NAME = 'sys-whonix'
+logger = logging.getLogger("qubes-new-qube")
+WHONIX_QUBE_NAME = "sys-whonix"
class TemplateSelector(abc.ABC):
"""
Abstract base class for various variants of template/source VM selection.
"""
+
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
self.qapp = qapp
- self.main_window = gtk_builder.get_object('main_window')
+ self.main_window = gtk_builder.get_object("main_window")
@abc.abstractmethod
def set_visible(self, state: bool):
@@ -81,7 +83,7 @@ def emit_signal(self, widget=None):
:return: None
"""
if widget is None or widget.get_active():
- self.main_window.emit('template-changed', self.get_selected_vm())
+ self.main_window.emit("template-changed", self.get_selected_vm())
@abc.abstractmethod
def is_vm_available(self, vm: qubesadmin.vm.QubesVM) -> bool:
@@ -94,11 +96,15 @@ class TemplateSelectorCombo(TemplateSelector):
"""
Simple combobox selector.
"""
- def __init__(self, gtk_builder: Gtk.Builder,
- qapp: qubesadmin.Qubes,
- name_suffix: str,
- filter_function: Callable[[qubesadmin.vm.QubesVM], bool],
- default_value: Optional[qubesadmin.vm.QubesVM]):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ qapp: qubesadmin.Qubes,
+ name_suffix: str,
+ filter_function: Callable[[qubesadmin.vm.QubesVM], bool],
+ default_value: Optional[qubesadmin.vm.QubesVM],
+ ):
"""
:param gtk_builder: Gtk.Builder object
:param qapp: Qubes object
@@ -111,17 +117,22 @@ def __init__(self, gtk_builder: Gtk.Builder,
"""
super().__init__(gtk_builder, qapp)
- self.label: Gtk.Label = \
- gtk_builder.get_object(f'label_template_{name_suffix}')
- self.explain_label: Gtk.Label = \
- gtk_builder.get_object(f'label_template_explanation_{name_suffix}')
- self.combo: Gtk.ComboBox = \
- gtk_builder.get_object(f'combo_template_{name_suffix}')
+ self.label: Gtk.Label = gtk_builder.get_object(
+ f"label_template_{name_suffix}"
+ )
+ self.explain_label: Gtk.Label = gtk_builder.get_object(
+ f"label_template_explanation_{name_suffix}"
+ )
+ self.combo: Gtk.ComboBox = gtk_builder.get_object(
+ f"combo_template_{name_suffix}"
+ )
self.modeler = VMListModeler(
- combobox=self.combo, qapp=self.qapp,
+ combobox=self.combo,
+ qapp=self.qapp,
filter_function=filter_function,
- default_value=default_value)
+ default_value=default_value,
+ )
# done separately to avoid recursion problem
self.modeler.connect_change_callback(self.emit_signal)
@@ -152,11 +163,15 @@ class TemplateSelectorNoneCombo(TemplateSelector):
"""
Selector for a combination of None/combobox with VMs.
"""
- def __init__(self, gtk_builder: Gtk.Builder,
- qapp: qubesadmin.Qubes,
- name_suffix: str,
- filter_function: Callable[[qubesadmin.vm.QubesVM], bool],
- default_value: Optional[qubesadmin.vm.QubesVM]):
+
+ def __init__(
+ self,
+ gtk_builder: Gtk.Builder,
+ qapp: qubesadmin.Qubes,
+ name_suffix: str,
+ filter_function: Callable[[qubesadmin.vm.QubesVM], bool],
+ default_value: Optional[qubesadmin.vm.QubesVM],
+ ):
"""
:param gtk_builder: Gtk.Builder object
:param qapp: Qubes object
@@ -167,26 +182,34 @@ def __init__(self, gtk_builder: Gtk.Builder,
"""
super().__init__(gtk_builder, qapp)
- self.label: Gtk.Label = \
- gtk_builder.get_object(f'label_template_{name_suffix}')
- self.explain_label: Gtk.Label = \
- gtk_builder.get_object(f'label_template_explanation_{name_suffix}')
- self.radio_none: Gtk.RadioButton = \
- gtk_builder.get_object(f'radio_{name_suffix}_none')
- self.box_template: Gtk.Box = \
- gtk_builder.get_object(f'box_radio_{name_suffix}')
- self.radio_template: Gtk.RadioButton = \
- gtk_builder.get_object(f'radio_template_{name_suffix}')
- self.combo_template: Gtk.ComboBox = \
- gtk_builder.get_object(f'combo_template_{name_suffix}')
+ self.label: Gtk.Label = gtk_builder.get_object(
+ f"label_template_{name_suffix}"
+ )
+ self.explain_label: Gtk.Label = gtk_builder.get_object(
+ f"label_template_explanation_{name_suffix}"
+ )
+ self.radio_none: Gtk.RadioButton = gtk_builder.get_object(
+ f"radio_{name_suffix}_none"
+ )
+ self.box_template: Gtk.Box = gtk_builder.get_object(
+ f"box_radio_{name_suffix}"
+ )
+ self.radio_template: Gtk.RadioButton = gtk_builder.get_object(
+ f"radio_template_{name_suffix}"
+ )
+ self.combo_template: Gtk.ComboBox = gtk_builder.get_object(
+ f"combo_template_{name_suffix}"
+ )
self.modeler = VMListModeler(
- combobox=self.combo_template, qapp=self.qapp,
+ combobox=self.combo_template,
+ qapp=self.qapp,
filter_function=filter_function,
- default_value=default_value)
+ default_value=default_value,
+ )
- self.radio_none.connect('toggled', self.emit_signal)
- self.radio_template.connect('toggled', self._radio_toggled)
+ self.radio_none.connect("toggled", self.emit_signal)
+ self.radio_template.connect("toggled", self._radio_toggled)
# done separately to avoid infinite recursion
self.modeler.connect_change_callback(self.emit_signal)
self.radio_none.set_active(True)
@@ -230,40 +253,55 @@ def is_vm_available(self, vm: qubesadmin.vm.QubesVM) -> bool:
class TemplateHandler:
"""Class to handle a collection of template selectors"""
+
def __init__(self, gtk_builder: Gtk.Builder, qapp: qubesadmin.Qubes):
"""
:param gtk_builder: Gtk.Builder object
:param qapp: Qubes object
"""
self.qapp = qapp
- self.main_window: Gtk.Window = gtk_builder.get_object('main_window')
+ self.main_window: Gtk.Window = gtk_builder.get_object("main_window")
self.template_selectors: Dict[str, TemplateSelector] = {
- 'qube_type_app': TemplateSelectorCombo(
- gtk_builder=gtk_builder, qapp=self.qapp, name_suffix='app',
- filter_function=lambda x: x.klass == 'TemplateVM',
- default_value=self.qapp.default_template),
- 'qube_type_template': TemplateSelectorNoneCombo(
- gtk_builder=gtk_builder, qapp=self.qapp, name_suffix='template',
- filter_function=lambda x: x.klass == 'TemplateVM',
- default_value=None),
- 'qube_type_standalone': TemplateSelectorNoneCombo(
- gtk_builder=gtk_builder, qapp=self.qapp,
- name_suffix='standalone',
- filter_function=lambda x: x.klass in
- ('TemplateVM', 'StandaloneVM'),
- default_value=None),
- 'qube_type_disposable': TemplateSelectorCombo(
- gtk_builder=gtk_builder, qapp=self.qapp, name_suffix='dispvm',
- filter_function=lambda x:
- getattr(x, 'template_for_dispvms', False),
- default_value=self.qapp.default_dispvm)}
+ "qube_type_app": TemplateSelectorCombo(
+ gtk_builder=gtk_builder,
+ qapp=self.qapp,
+ name_suffix="app",
+ filter_function=lambda x: x.klass == "TemplateVM",
+ default_value=self.qapp.default_template,
+ ),
+ "qube_type_template": TemplateSelectorNoneCombo(
+ gtk_builder=gtk_builder,
+ qapp=self.qapp,
+ name_suffix="template",
+ filter_function=lambda x: x.klass == "TemplateVM",
+ default_value=None,
+ ),
+ "qube_type_standalone": TemplateSelectorNoneCombo(
+ gtk_builder=gtk_builder,
+ qapp=self.qapp,
+ name_suffix="standalone",
+ filter_function=lambda x: x.klass
+ in ("TemplateVM", "StandaloneVM"),
+ default_value=None,
+ ),
+ "qube_type_disposable": TemplateSelectorCombo(
+ gtk_builder=gtk_builder,
+ qapp=self.qapp,
+ name_suffix="dispvm",
+ filter_function=lambda x: getattr(
+ x, "template_for_dispvms", False
+ ),
+ default_value=self.qapp.default_dispvm,
+ ),
+ }
self.selected_type: Optional[str] = None
- self.change_vm_type('qube_type_app')
+ self.change_vm_type("qube_type_app")
- self._application_data: \
- Dict[qubesadmin.vm.QubesVM, List[ApplicationData]] = {}
+ self._application_data: Dict[
+ qubesadmin.vm.QubesVM, List[ApplicationData]
+ ] = {}
self._default_applications: Dict[qubesadmin.vm.QubesVM, List[str]] = {}
def change_vm_type(self, vm_type: str):
@@ -272,8 +310,7 @@ def change_vm_type(self, vm_type: str):
for selector_type, selector in self.template_selectors.items():
selector.set_visible(selector_type == vm_type)
self.selected_type = vm_type
- self.main_window.emit('template-changed',
- self.get_selected_template())
+ self.main_window.emit("template-changed", self.get_selected_template())
def get_selected_template(self) -> Optional[qubesadmin.vm.QubesVM]:
"""Get currently selected VM."""
@@ -281,12 +318,14 @@ def get_selected_template(self) -> Optional[qubesadmin.vm.QubesVM]:
return self.template_selectors[self.selected_type].get_selected_vm()
return None
- def is_given_template_available(self,
- template: qubesadmin.vm.QubesVM) -> bool:
+ def is_given_template_available(
+ self, template: qubesadmin.vm.QubesVM
+ ) -> bool:
"""Check if given qubesVM is among available templates."""
if self.selected_type:
return self.template_selectors[self.selected_type].is_vm_available(
- template)
+ template
+ )
return False
def load_all_available_apps(self):
@@ -298,38 +337,53 @@ def get_available_apps(self, vm: Optional[qubesadmin.vm.QubesVM] = None):
if vm:
if vm in self._application_data:
return self._application_data.get(vm, [])
- command = ['qvm-appmenus', '--get-available',
- '--i-understand-format-is-unstable', '--file-field',
- 'Comment', vm.name]
+ command = [
+ "qvm-appmenus",
+ "--get-available",
+ "--i-understand-format-is-unstable",
+ "--file-field",
+ "Comment",
+ vm.name,
+ ]
try:
available_apps = [
ApplicationData.from_line(line, template=vm)
- for line in subprocess.check_output(
- command).decode().splitlines()]
+ for line in subprocess.check_output(command)
+ .decode()
+ .splitlines()
+ ]
self._application_data[vm] = available_apps
except subprocess.CalledProcessError:
- show_error(self.main_window,
- _('Failed to load application data'),
- _('Failed to load application data for ') + vm.name)
+ show_error(
+ self.main_window,
+ _("Failed to load application data"),
+ _("Failed to load application data for ") + vm.name,
+ )
available_apps = []
return available_apps
- return [appdata for appdata_list
- in self._application_data.values() for appdata in appdata_list]
+ return [
+ appdata
+ for appdata_list in self._application_data.values()
+ for appdata in appdata_list
+ ]
def get_default_apps(self, vm: qubesadmin.vm.QubesVM) -> List[str]:
"""Get list of default apps for a given template"""
if vm in self._default_applications:
return self._default_applications.get(vm, [])
- command = ['qvm-appmenus', '--get-default-whitelist', vm.name]
+ command = ["qvm-appmenus", "--get-default-whitelist", vm.name]
try:
- default_applications = subprocess.check_output(
- command).decode().splitlines()
+ default_applications = (
+ subprocess.check_output(command).decode().splitlines()
+ )
except subprocess.CalledProcessError:
- show_error(self.main_window,
- _('Failed to load application data'),
- _('Failed to load default_applications for ') + vm.name)
+ show_error(
+ self.main_window,
+ _("Failed to load application data"),
+ _("Failed to load default_applications for ") + vm.name,
+ )
default_applications = []
self._default_applications[vm] = default_applications
return default_applications
diff --git a/qubes_config/policy_editor/policy_editor.py b/qubes_config/policy_editor/policy_editor.py
index 1e60c1af..cd574841 100644
--- a/qubes_config/policy_editor/policy_editor.py
+++ b/qubes_config/policy_editor/policy_editor.py
@@ -31,17 +31,24 @@
from qrexec.policy.parser import StringPolicy
from qrexec.exc import PolicySyntaxError
-from qubes_config.widgets.gtk_utils import load_theme, show_error, \
- ask_question, is_theme_light
+from qubes_config.widgets.gtk_utils import (
+ load_theme,
+ show_error,
+ ask_question,
+ is_theme_light,
+)
from qubes_config.widgets.utils import open_url_in_disposable
-gi.require_version('Gtk', '3.0')
-gi.require_version('GtkSource', '4')
+gi.require_version("Gtk", "3.0")
+gi.require_version("GtkSource", "4")
from gi.repository import Gtk, GtkSource, Gio, Gdk
-HEADER_NORMAL = ' service_name\targument\tsource_qube' \
- '\ttarget_qube\taction [parameter=value] '
+HEADER_NORMAL = (
+ " service_name\targument\tsource_qube"
+ "\ttarget_qube\taction [parameter=value] "
+)
+
class FileListBoxRow(Gtk.ListBoxRow):
def __init__(self, filename):
@@ -52,20 +59,25 @@ def __init__(self, filename):
self.show_all()
self.filename = filename
+
class OpenDialogHandler:
- def __init__(self, builder: Gtk.Builder,
- policy_client: 'PolicyClientWrapper',
- triggered_func: Callable):
+ def __init__(
+ self,
+ builder: Gtk.Builder,
+ policy_client: "PolicyClientWrapper",
+ triggered_func: Callable,
+ ):
self.policy_client = policy_client
self.triggered_func = triggered_func
- self.dialog_window: Gtk.Dialog = builder.get_object('open_dialog')
- self.file_list: Gtk.ListBox = builder.get_object('open_policy_list')
- self.ok_button: Gtk.Button = builder.get_object('open_button_ok')
- self.cancel_button: Gtk.Button = \
- builder.get_object('open_button_cancel')
+ self.dialog_window: Gtk.Dialog = builder.get_object("open_dialog")
+ self.file_list: Gtk.ListBox = builder.get_object("open_policy_list")
+ self.ok_button: Gtk.Button = builder.get_object("open_button_ok")
+ self.cancel_button: Gtk.Button = builder.get_object(
+ "open_button_cancel"
+ )
- self.file_list.connect('row-activated', self._ok)
+ self.file_list.connect("row-activated", self._ok)
# populate dialog
for file in self.policy_client.policy_list():
@@ -73,11 +85,11 @@ def __init__(self, builder: Gtk.Builder,
self.file_list.show_all()
self.dialog_window.set_modal(True)
- self.ok_button.connect('clicked', self._ok)
+ self.ok_button.connect("clicked", self._ok)
self.ok_button.set_sensitive(False)
- self.cancel_button.connect('clicked', self._cancel)
- self.file_list.connect('row-selected', self._selection_changed)
- self.dialog_window.connect('hide', self._on_hide)
+ self.cancel_button.connect("clicked", self._cancel)
+ self.file_list.connect("row-selected", self._selection_changed)
+ self.dialog_window.connect("hide", self._on_hide)
def _selection_changed(self, *_args):
if self.get_selected_file():
@@ -110,7 +122,9 @@ class PolicyClientWrapper:
wrapper for policy client that handles files with include/ prefix
transparently
"""
- INCLUDE_PREFIX = 'include/'
+
+ INCLUDE_PREFIX = "include/"
+
def __init__(self, policy_client: PolicyClient):
self.policy_client = policy_client
@@ -118,21 +132,25 @@ def policy_list(self):
"""List all policy files, prefacing those from include directory
with include/"""
file_list = self.policy_client.policy_list()
- file_list.extend(['include/' + name for name in
- self.policy_client.policy_include_list()])
+ file_list.extend(
+ [
+ "include/" + name
+ for name in self.policy_client.policy_include_list()
+ ]
+ )
return file_list
def policy_get(self, name: str) -> Tuple[str, str]:
"""Get provided policy file, return contents and token."""
if name.startswith(self.INCLUDE_PREFIX):
- name = name[len(self.INCLUDE_PREFIX):]
+ name = name[len(self.INCLUDE_PREFIX) :]
return self.policy_client.policy_include_get(name)
return self.policy_client.policy_get(name)
def policy_replace(self, name: str, content: str, token="any"):
"""Replace provided policy file."""
if name.startswith(self.INCLUDE_PREFIX):
- name = name[len(self.INCLUDE_PREFIX):]
+ name = name[len(self.INCLUDE_PREFIX) :]
self.policy_client.policy_include_replace(name, content, token)
return
self.policy_client.policy_replace(name, content, token)
@@ -142,12 +160,13 @@ class PolicyEditor(Gtk.Application):
"""
Main Gtk.Application for new qube widget.
"""
+
def __init__(self, filename: str, policy_client: PolicyClient):
- super().__init__(application_id='org.qubesos.policyeditor')
+ super().__init__(application_id="org.qubesos.policyeditor")
self.token: Optional[str] = None
self.policy_client = PolicyClientWrapper(policy_client)
self.filename = filename
- self.window_title = 'Qubes OS Policy Editor'
+ self.window_title = "Qubes OS Policy Editor"
self.action_items: Dict[str, Gio.SimpleAction] = {}
self.accel_group = Gtk.AccelGroup()
@@ -167,35 +186,40 @@ def perform_setup(self):
"""
The function that performs actual widget realization and setup.
"""
- self.clipboard: Gtk.Clipboard = \
- Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
+ self.clipboard: Gtk.Clipboard = Gtk.Clipboard.get(
+ Gdk.SELECTION_CLIPBOARD
+ )
self.builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'policy_editor.glade')
+ glade_ref = (
+ importlib.resources.files("qubes_config") / "policy_editor.glade"
+ )
with importlib.resources.as_file(glade_ref) as path:
self.builder.add_from_file(str(path))
- self.file_select_handler = OpenDialogHandler(self.builder,
- self.policy_client,
- self.open_policy_file)
+ self.file_select_handler = OpenDialogHandler(
+ self.builder, self.policy_client, self.open_policy_file
+ )
- self.main_window : Gtk.ApplicationWindow = \
- self.builder.get_object('main_window')
+ self.main_window: Gtk.ApplicationWindow = self.builder.get_object(
+ "main_window"
+ )
# Reserving 3 pixels for window border on all sides (most themes use 2)
# Reserving 32x2 pixels for taskbar (default on top) and Window title.
# Setting minimum supported size
- self.main_window.set_size_request(1024 - 3*2, 768 - 3*2 - 32*2)
+ self.main_window.set_size_request(1024 - 3 * 2, 768 - 3 * 2 - 32 * 2)
width = min(1920, self.main_window.get_screen().get_width())
- height= min(1280, self.main_window.get_screen().get_height())
- self.main_window.set_default_size(width - 3*2, height - 3*2 - 32*2)
+ height = min(1280, self.main_window.get_screen().get_height())
+ self.main_window.set_default_size(
+ width - 3 * 2, height - 3 * 2 - 32 * 2
+ )
# ToDo: Considering maximizing by default as it packs too much info.
# self.main_window.maximize()
# setup source and help
- header_box: Gtk.Box = self.builder.get_object('header_box')
+ header_box: Gtk.Box = self.builder.get_object("header_box")
self.header_view = GtkSource.View()
self.header_buffer: GtkSource.Buffer = self.header_view.get_buffer()
header_box.pack_start(self.header_view, True, True, 0)
@@ -205,8 +229,9 @@ def perform_setup(self):
self.header_view.set_monospace(True)
self.header_view.set_editable(False)
- self.source_viewport: Gtk.Viewport = \
- self.builder.get_object('source_viewport')
+ self.source_viewport: Gtk.Viewport = self.builder.get_object(
+ "source_viewport"
+ )
self.source_view = GtkSource.View()
self.source_buffer: GtkSource.Buffer = self.source_view.get_buffer()
@@ -214,32 +239,39 @@ def perform_setup(self):
self.source_view.set_hexpand(True)
self.source_viewport.add(self.source_view)
- self.help_window: Gtk.ScrolledWindow = \
- self.builder.get_object('help_window')
- self.about_window: Gtk.AboutDialog = \
- self.builder.get_object('about_window')
- self.about_window.connect('response',
- lambda *_args: self.about_window.hide())
- self.about_window.connect('activate-link',
- self._open_docs)
+ self.help_window: Gtk.ScrolledWindow = self.builder.get_object(
+ "help_window"
+ )
+ self.about_window: Gtk.AboutDialog = self.builder.get_object(
+ "about_window"
+ )
+ self.about_window.connect(
+ "response", lambda *_args: self.about_window.hide()
+ )
+ self.about_window.connect("activate-link", self._open_docs)
- self.error_info: Gtk.Label = self.builder.get_object('error_info')
+ self.error_info: Gtk.Label = self.builder.get_object("error_info")
- self.menu_bar: Gtk.MenuBar = self.builder.get_object('menubar')
+ self.menu_bar: Gtk.MenuBar = self.builder.get_object("menubar")
- self.main_window.connect('delete-event', self._ask_to_quit)
+ self.main_window.connect("delete-event", self._ask_to_quit)
self.setup_actions()
self.setup_menu()
- load_theme(widget=self.main_window, package_name='qubes_config',
- light_file_name='qubes-policy-editor-light.css',
- dark_file_name='qubes-policy-editor-dark.css')
+ load_theme(
+ widget=self.main_window,
+ package_name="qubes_config",
+ light_file_name="qubes-policy-editor-light.css",
+ dark_file_name="qubes-policy-editor-dark.css",
+ )
self.setup_source()
- help_text = importlib.resources.files(
- 'qubes_config').joinpath(
- 'policy_editor/policy_help.txt').read_text()
+ help_text = (
+ importlib.resources.files("qubes_config")
+ .joinpath("policy_editor/policy_help.txt")
+ .read_text()
+ )
self.builder.get_object("help_label").set_markup(help_text)
self.open_policy_file(self.filename)
@@ -264,7 +296,8 @@ def setup_actions(self):
"reset": self._reset,
"about": self._about,
"help": self._toggle_help,
- "save_exit": self._save_exit}
+ "save_exit": self._save_exit,
+ }
for name, func in actions.items():
action: Gio.SimpleAction = Gio.SimpleAction.new(name, None)
@@ -277,38 +310,46 @@ def setup_menu(self):
file_menu = Gtk.Menu()
file_item = self._get_menu_item("_File")
file_item.set_submenu(file_menu)
- file_menu.add(self._get_menu_item_with_ac(
- "_New", "win.new", Gdk.KEY_n))
- file_menu.add(self._get_menu_item_with_ac(
- "_Open", "win.open", Gdk.KEY_o))
- file_menu.add(self._get_menu_item_with_ac(
- "_Save", "win.save", Gdk.KEY_s))
- file_menu.add(self._get_menu_item_with_ac(
- "_Quit", "win.quit", Gdk.KEY_q))
+ file_menu.add(self._get_menu_item_with_ac("_New", "win.new", Gdk.KEY_n))
+ file_menu.add(
+ self._get_menu_item_with_ac("_Open", "win.open", Gdk.KEY_o)
+ )
+ file_menu.add(
+ self._get_menu_item_with_ac("_Save", "win.save", Gdk.KEY_s)
+ )
+ file_menu.add(
+ self._get_menu_item_with_ac("_Quit", "win.quit", Gdk.KEY_q)
+ )
self.menu_bar.add(file_item)
# Edit
edit_menu = Gtk.Menu()
edit_item = self._get_menu_item("_Edit")
edit_item.set_submenu(edit_menu)
- edit_menu.add(self._get_menu_item_with_ac(
- "_Redo", "win.redo", Gdk.KEY_y))
- edit_menu.add(self._get_menu_item_with_ac(
- "_Undo", "win.undo", Gdk.KEY_z))
- edit_menu.add(self._get_menu_item_with_ac(
- "_Copy", "win.copy", Gdk.KEY_c))
- edit_menu.add(self._get_menu_item_with_ac(
- "_Paste", "win.paste", Gdk.KEY_v))
- edit_menu.add(self._get_menu_item_with_ac(
- "Re_set", "win.reset", None))
+ edit_menu.add(
+ self._get_menu_item_with_ac("_Redo", "win.redo", Gdk.KEY_y)
+ )
+ edit_menu.add(
+ self._get_menu_item_with_ac("_Undo", "win.undo", Gdk.KEY_z)
+ )
+ edit_menu.add(
+ self._get_menu_item_with_ac("_Copy", "win.copy", Gdk.KEY_c)
+ )
+ edit_menu.add(
+ self._get_menu_item_with_ac("_Paste", "win.paste", Gdk.KEY_v)
+ )
+ edit_menu.add(self._get_menu_item_with_ac("Re_set", "win.reset", None))
self.menu_bar.add(edit_item)
# About
help_menu = Gtk.Menu()
help_item = self._get_menu_item("_Help")
help_item.set_submenu(help_menu)
- help_menu.add(self._get_menu_item_with_ac(
- "_Show/Hide Help", "win.help", Gdk.KEY_h))
+ help_menu.add(
+ self._get_menu_item_with_ac(
+ "_Show/Hide Help", "win.help", Gdk.KEY_h
+ )
+ )
help_menu.add(self._get_menu_item_with_ac("_About", "win.about", None))
self.menu_bar.add(help_item)
@@ -323,26 +364,33 @@ def _get_menu_item(name) -> Gtk.MenuItem:
def _get_menu_item_with_ac(self, name: str, action: str, key):
item = self._get_menu_item(name)
if key:
- item.add_accelerator("activate", self.accel_group, key,
- Gdk.ModifierType.CONTROL_MASK,
- Gtk.AccelFlags.VISIBLE)
+ item.add_accelerator(
+ "activate",
+ self.accel_group,
+ key,
+ Gdk.ModifierType.CONTROL_MASK,
+ Gtk.AccelFlags.VISIBLE,
+ )
item.set_action_name(action)
return item
def setup_source(self):
lang_manager = GtkSource.LanguageManager()
- self.source_buffer.set_language(lang_manager.get_language('qubes-rpc'))
+ self.source_buffer.set_language(lang_manager.get_language("qubes-rpc"))
self.source_buffer.set_highlight_syntax(True)
self.source_view.set_show_line_numbers(True)
self.source_view.set_input_hints(
- self.source_view.get_input_hints() | Gtk.InputHints.NO_EMOJI)
+ self.source_view.get_input_hints() | Gtk.InputHints.NO_EMOJI
+ )
self.source_view.set_monospace(True)
- self.source_buffer.connect('changed', self._text_changed)
- self.source_buffer.get_undo_manager().connect('can-redo-changed',
- self._redo_changed)
- self.source_buffer.get_undo_manager().connect('can-undo-changed',
- self._undo_changed)
+ self.source_buffer.connect("changed", self._text_changed)
+ self.source_buffer.get_undo_manager().connect(
+ "can-redo-changed", self._redo_changed
+ )
+ self.source_buffer.get_undo_manager().connect(
+ "can-undo-changed", self._undo_changed
+ )
style_manager = GtkSource.StyleSchemeManager()
if is_theme_light(self.main_window):
@@ -358,7 +406,8 @@ def _ask_to_quit(self, *_args):
response = ask_question(
self.main_window,
"Unsaved changes found",
- "Do you want to save changes before exiting?")
+ "Do you want to save changes before exiting?",
+ )
if response == Gtk.ResponseType.YES:
if not self._save():
return True
@@ -371,30 +420,34 @@ def _quit(self, *_args):
self.quit()
def _new(self, *_args):
- if self.action_items['save'].get_enabled():
+ if self.action_items["save"].get_enabled():
response = ask_question(
self.main_window,
"Unsaved changes found",
- "Do you want to save changes before creating a new file?")
+ "Do you want to save changes before creating a new file?",
+ )
if response == Gtk.ResponseType.YES:
if not self._save():
return
elif response == Gtk.ResponseType.CANCEL:
return
- ask_dialog = Gtk.MessageDialog(transient_for=self.main_window,
- modal=True,
- message_type=Gtk.MessageType.QUESTION,
- buttons=Gtk.ButtonsType.OK_CANCEL,
- text="Name of the new policy file:")
+ ask_dialog = Gtk.MessageDialog(
+ transient_for=self.main_window,
+ modal=True,
+ message_type=Gtk.MessageType.QUESTION,
+ buttons=Gtk.ButtonsType.OK_CANCEL,
+ text="Name of the new policy file:",
+ )
ask_dialog.set_title("New file")
entry = Gtk.Entry()
ask_dialog.get_content_area().pack_end(entry, False, False, 0)
# manually connect Enter to closing the window
- entry.connect('activate', lambda *_args:
- ask_dialog.response(Gtk.ResponseType.OK))
+ entry.connect(
+ "activate", lambda *_args: ask_dialog.response(Gtk.ResponseType.OK)
+ )
ask_dialog.show_all()
try:
@@ -406,31 +459,38 @@ def _new(self, *_args):
ask_dialog.destroy()
# validation - only alphanumerics and - _
- if not re.compile(r'^[\w-]+$').match(new_name):
- show_error(self.main_window, "Invalid policy file name",
- f"Invalid policy file name: {new_name}. Policy file "
- "names must contain only alphanumeric characters, "
- "underscore and hyphen.")
+ if not re.compile(r"^[\w-]+$").match(new_name):
+ show_error(
+ self.main_window,
+ "Invalid policy file name",
+ f"Invalid policy file name: {new_name}. Policy file "
+ "names must contain only alphanumeric characters, "
+ "underscore and hyphen.",
+ )
return
# try to create new file
if new_name in self.policy_client.policy_list():
- show_error(self.main_window, "File already exists",
- f"Policy file: {new_name} already exists.")
+ show_error(
+ self.main_window,
+ "File already exists",
+ f"Policy file: {new_name} already exists.",
+ )
return
self.token = "new"
self._set_policy_file(new_name, "")
-
def _save(self, *_args):
"""Save changes. If successful, return True."""
try:
- self.policy_client.policy_replace(self.filename,
- self.policy_text, self.token)
+ self.policy_client.policy_replace(
+ self.filename, self.policy_text, self.token
+ )
except subprocess.CalledProcessError as ex:
- err_msg = "An error occurred while trying to save the policy" \
- " file:\n"
+ err_msg = (
+ "An error occurred while trying to save the policy file:\n"
+ )
if ex.stdout:
err_msg += ex.stdout.decode()
else:
@@ -439,8 +499,8 @@ def _save(self, *_args):
return False
self.open_policy_file(self.filename)
self.source_buffer.set_modified(False)
- self.action_items['save'].set_enabled(False)
- self.action_items['save_exit'].set_enabled(False)
+ self.action_items["save"].set_enabled(False)
+ self.action_items["save_exit"].set_enabled(False)
return True
def _save_exit(self, *_args):
@@ -474,10 +534,9 @@ def _about(self, *_args):
def _toggle_help(self, *_args):
self.help_window.set_visible(not self.help_window.get_visible())
- def _set_policy_file(self, name,
- contents=''):
+ def _set_policy_file(self, name, contents=""):
"""If name is an empty string, disable all available edit buttons
- and ignore contents to show a generic error message"""
+ and ignore contents to show a generic error message"""
if not name:
contents = "# Create new file or open an existing one."
self.source_view.set_sensitive(False)
@@ -488,14 +547,14 @@ def _set_policy_file(self, name,
self.filename = name
self.source_buffer.begin_not_undoable_action()
self.source_buffer.set_text(contents)
- self.window_title = 'Qubes OS Policy Editor - ' + self.filename
+ self.window_title = "Qubes OS Policy Editor - " + self.filename
self.main_window.set_title(self.window_title)
self.source_buffer.set_modified(False)
self.source_buffer.end_not_undoable_action()
- self.action_items['undo'].set_enabled(False)
- self.action_items['redo'].set_enabled(False)
- self.action_items['save'].set_enabled(False)
- self.action_items['save_exit'].set_enabled(False)
+ self.action_items["undo"].set_enabled(False)
+ self.action_items["redo"].set_enabled(False)
+ self.action_items["save"].set_enabled(False)
+ self.action_items["save_exit"].set_enabled(False)
def open_policy_file(self, name: Optional[str]):
"""Open file of provided name.
@@ -507,22 +566,27 @@ def open_policy_file(self, name: Optional[str]):
"""
if name is None:
return
- if name == '':
+ if name == "":
self.token = None
- text = ''
+ text = ""
else:
try:
text, self.token = self.policy_client.policy_get(name)
except subprocess.CalledProcessError as ex:
if ex.returncode == 126:
- show_error(self.main_window, "Access denied",
- "Access denied to file {}.".format(name))
+ show_error(
+ self.main_window,
+ "Access denied",
+ "Access denied to file {}.".format(name),
+ )
response = Gtk.ResponseType.NO
else:
response = ask_question(
- self.main_window, "Policy file not found",
+ self.main_window,
+ "Policy file not found",
"File {} not found. Do you want to create a "
- "new policy file?".format(name))
+ "new policy file?".format(name),
+ )
if response == Gtk.ResponseType.YES:
# make new file
text = ""
@@ -541,52 +605,56 @@ def open_policy_file(self, name: Optional[str]):
def _text_changed(self, *_args):
errors = []
text = self.policy_text
- for lineno, line in enumerate(text.split('\n')):
- if not line or line.startswith('#') or line.startswith('!include'):
+ for lineno, line in enumerate(text.split("\n")):
+ if not line or line.startswith("#") or line.startswith("!include"):
continue
try:
- StringPolicy(policy={'__main__': line}).rules
+ StringPolicy(policy={"__main__": line}).rules
except PolicySyntaxError as ex:
- msg = str(ex).split(':', 2)[-1]
+ msg = str(ex).split(":", 2)[-1]
msg = html.escape(msg, quote=True)
- errors.append('Line ' + str(lineno + 1) + ':' + msg)
+ errors.append("Line " + str(lineno + 1) + ":" + msg)
if errors:
- self.error_info.get_style_context().remove_class('error_ok')
- self.error_info.get_style_context().add_class('error_bad')
+ self.error_info.get_style_context().remove_class("error_ok")
+ self.error_info.get_style_context().add_class("error_bad")
self.error_info.set_markup(
- 'Errors found:\n' + '\n'.join(errors))
+ "Errors found:\n" + "\n".join(errors)
+ )
else:
- self.error_info.get_style_context().remove_class('error_bad')
- self.error_info.get_style_context().add_class('error_ok')
+ self.error_info.get_style_context().remove_class("error_bad")
+ self.error_info.get_style_context().add_class("error_ok")
self.error_info.set_text("No errors found!")
if self.source_buffer.get_modified():
- self.main_window.set_title(self.window_title + ' *')
+ self.main_window.set_title(self.window_title + " *")
else:
self.main_window.set_title(self.window_title)
if not errors and self.source_buffer.get_modified():
- self.action_items['save'].set_enabled(True)
- self.action_items['save_exit'].set_enabled(True)
+ self.action_items["save"].set_enabled(True)
+ self.action_items["save_exit"].set_enabled(True)
else:
- self.action_items['save'].set_enabled(False)
- self.action_items['save_exit'].set_enabled(False)
+ self.action_items["save"].set_enabled(False)
+ self.action_items["save_exit"].set_enabled(False)
# source_buffer can_undo and can_redo always report False here
# do not use them to fix undo/redo enabledness
def _redo_changed(self, undo_manager):
- self.action_items['redo'].set_enabled(undo_manager.can_redo())
+ self.action_items["redo"].set_enabled(undo_manager.can_redo())
def _undo_changed(self, undo_manager):
- self.action_items['undo'].set_enabled(undo_manager.can_undo())
+ self.action_items["undo"].set_enabled(undo_manager.can_undo())
@property
def policy_text(self):
return self.source_buffer.get_text(
self.source_buffer.get_start_iter(),
- self.source_buffer.get_end_iter(), False)
+ self.source_buffer.get_end_iter(),
+ False,
+ )
+
def main():
"""
@@ -596,9 +664,10 @@ def main():
if len(sys.argv) > 1:
filename = sys.argv[1]
else:
- filename = ''
+ filename = ""
app = PolicyEditor(filename, policy_client)
app.run()
-if __name__ == '__main__':
+
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qubes_config/tests/conftest.py b/qubes_config/tests/conftest.py
index 103e077c..09717aa3 100644
--- a/qubes_config/tests/conftest.py
+++ b/qubes_config/tests/conftest.py
@@ -26,8 +26,9 @@
from qubesadmin.tests import QubesTest
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
from ..global_config.global_config import GlobalConfig
@@ -82,17 +83,17 @@
"template": ("vm", False, "fedora-36"),
}
-possible_tags = ['whonix-updatevm', 'anon-gateway']
+possible_tags = ["whonix-updatevm", "anon-gateway"]
-def add_expected_vm(qapp,
- name: str,
- klass: str,
- properties: Mapping[str,
- Union[bool, str, int,
- Tuple[str, bool, str]]],
- features,
- tags):
+def add_expected_vm(
+ qapp,
+ name: str,
+ klass: str,
+ properties: Mapping[str, Union[bool, str, int, Tuple[str, bool, str]]],
+ features,
+ tags,
+):
"""Generate expected_calls entries to get info about a VM
:param qapp: QubesTest object
:param name: name of the VM
@@ -104,11 +105,11 @@ def add_expected_vm(qapp,
:param tags: list of tags
:return:
"""
- vm_list_call = ('dom0', 'admin.vm.List', None, None)
- vm_list = b'0\x00'
+ vm_list_call = ("dom0", "admin.vm.List", None, None)
+ vm_list = b"0\x00"
if vm_list_call in qapp.expected_calls:
vm_list = qapp.expected_calls[vm_list_call]
- vm_list += f'{name} class={klass} state=Halted\n'.encode()
+ vm_list += f"{name} class={klass} state=Halted\n".encode()
qapp.expected_calls[vm_list_call] = vm_list
properties_getall = b"0\x00"
combined_properties = default_vm_properties.copy()
@@ -121,98 +122,128 @@ def add_expected_vm(qapp,
except KeyError:
pass
elif prop in combined_properties:
- combined_properties[prop] = \
- (combined_properties[prop][0],
- combined_properties[prop][1], str(value))
+ combined_properties[prop] = (
+ combined_properties[prop][0],
+ combined_properties[prop][1],
+ str(value),
+ )
else:
raise KeyError(f"Unknown property '{prop}'")
for prop, value in combined_properties.items():
- if prop == 'template' and klass in ("TemplateVM", "StandaloneVM"):
- qapp.expected_calls[(name, "admin.vm.property.Get", prop, None)] = \
- b'2\x00QubesNoSuchPropertyError\x00\x00No such property\x00'
+ if prop == "template" and klass in ("TemplateVM", "StandaloneVM"):
+ qapp.expected_calls[(name, "admin.vm.property.Get", prop, None)] = (
+ b"2\x00QubesNoSuchPropertyError\x00\x00No such property\x00"
+ )
continue
prop_line = f"default={value[1]} type={value[0]} {value[2]}"
properties_getall += (f"{prop} " + prop_line + "\n").encode()
- qapp.expected_calls[(name, "admin.vm.property.Get", prop, None)] = \
+ qapp.expected_calls[(name, "admin.vm.property.Get", prop, None)] = (
b"0\x00" + prop_line.encode()
-
- qapp.expected_calls[(name, "admin.vm.feature.List", None, None)] = \
- ("0\x00" + "".join(f"{feature}\n" for feature, value in
- features.items() if value is not None)).encode()
+ )
+
+ qapp.expected_calls[(name, "admin.vm.feature.List", None, None)] = (
+ "0\x00"
+ + "".join(
+ f"{feature}\n"
+ for feature, value in features.items()
+ if value is not None
+ )
+ ).encode()
for feature, value in features.items():
if value is None:
qapp.expected_calls[
- (name, "admin.vm.feature.Get", feature, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(feature).encode() + b'\x00'
+ (name, "admin.vm.feature.Get", feature, None)
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(feature).encode()
+ + b"\x00"
+ )
else:
qapp.expected_calls[
- (name, "admin.vm.feature.Get", feature, None)] = \
- b"0\x00" + str(value).encode()
+ (name, "admin.vm.feature.Get", feature, None)
+ ] = (b"0\x00" + str(value).encode())
- qapp.expected_calls[(name, "admin.vm.tag.List", None, None)] = \
- ("0\x00" + "".join(f"{tag}\n" for tag in tags)).encode()
+ qapp.expected_calls[(name, "admin.vm.tag.List", None, None)] = (
+ "0\x00" + "".join(f"{tag}\n" for tag in tags)
+ ).encode()
for tag in possible_tags:
- qapp.expected_calls[(name, "admin.vm.tag.Get", tag, None)] = \
- b"0\x000"
+ qapp.expected_calls[(name, "admin.vm.tag.Get", tag, None)] = b"0\x000"
for tag in tags:
- qapp.expected_calls[(name, "admin.vm.tag.Get", tag, None)] = \
- b"0\x001"
+ qapp.expected_calls[(name, "admin.vm.tag.Get", tag, None)] = b"0\x001"
- qapp.expected_calls[(name, "admin.vm.device.pci.List", None, None)] = \
+ qapp.expected_calls[(name, "admin.vm.device.pci.List", None, None)] = (
b"0\x00"
+ )
+
def add_dom0_vm_property(qapp, prop_name, prop_value):
"""Add a vm property to dom0"""
if not prop_value:
prop_value = ""
- qapp.expected_calls[('dom0', 'admin.property.Get', prop_name, None)] = \
- b'0\x00' + f'default=True type=vm {prop_value}'.encode()
+ qapp.expected_calls[("dom0", "admin.property.Get", prop_name, None)] = (
+ b"0\x00" + f"default=True type=vm {prop_value}".encode()
+ )
+
def add_dom0_text_property(qapp, prop_name, prop_value):
"""Add a str property to dom0"""
- qapp.expected_calls[('dom0', 'admin.property.Get', prop_name, None)] = \
- b'0\x00' + f'default=True type=str {prop_value}'.encode()
+ qapp.expected_calls[("dom0", "admin.property.Get", prop_name, None)] = (
+ b"0\x00" + f"default=True type=str {prop_value}".encode()
+ )
def add_dom0_feature(qapp, feature, feature_value):
"""Add dom0 feature"""
if feature_value is not None:
- qapp.expected_calls[('dom0', 'admin.vm.feature.Get', feature, None)] = \
- b'0\x00' + f'{feature_value}'.encode()
+ qapp.expected_calls[("dom0", "admin.vm.feature.Get", feature, None)] = (
+ b"0\x00" + f"{feature_value}".encode()
+ )
else:
- qapp.expected_calls[('dom0', 'admin.vm.feature.Get', feature, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + str(feature).encode() + b'\x00'
+ qapp.expected_calls[("dom0", "admin.vm.feature.Get", feature, None)] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(feature).encode()
+ + b"\x00"
+ )
+
-def add_feature_with_template_to_all(qapp, feature_name,
- enable_vm_names: List[str]):
+def add_feature_with_template_to_all(
+ qapp, feature_name, enable_vm_names: List[str]
+):
"""Add possibility of checking for a feature with templated to all qubes;
those listed in enabled_vm_names will have it set to 1, others will
have it absent."""
for vm in qapp.domains:
if vm.name in enable_vm_names:
- result=b'0\x001'
+ result = b"0\x001"
else:
- result = b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(feature_name).encode() + b'\x00'
- qapp.expected_calls[(vm, 'admin.vm.feature.CheckWithTemplate',
- feature_name, None)] = result
+ result = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(feature_name).encode()
+ + b"\x00"
+ )
+ qapp.expected_calls[
+ (vm, "admin.vm.feature.CheckWithTemplate", feature_name, None)
+ ] = result
+
def add_feature_to_all(qapp, feature_name, enable_vm_names: List[str]):
"""Add possibility of checking for a feature to all qubes; those listed
in enabled_vm_names will have it set to 1, others will have it absent."""
for vm in qapp.domains:
if vm.name in enable_vm_names:
- result=b'0\x001'
+ result = b"0\x001"
else:
- result = b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(feature_name).encode() + b'\x00'
- qapp.expected_calls[(vm, 'admin.vm.feature.Get',
- feature_name, None)] = result
+ result = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(feature_name).encode()
+ + b"\x00"
+ )
+ qapp.expected_calls[
+ (vm, "admin.vm.feature.Get", feature_name, None)
+ ] = result
@pytest.fixture
@@ -223,93 +254,151 @@ def test_qapp():
def test_qapp_impl():
"""Test QubesApp"""
qapp = QubesTest()
- qapp._local_name = 'dom0' # pylint: disable=protected-access
+ qapp._local_name = "dom0" # pylint: disable=protected-access
- add_dom0_vm_property(qapp, 'clockvm', 'sys-net')
- add_dom0_vm_property(qapp, 'updatevm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_netvm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_template', 'fedora-36')
- add_dom0_vm_property(qapp, 'default_dispvm', 'fedora-36')
+ add_dom0_vm_property(qapp, "clockvm", "sys-net")
+ add_dom0_vm_property(qapp, "updatevm", "sys-net")
+ add_dom0_vm_property(qapp, "default_netvm", "sys-net")
+ add_dom0_vm_property(qapp, "default_template", "fedora-36")
+ add_dom0_vm_property(qapp, "default_dispvm", "fedora-36")
- add_dom0_text_property(qapp, 'default_kernel', '1.1')
- add_dom0_text_property(qapp, 'default_pool', 'file')
+ add_dom0_text_property(qapp, "default_kernel", "1.1")
+ add_dom0_text_property(qapp, "default_pool", "file")
- add_dom0_feature(qapp, 'gui-default-allow-fullscreen', '')
- add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
- add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
+ add_dom0_feature(qapp, "gui-default-allow-fullscreen", "")
+ add_dom0_feature(qapp, "gui-default-allow-utf8-titles", "")
+ add_dom0_feature(qapp, "gui-default-trayicon-mode", "")
# setup labels
- qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
- b'0\x00red\nblue\ngreen\n'
+ qapp.expected_calls[("dom0", "admin.label.List", None, None)] = (
+ b"0\x00red\nblue\ngreen\n"
+ )
# setup pools:
- qapp.expected_calls[('dom0', 'admin.pool.List', None, None)] = \
- b'0\x00linux-kernel\nlvm\nfile\n'
- qapp.expected_calls[('dom0', 'admin.pool.volume.List',
- 'linux-kernel', None)] = \
- b'0\x001.1\nmisc\n4.2\n'
-
- add_expected_vm(qapp, 'dom0', 'AdminVM',
- {}, {'service.qubes-update-check': 1,
- 'config.default.qubes-update-check': None,
- 'config-usbvm-name': None,
- 'gui-default-secure-copy-sequence': None,
- 'gui-default-secure-paste-sequence': None
- }, [])
- add_expected_vm(qapp, 'sys-net', 'AppVM',
- {'provides_network': ('bool', False, 'True')},
- {'service.qubes-update-check': None,
- 'service.qubes-updates-proxy': 1}, [])
-
- add_expected_vm(qapp, 'sys-firewall', 'AppVM',
- {'provides_network': ('bool', False, 'True')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'sys-usb', 'AppVM',
- {},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'fedora-36', 'TemplateVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'fedora-35', 'TemplateVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'default-dvm', 'DispVM',
- {'template_for_dispvms': ('bool', False, 'True')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-vm', 'AppVM',
- {}, {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-blue', 'AppVM',
- {'label': ('str', False, 'blue')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-red', 'AppVM',
- {'label': ('str', False, 'red')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-standalone', 'StandaloneVM',
- {'label': ('str', False, 'green')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'vault', 'AppVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, [])
-
- add_feature_with_template_to_all(qapp, 'supported-service.qubes-u2f-proxy',
- ['test-vm', 'fedora-35', 'sys-usb'])
- add_feature_with_template_to_all(qapp, 'service.updates-proxy-setup',
- ['fedora-36', 'fedora-35'])
- add_feature_to_all(qapp, 'service.qubes-u2f-proxy',
- ['test-vm'])
+ qapp.expected_calls[("dom0", "admin.pool.List", None, None)] = (
+ b"0\x00linux-kernel\nlvm\nfile\n"
+ )
+ qapp.expected_calls[
+ ("dom0", "admin.pool.volume.List", "linux-kernel", None)
+ ] = b"0\x001.1\nmisc\n4.2\n"
+
+ add_expected_vm(
+ qapp,
+ "dom0",
+ "AdminVM",
+ {},
+ {
+ "service.qubes-update-check": 1,
+ "config.default.qubes-update-check": None,
+ "config-usbvm-name": None,
+ "gui-default-secure-copy-sequence": None,
+ "gui-default-secure-paste-sequence": None,
+ },
+ [],
+ )
+ add_expected_vm(
+ qapp,
+ "sys-net",
+ "AppVM",
+ {"provides_network": ("bool", False, "True")},
+ {"service.qubes-update-check": None, "service.qubes-updates-proxy": 1},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "sys-firewall",
+ "AppVM",
+ {"provides_network": ("bool", False, "True")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp, "sys-usb", "AppVM", {}, {"service.qubes-update-check": None}, []
+ )
+
+ add_expected_vm(
+ qapp,
+ "fedora-36",
+ "TemplateVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "fedora-35",
+ "TemplateVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "default-dvm",
+ "DispVM",
+ {"template_for_dispvms": ("bool", False, "True")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp, "test-vm", "AppVM", {}, {"service.qubes-update-check": None}, []
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-blue",
+ "AppVM",
+ {"label": ("str", False, "blue")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-red",
+ "AppVM",
+ {"label": ("str", False, "red")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-standalone",
+ "StandaloneVM",
+ {"label": ("str", False, "green")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "vault",
+ "AppVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_feature_with_template_to_all(
+ qapp,
+ "supported-service.qubes-u2f-proxy",
+ ["test-vm", "fedora-35", "sys-usb"],
+ )
+ add_feature_with_template_to_all(
+ qapp, "service.updates-proxy-setup", ["fedora-36", "fedora-35"]
+ )
+ add_feature_to_all(qapp, "service.qubes-u2f-proxy", ["test-vm"])
for vm in qapp.domains:
qapp.expected_calls[
- (vm.name, 'admin.vm.device.pci.Attached', None, None)] = b'0\x00'
+ (vm.name, "admin.vm.device.pci.Attached", None, None)
+ ] = b"0\x00"
return qapp
@@ -318,75 +407,116 @@ def test_qapp_impl():
def test_qapp_whonix(test_qapp): # pylint: disable=redefined-outer-name
# pylint does not understand fixtures
"""Testing qapp with whonix vms added"""
- add_expected_vm(test_qapp, 'sys-whonix', 'AppVM',
- {},
- {'service.qubes-update-check': None,
- 'service.qubes-updates-proxy': 1}, ['anon-gateway'])
- add_expected_vm(test_qapp, 'anon-whonix', 'AppVM',
- {},
- {'service.qubes-update-check': None}, ['anon-gateway'])
- add_expected_vm(test_qapp, 'whonix-gw-15', 'TemplateVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, ['whonix-updatevm'])
- add_expected_vm(test_qapp, 'whonix-gw-14', 'TemplateVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, ['whonix-updatevm'])
+ add_expected_vm(
+ test_qapp,
+ "sys-whonix",
+ "AppVM",
+ {},
+ {"service.qubes-update-check": None, "service.qubes-updates-proxy": 1},
+ ["anon-gateway"],
+ )
+ add_expected_vm(
+ test_qapp,
+ "anon-whonix",
+ "AppVM",
+ {},
+ {"service.qubes-update-check": None},
+ ["anon-gateway"],
+ )
+ add_expected_vm(
+ test_qapp,
+ "whonix-gw-15",
+ "TemplateVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ ["whonix-updatevm"],
+ )
+ add_expected_vm(
+ test_qapp,
+ "whonix-gw-14",
+ "TemplateVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ ["whonix-updatevm"],
+ )
test_qapp.domains.clear_cache()
add_feature_with_template_to_all(
- test_qapp, 'service.updates-proxy-setup',
- ['fedora-36', 'fedora-35', 'whonix-gw-15', 'whonix-gw-14'])
+ test_qapp,
+ "service.updates-proxy-setup",
+ ["fedora-36", "fedora-35", "whonix-gw-15", "whonix-gw-14"],
+ )
return test_qapp
+
@pytest.fixture
-def test_qapp_simple(): # pylint: disable=redefined-outer-name
+def test_qapp_simple(): # pylint: disable=redefined-outer-name
"""A qapp with only one template, one sys-net and that's all"""
# pylint does not understand fixtures
qapp = QubesTest()
- qapp._local_name = 'dom0' # pylint: disable=protected-access
+ qapp._local_name = "dom0" # pylint: disable=protected-access
- add_dom0_vm_property(qapp, 'clockvm', 'sys-net')
- add_dom0_vm_property(qapp, 'updatevm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_netvm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_template', 'fedora-36')
- add_dom0_vm_property(qapp, 'default_dispvm', 'fedora-36')
+ add_dom0_vm_property(qapp, "clockvm", "sys-net")
+ add_dom0_vm_property(qapp, "updatevm", "sys-net")
+ add_dom0_vm_property(qapp, "default_netvm", "sys-net")
+ add_dom0_vm_property(qapp, "default_template", "fedora-36")
+ add_dom0_vm_property(qapp, "default_dispvm", "fedora-36")
- add_dom0_text_property(qapp, 'default_kernel', '1.1')
- add_dom0_text_property(qapp, 'default_pool', 'file')
+ add_dom0_text_property(qapp, "default_kernel", "1.1")
+ add_dom0_text_property(qapp, "default_pool", "file")
- add_dom0_feature(qapp, 'gui-default-allow-fullscreen', '')
- add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
- add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
+ add_dom0_feature(qapp, "gui-default-allow-fullscreen", "")
+ add_dom0_feature(qapp, "gui-default-allow-utf8-titles", "")
+ add_dom0_feature(qapp, "gui-default-trayicon-mode", "")
# setup labels
- qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
- b'0\x00red\nblue\ngreen\n'
+ qapp.expected_calls[("dom0", "admin.label.List", None, None)] = (
+ b"0\x00red\nblue\ngreen\n"
+ )
# setup pools:
- qapp.expected_calls[('dom0', 'admin.pool.List', None, None)] = \
- b'0\x00linux-kernel\nlvm\nfile\n'
- qapp.expected_calls[('dom0', 'admin.pool.volume.List',
- 'linux-kernel', None)] = \
- b'0\x001.1\nmisc\n4.2\n'
-
- add_expected_vm(qapp, 'dom0', 'AdminVM',
- {}, {'service.qubes-update-check': 1,
- 'config.default.qubes-update-check': None,
- 'config-usbvm-name': None,
- 'gui-default-secure-copy-sequence': None,
- 'gui-default-secure-paste-sequence': None
- }, [])
- add_expected_vm(qapp, 'sys-net', 'AppVM',
- {'provides_network': ('bool', False, 'True')},
- {'service.qubes-update-check': None,
- 'service.qubes-updates-proxy': 1}, [])
-
- add_expected_vm(qapp, 'fedora-36', 'TemplateVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, [])
+ qapp.expected_calls[("dom0", "admin.pool.List", None, None)] = (
+ b"0\x00linux-kernel\nlvm\nfile\n"
+ )
+ qapp.expected_calls[
+ ("dom0", "admin.pool.volume.List", "linux-kernel", None)
+ ] = b"0\x001.1\nmisc\n4.2\n"
+
+ add_expected_vm(
+ qapp,
+ "dom0",
+ "AdminVM",
+ {},
+ {
+ "service.qubes-update-check": 1,
+ "config.default.qubes-update-check": None,
+ "config-usbvm-name": None,
+ "gui-default-secure-copy-sequence": None,
+ "gui-default-secure-paste-sequence": None,
+ },
+ [],
+ )
+ add_expected_vm(
+ qapp,
+ "sys-net",
+ "AppVM",
+ {"provides_network": ("bool", False, "True")},
+ {"service.qubes-update-check": None, "service.qubes-updates-proxy": 1},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "fedora-36",
+ "TemplateVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ [],
+ )
for vm in qapp.domains:
qapp.expected_calls[
- (vm.name, 'admin.vm.device.pci.Attached', None, None)] = b'0\x00'
+ (vm.name, "admin.vm.device.pci.Attached", None, None)
+ ] = b"0\x00"
return qapp
@@ -396,44 +526,54 @@ def test_qapp_broken(): # pylint: disable=redefined-outer-name
"""A qapp with no templates, no sys-net"""
# pylint does not understand fixtures
qapp = QubesTest()
- qapp._local_name = 'dom0' # pylint: disable=protected-access
+ qapp._local_name = "dom0" # pylint: disable=protected-access
- add_dom0_vm_property(qapp, 'clockvm', None)
- add_dom0_vm_property(qapp, 'updatevm', None)
- add_dom0_vm_property(qapp, 'default_netvm', None)
- add_dom0_vm_property(qapp, 'default_template', None)
- add_dom0_vm_property(qapp, 'default_dispvm', None)
+ add_dom0_vm_property(qapp, "clockvm", None)
+ add_dom0_vm_property(qapp, "updatevm", None)
+ add_dom0_vm_property(qapp, "default_netvm", None)
+ add_dom0_vm_property(qapp, "default_template", None)
+ add_dom0_vm_property(qapp, "default_dispvm", None)
- add_dom0_text_property(qapp, 'default_kernel', '1.1')
- add_dom0_text_property(qapp, 'default_pool', 'file')
+ add_dom0_text_property(qapp, "default_kernel", "1.1")
+ add_dom0_text_property(qapp, "default_pool", "file")
- add_dom0_feature(qapp, 'gui-default-allow-fullscreen', '')
- add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
- add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
+ add_dom0_feature(qapp, "gui-default-allow-fullscreen", "")
+ add_dom0_feature(qapp, "gui-default-allow-utf8-titles", "")
+ add_dom0_feature(qapp, "gui-default-trayicon-mode", "")
# setup labels
- qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
- b'0\x00red\nblue\ngreen\n'
+ qapp.expected_calls[("dom0", "admin.label.List", None, None)] = (
+ b"0\x00red\nblue\ngreen\n"
+ )
# setup pools:
- qapp.expected_calls[('dom0', 'admin.pool.List', None, None)] = \
- b'0\x00linux-kernel\nlvm\nfile\n'
- qapp.expected_calls[('dom0', 'admin.pool.volume.List',
- 'linux-kernel', None)] = \
- b'0\x001.1\nmisc\n4.2\n'
-
- add_expected_vm(qapp, 'dom0', 'AdminVM',
- {}, {'service.qubes-update-check': 1,
- 'config.default.qubes-update-check': None,
- 'config-usbvm-name': None,
- 'gui-default-secure-copy-sequence': None,
- 'gui-default-secure-paste-sequence': None
- }, [])
+ qapp.expected_calls[("dom0", "admin.pool.List", None, None)] = (
+ b"0\x00linux-kernel\nlvm\nfile\n"
+ )
+ qapp.expected_calls[
+ ("dom0", "admin.pool.volume.List", "linux-kernel", None)
+ ] = b"0\x001.1\nmisc\n4.2\n"
+
+ add_expected_vm(
+ qapp,
+ "dom0",
+ "AdminVM",
+ {},
+ {
+ "service.qubes-update-check": 1,
+ "config.default.qubes-update-check": None,
+ "config-usbvm-name": None,
+ "gui-default-secure-copy-sequence": None,
+ "gui-default-secure-paste-sequence": None,
+ },
+ [],
+ )
#
for vm in qapp.domains:
qapp.expected_calls[
- (vm.name, 'admin.vm.device.pci.Attached', None, None)] = b'0\x00'
+ (vm.name, "admin.vm.device.pci.Attached", None, None)
+ ] = b"0\x00"
return qapp
@@ -448,12 +588,12 @@ def test_builder():
pass
# test glade file contains very simple setup with correctly named widgets
builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'tests/test.glade')
+ glade_ref = importlib.resources.files("qubes_config") / "tests/test.glade"
with importlib.resources.as_file(glade_ref) as path:
builder.add_from_file(str(path))
return builder
+
@pytest.fixture
def real_builder():
"""Gtk builder with actual config glade file registered"""
@@ -464,8 +604,9 @@ def real_builder():
pass
# test glade file contains very simple setup with correctly named widgets
builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'global_config.glade')
+ glade_ref = (
+ importlib.resources.files("qubes_config") / "global_config.glade"
+ )
with importlib.resources.as_file(glade_ref) as path:
builder.add_from_file(str(path))
return builder
@@ -481,8 +622,7 @@ def new_qube_builder():
pass
# test glade file contains very simple setup with correctly named widgets
builder = Gtk.Builder()
- glade_ref = (importlib.resources.files('qubes_config') /
- 'new_qube.glade')
+ glade_ref = importlib.resources.files("qubes_config") / "new_qube.glade"
with importlib.resources.as_file(glade_ref) as path:
builder.add_from_file(str(path))
return builder
@@ -490,63 +630,58 @@ def new_qube_builder():
class TestPolicyClient:
"""Testing policy client that does not interact with Policy API"""
+
def __init__(self):
- self.file_tokens = {
- 'a-test': 'a',
- 'b-test': 'b'
- }
+ self.file_tokens = {"a-test": "a", "b-test": "b"}
self.files = {
- 'a-test': """Test * @anyvm @anyvm deny""",
- 'b-test': """Test * test-vm @anyvm allow\n
-Test * test-red test-blue deny"""
- }
- self.service_to_files = {
- 'Test': ['a-test', 'b-test']
- }
- self.include_file_tokens = {
- 'include-1': 'c',
- 'include-2': 'd'
+ "a-test": """Test * @anyvm @anyvm deny""",
+ "b-test": """Test * test-vm @anyvm allow\n
+Test * test-red test-blue deny""",
}
+ self.service_to_files = {"Test": ["a-test", "b-test"]}
+ self.include_file_tokens = {"include-1": "c", "include-2": "d"}
self.include_files = {
- 'include-1': """!include include/include-2""",
- 'include-2': """Test.Test +argument @anyvm @anyvm allow"""
+ "include-1": """!include include/include-2""",
+ "include-2": """Test.Test +argument @anyvm @anyvm allow""",
}
def policy_get_files(self, service_name):
"""Get files connected to a given service; does not
take into account policy_replace"""
- return self.service_to_files.get(service_name, '')
+ return self.service_to_files.get(service_name, "")
def policy_get(self, file_name):
"""Get file contents; takes into account policy_replace."""
if file_name in self.files:
return self.files[file_name], self.file_tokens[file_name]
- raise subprocess.CalledProcessError(2, 'test')
+ raise subprocess.CalledProcessError(2, "test")
def policy_include_get(self, file_name):
"""Get file contents; takes into account policy_replace."""
if file_name in self.include_files:
- return self.include_files[file_name], \
- self.include_file_tokens[file_name]
- raise subprocess.CalledProcessError(2, 'test')
+ return (
+ self.include_files[file_name],
+ self.include_file_tokens[file_name],
+ )
+ raise subprocess.CalledProcessError(2, "test")
- def policy_replace(self, filename, policy_text, token='any'):
+ def policy_replace(self, filename, policy_text, token="any"):
"""Replace file contents with provided contents."""
- if token == 'new':
+ if token == "new":
if filename in self.file_tokens:
- raise subprocess.CalledProcessError(2, 'test')
- elif token != 'any':
- if token != self.file_tokens.get(filename, ''):
- raise subprocess.CalledProcessError(2, 'test')
+ raise subprocess.CalledProcessError(2, "test")
+ elif token != "any":
+ if token != self.file_tokens.get(filename, ""):
+ raise subprocess.CalledProcessError(2, "test")
self.files[filename] = policy_text
self.file_tokens[filename] = str(len(policy_text))
- def policy_include_replace(self, filename, policy_text, token='any'):
+ def policy_include_replace(self, filename, policy_text, token="any"):
"""Replace file contents with provided contents."""
- if token != 'any':
- if token != self.include_file_tokens.get(filename, ''):
- raise subprocess.CalledProcessError(2, 'test')
+ if token != "any":
+ if token != self.include_file_tokens.get(filename, ""):
+ raise subprocess.CalledProcessError(2, "test")
self.include_files[filename] = policy_text
self.include_file_tokens[filename] = str(len(policy_text))
@@ -556,6 +691,7 @@ def policy_list(self):
def policy_include_list(self):
return list(self.include_files.keys())
+
@pytest.fixture
def test_policy_client():
"""Policy client fixture"""
diff --git a/qubes_config/tests/test_basics_handler.py b/qubes_config/tests/test_basics_handler.py
index 131e8328..118cd232 100644
--- a/qubes_config/tests/test_basics_handler.py
+++ b/qubes_config/tests/test_basics_handler.py
@@ -24,25 +24,36 @@
import os
from unittest.mock import Mock, patch
-from ..global_config.basics_handler import KernelVersion, PropertyHandler, \
- FeatureHandler, QMemManHelper, MemoryHandler, BasicSettingsHandler, \
- KernelHolder
+from ..global_config.basics_handler import (
+ KernelVersion,
+ PropertyHandler,
+ FeatureHandler,
+ QMemManHelper,
+ MemoryHandler,
+ BasicSettingsHandler,
+ KernelHolder,
+)
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
def test_kernel_sorting():
# check if the sorting does not complain when weirdly-named kernels appear
- kernels = ['1.09', '1.1', 'testkernel', '1.1a']
- assert sorted(kernels, key=KernelVersion) == \
- ['1.1', '1.1a', '1.09', 'testkernel']
+ kernels = ["1.09", "1.1", "testkernel", "1.1a"]
+ assert sorted(kernels, key=KernelVersion) == [
+ "1.1",
+ "1.1a",
+ "1.09",
+ "testkernel",
+ ]
def test_property_handler(test_qapp):
- test_vm = test_qapp.domains['test-vm']
+ test_vm = test_qapp.domains["test-vm"]
mock_holder = Mock()
mock_holder.mock_trait = test_vm
@@ -51,151 +62,152 @@ def test_property_handler(test_qapp):
handler = PropertyHandler(
qapp=test_qapp,
trait_holder=mock_holder,
- trait_name='mock_trait',
+ trait_name="mock_trait",
widget=combobox,
- readable_name='name')
+ readable_name="name",
+ )
assert handler.get_current_value() == test_vm
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('test-blue')
+ handler.widget.set_active_id("test-blue")
assert handler.is_changed()
assert handler.get_unsaved() == "name"
# and reset
handler.reset()
assert handler.get_current_value() == test_vm
- assert handler.widget.get_active_id() == 'test-vm'
+ assert handler.widget.get_active_id() == "test-vm"
assert not handler.is_changed()
# change stuff
- handler.widget.set_active_id('test-blue')
+ handler.widget.set_active_id("test-blue")
assert handler.is_changed()
assert handler.get_unsaved() == "name"
handler.save()
- assert mock_holder.mock_trait == test_qapp.domains['test-blue']
- assert handler.get_current_value() == test_qapp.domains['test-blue']
- assert handler.widget.get_active_id() == 'test-blue'
+ assert mock_holder.mock_trait == test_qapp.domains["test-blue"]
+ assert handler.get_current_value() == test_qapp.domains["test-blue"]
+ assert handler.widget.get_active_id() == "test-blue"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# when dealing with features, we need to be always using helper methods
-@patch('qubes_config.global_config.basics_handler.get_feature')
-@patch('qubes_config.global_config.basics_handler.apply_feature_change')
+@patch("qubes_config.global_config.basics_handler.get_feature")
+@patch("qubes_config.global_config.basics_handler.apply_feature_change")
def test_feature_handler(mock_apply, mock_get, test_qapp):
- trait_options = { 'a': 1, 'b': None, 'c': 2}
+ trait_options = {"a": 1, "b": None, "c": 2}
- test_vm = test_qapp.domains['test-vm']
+ test_vm = test_qapp.domains["test-vm"]
mock_get.return_value = 1
combobox = Gtk.ComboBoxText()
handler = FeatureHandler(
trait_holder=test_vm,
- trait_name='test_trait',
+ trait_name="test_trait",
widget=combobox,
options=trait_options,
- readable_name= 'name',
+ readable_name="name",
)
# is correct selected?
assert handler.get_current_value() == 1
- assert handler.widget.get_active_text() == 'a'
+ assert handler.widget.get_active_text() == "a"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('b')
- assert handler.widget.get_active_id() == 'b'
+ handler.widget.set_active_id("b")
+ assert handler.widget.get_active_id() == "b"
assert handler.is_changed()
assert handler.get_unsaved() == "name"
# and reset
handler.reset()
assert handler.get_current_value() == 1
- assert handler.widget.get_active_text() == 'a'
+ assert handler.widget.get_active_text() == "a"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('b')
+ handler.widget.set_active_id("b")
handler.save()
- mock_apply.assert_called_with(test_vm, 'test_trait', None)
- assert handler.widget.get_active_id() == 'b'
+ mock_apply.assert_called_with(test_vm, "test_trait", None)
+ assert handler.widget.get_active_id() == "b"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('c')
+ handler.widget.set_active_id("c")
handler.save()
- mock_apply.assert_called_with(test_vm, 'test_trait', 2)
- assert handler.widget.get_active_id() == 'c'
+ mock_apply.assert_called_with(test_vm, "test_trait", 2)
+ assert handler.widget.get_active_id() == "c"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# when dealing with features, we need to be always using helper methods
-@patch('qubes_config.global_config.basics_handler.get_boolean_feature')
-@patch('qubes_config.global_config.basics_handler.apply_feature_change')
+@patch("qubes_config.global_config.basics_handler.get_boolean_feature")
+@patch("qubes_config.global_config.basics_handler.apply_feature_change")
def test_bool_feature_handler(mock_apply, mock_get_bool, test_qapp):
- trait_options = { 'a': False, 'b': None, 'c': True}
+ trait_options = {"a": False, "b": None, "c": True}
- test_vm = test_qapp.domains['test-vm']
+ test_vm = test_qapp.domains["test-vm"]
mock_get_bool.return_value = True
combobox = Gtk.ComboBoxText()
handler = FeatureHandler(
trait_holder=test_vm,
- trait_name='test_trait',
+ trait_name="test_trait",
widget=combobox,
options=trait_options,
- readable_name= 'name',
- is_bool=True
+ readable_name="name",
+ is_bool=True,
)
# is correct selected?
assert handler.get_current_value()
- assert handler.widget.get_active_text() == 'c'
+ assert handler.widget.get_active_text() == "c"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('b')
- assert handler.widget.get_active_id() == 'b'
+ handler.widget.set_active_id("b")
+ assert handler.widget.get_active_id() == "b"
assert handler.is_changed()
assert handler.get_unsaved() == "name"
# and reset
handler.reset()
- assert handler.widget.get_active_text() == 'c'
+ assert handler.widget.get_active_text() == "c"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('b')
+ handler.widget.set_active_id("b")
handler.save()
- mock_apply.assert_called_with(test_vm, 'test_trait', None)
- assert handler.widget.get_active_id() == 'b'
+ mock_apply.assert_called_with(test_vm, "test_trait", None)
+ assert handler.widget.get_active_id() == "b"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
- handler.widget.set_active_id('a')
+ handler.widget.set_active_id("a")
handler.save()
- mock_apply.assert_called_with(test_vm, 'test_trait', False)
- assert handler.widget.get_active_id() == 'a'
+ mock_apply.assert_called_with(test_vm, "test_trait", False)
+ assert handler.widget.get_active_id() == "a"
assert not handler.is_changed()
assert handler.get_unsaved() == ""
def test_qmemmanhelper(tmp_path):
- f = tmp_path / 'test.ini'
- defaults = {'vm-min-mem': 200, 'dom0-mem-boost': 350}
+ f = tmp_path / "test.ini"
+ defaults = {"vm-min-mem": 200, "dom0-mem-boost": 350}
helper = QMemManHelper()
helper.QMEMMAN_CONFIG_PATH = str(f)
@@ -215,10 +227,10 @@ def test_qmemmanhelper(tmp_path):
cache-margin-factor = 1.3
"""
f.write_text(contents)
- assert helper.get_values() == {'vm-min-mem': 300, 'dom0-mem-boost': 400}
+ assert helper.get_values() == {"vm-min-mem": 300, "dom0-mem-boost": 400}
# now write some values
- helper.save_values({'vm-min-mem': 123, 'dom0-mem-boost': 321})
+ helper.save_values({"vm-min-mem": 123, "dom0-mem-boost": 321})
expected_text = """# The only section in this file
[global]
vm-min-mem = 123MiB
@@ -233,28 +245,28 @@ def test_qmemmanhelper(tmp_path):
# remove file contents, save and read values
os.remove(f)
- helper.save_values({'vm-min-mem': 22, 'dom0-mem-boost': 55})
- assert helper.get_values() == {'vm-min-mem': 22, 'dom0-mem-boost': 55}
+ helper.save_values({"vm-min-mem": 22, "dom0-mem-boost": 55})
+ assert helper.get_values() == {"vm-min-mem": 22, "dom0-mem-boost": 55}
class MockHelper(Mock):
- MINMEM_NAME = 'vm-min-mem'
- DOM0_NAME = 'dom0-mem-boost'
+ MINMEM_NAME = "vm-min-mem"
+ DOM0_NAME = "dom0-mem-boost"
def __init__(self, *args, **kwargs):
super().__init__(args, kwargs)
- self.values = {'vm-min-mem': 300, 'dom0-mem-boost': 400}
+ self.values = {"vm-min-mem": 300, "dom0-mem-boost": 400}
def get_values(self):
return self.values
def save_values(self, d):
- assert len(d) == 2 and \
- 'vm-min-mem' in d and 'dom0-mem-boost' in d
- self.values['vm-min-mem'] = d['vm-min-mem']
- self.values['dom0-mem-boost'] = d['dom0-mem-boost']
+ assert len(d) == 2 and "vm-min-mem" in d and "dom0-mem-boost" in d
+ self.values["vm-min-mem"] = d["vm-min-mem"]
+ self.values["dom0-mem-boost"] = d["dom0-mem-boost"]
-@patch('qubes_config.global_config.basics_handler.QMemManHelper', MockHelper)
+
+@patch("qubes_config.global_config.basics_handler.QMemManHelper", MockHelper)
def test_memory_handler(test_builder):
# pylint: disable=no-member
# disable complaints about missing member - we have a patched QMemMan here
@@ -263,25 +275,31 @@ def test_memory_handler(test_builder):
assert handler.dom0_memory_spin.get_value() == 400
assert handler.min_memory_spin.get_value() == 300
- assert handler.mem_helper.values == \
- {'vm-min-mem': 300, 'dom0-mem-boost': 400}
+ assert handler.mem_helper.values == {
+ "vm-min-mem": 300,
+ "dom0-mem-boost": 400,
+ }
assert not handler.is_changed()
assert handler.get_unsaved() == ""
# change stuff
handler.dom0_memory_spin.set_value(1)
handler.min_memory_spin.set_value(2)
- assert handler.mem_helper.values == \
- {'vm-min-mem': 300, 'dom0-mem-boost': 400}
+ assert handler.mem_helper.values == {
+ "vm-min-mem": 300,
+ "dom0-mem-boost": 400,
+ }
assert handler.is_changed()
- assert handler.get_unsaved() == 'Qube memory settings'
+ assert handler.get_unsaved() == "Qube memory settings"
# reset
handler.reset()
assert handler.dom0_memory_spin.get_value() == 400
assert handler.min_memory_spin.get_value() == 300
- assert handler.mem_helper.values == \
- {'vm-min-mem': 300, 'dom0-mem-boost': 400}
+ assert handler.mem_helper.values == {
+ "vm-min-mem": 300,
+ "dom0-mem-boost": 400,
+ }
assert not handler.is_changed()
assert handler.get_unsaved() == ""
@@ -289,8 +307,7 @@ def test_memory_handler(test_builder):
handler.dom0_memory_spin.set_value(10)
handler.min_memory_spin.set_value(20)
handler.save()
- assert handler.mem_helper.values == \
- {'vm-min-mem': 20, 'dom0-mem-boost': 10}
+ assert handler.mem_helper.values == {"vm-min-mem": 20, "dom0-mem-boost": 10}
assert not handler.is_changed()
assert handler.get_unsaved() == ""
@@ -299,23 +316,24 @@ def test_kernels(test_qapp):
combo = Gtk.ComboBoxText()
handler = KernelHolder(test_qapp, combo)
- assert handler.widget.get_active_text() == '1.1'
+ assert handler.widget.get_active_text() == "1.1"
assert handler.get_unsaved() == ""
# check that kernel dict is correct (see defaults in conftest)
# 1.1\nmisc\n4.2
assert handler._get_kernel_options() == {
- '1.1': '1.1',
- '4.2': '4.2',
- 'misc': 'misc',
- '(none)': None
+ "1.1": "1.1",
+ "4.2": "4.2",
+ "misc": "misc",
+ "(none)": None,
}
- handler.widget.set_active_id('(none)')
+ handler.widget.set_active_id("(none)")
assert handler.get_unsaved() == "Default kernel"
- test_qapp.expected_calls[('dom0', 'admin.property.Set',
- 'default_kernel', b'')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.property.Set", "default_kernel", b"")
+ ] = b"0\x00"
handler.save()
assert handler.get_unsaved() == ""
@@ -328,10 +346,11 @@ def test_basics_handler(real_builder, test_qapp):
# all handlers are tested above, so now just use one as example
# change clockvm
clockvm_combo: Gtk.ComboBox = real_builder.get_object(
- 'basics_clockvm_combo')
+ "basics_clockvm_combo"
+ )
initial_clockvm = clockvm_combo.get_active_id()
- assert initial_clockvm != 'test-blue'
- clockvm_combo.set_active_id('test-blue')
+ assert initial_clockvm != "test-blue"
+ clockvm_combo.set_active_id("test-blue")
assert basics_handler.get_unsaved() == "Clock qube"
@@ -340,8 +359,9 @@ def test_basics_handler(real_builder, test_qapp):
assert clockvm_combo.get_active_id() == initial_clockvm
assert basics_handler.get_unsaved() == ""
- clockvm_combo.set_active_id('test-blue')
+ clockvm_combo.set_active_id("test-blue")
- test_qapp.expected_calls[('dom0', 'admin.property.Set',
- 'clockvm', b'test-blue')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.property.Set", "clockvm", b"test-blue")
+ ] = b"0\x00"
basics_handler.save()
diff --git a/qubes_config/tests/test_conflict_handler.py b/qubes_config/tests/test_conflict_handler.py
index 1f182d98..dd5d5888 100644
--- a/qubes_config/tests/test_conflict_handler.py
+++ b/qubes_config/tests/test_conflict_handler.py
@@ -21,94 +21,129 @@
# pylint: disable=missing-function-docstring
# pylint: disable=missing-class-docstring
-from ..global_config.conflict_handler import ConflictFileListRow,\
- ConflictFileHandler
+from ..global_config.conflict_handler import (
+ ConflictFileListRow,
+ ConflictFileHandler,
+)
def test_row_normal_and_legacy():
- row_normal = ConflictFileListRow('test1')
- row_legacy = ConflictFileListRow('/etc/qubes-rpc/test')
+ row_normal = ConflictFileListRow("test1")
+ row_legacy = ConflictFileListRow("/etc/qubes-rpc/test")
- assert row_normal.get_style_context().has_class('problem_row')
- assert row_legacy.get_style_context().has_class('problem_row')
+ assert row_normal.get_style_context().has_class("problem_row")
+ assert row_legacy.get_style_context().has_class("problem_row")
- assert not row_normal.get_tooltip_text() or \
- not 'a legacy file' in row_normal.get_tooltip_text()
- assert 'a legacy file' in row_legacy.get_tooltip_text()
+ assert (
+ not row_normal.get_tooltip_text()
+ or not "a legacy file" in row_normal.get_tooltip_text()
+ )
+ assert "a legacy file" in row_legacy.get_tooltip_text()
def test_conflict_handler_simple(test_builder, test_policy_manager):
- test_policy_manager.policy_client.service_to_files['ConflictTest'] = \
- ['a', 'b', 'test', 'z']
- test_policy_manager.policy_client.service_to_files['ConflictLegacy'] = \
- ['/etc/qubes-rpc/test', 'test', 'z']
+ test_policy_manager.policy_client.service_to_files["ConflictTest"] = [
+ "a",
+ "b",
+ "test",
+ "z",
+ ]
+ test_policy_manager.policy_client.service_to_files["ConflictLegacy"] = [
+ "/etc/qubes-rpc/test",
+ "test",
+ "z",
+ ]
conflict_handler = ConflictFileHandler(
gtk_builder=test_builder,
prefix="policytest",
- service_names=['ConflictTest'],
- own_file_name='test',
- policy_manager=test_policy_manager
+ service_names=["ConflictTest"],
+ own_file_name="test",
+ policy_manager=test_policy_manager,
)
assert conflict_handler.problem_box.get_visible()
- children_labels = [str(child) for child in
- conflict_handler.problem_list.get_children()]
- assert children_labels == ['a', 'b']
+ children_labels = [
+ str(child) for child in conflict_handler.problem_list.get_children()
+ ]
+ assert children_labels == ["a", "b"]
def test_conflict_handler_legacy(test_builder, test_policy_manager):
- test_policy_manager.policy_client.service_to_files['ConflictTest'] = \
- ['a', 'b', 'test', 'z']
- test_policy_manager.policy_client.service_to_files['ConflictLegacy'] = \
- ['/etc/qubes-rpc/test', 'test', 'z']
+ test_policy_manager.policy_client.service_to_files["ConflictTest"] = [
+ "a",
+ "b",
+ "test",
+ "z",
+ ]
+ test_policy_manager.policy_client.service_to_files["ConflictLegacy"] = [
+ "/etc/qubes-rpc/test",
+ "test",
+ "z",
+ ]
conflict_handler = ConflictFileHandler(
gtk_builder=test_builder,
prefix="policytest",
- service_names=['ConflictLegacy'],
- own_file_name='test',
- policy_manager=test_policy_manager
+ service_names=["ConflictLegacy"],
+ own_file_name="test",
+ policy_manager=test_policy_manager,
)
assert conflict_handler.problem_box.get_visible()
- children_labels = [str(child) for child in
- conflict_handler.problem_list.get_children()]
- assert children_labels == ['/etc/qubes-rpc/test']
+ children_labels = [
+ str(child) for child in conflict_handler.problem_list.get_children()
+ ]
+ assert children_labels == ["/etc/qubes-rpc/test"]
def test_conflict_handler_multiple(test_builder, test_policy_manager):
- test_policy_manager.policy_client.service_to_files['ConflictTest'] = \
- ['a', 'b', 'test', 'z']
- test_policy_manager.policy_client.service_to_files['ConflictLegacy'] = \
- ['/etc/qubes-rpc/test', 'test', 'z']
+ test_policy_manager.policy_client.service_to_files["ConflictTest"] = [
+ "a",
+ "b",
+ "test",
+ "z",
+ ]
+ test_policy_manager.policy_client.service_to_files["ConflictLegacy"] = [
+ "/etc/qubes-rpc/test",
+ "test",
+ "z",
+ ]
conflict_handler = ConflictFileHandler(
gtk_builder=test_builder,
prefix="policytest",
- service_names=['ConflictTest', 'ConflictLegacy'],
- own_file_name='test',
- policy_manager=test_policy_manager
+ service_names=["ConflictTest", "ConflictLegacy"],
+ own_file_name="test",
+ policy_manager=test_policy_manager,
)
assert conflict_handler.problem_box.get_visible()
- children_labels = [str(child) for child in
- conflict_handler.problem_list.get_children()]
- assert sorted(children_labels) == sorted(['a', 'b', '/etc/qubes-rpc/test'])
+ children_labels = [
+ str(child) for child in conflict_handler.problem_list.get_children()
+ ]
+ assert sorted(children_labels) == sorted(["a", "b", "/etc/qubes-rpc/test"])
def test_conflict_handler_empty(test_builder, test_policy_manager):
- test_policy_manager.policy_client.service_to_files['ConflictTest'] = \
- ['a', 'b', 'test', 'z']
- test_policy_manager.policy_client.service_to_files['ConflictLegacy'] = \
- ['/etc/qubes-rpc/test', 'test', 'z']
+ test_policy_manager.policy_client.service_to_files["ConflictTest"] = [
+ "a",
+ "b",
+ "test",
+ "z",
+ ]
+ test_policy_manager.policy_client.service_to_files["ConflictLegacy"] = [
+ "/etc/qubes-rpc/test",
+ "test",
+ "z",
+ ]
conflict_handler = ConflictFileHandler(
gtk_builder=test_builder,
prefix="policytest",
- service_names=['ConflictTest'],
- own_file_name='a',
- policy_manager=test_policy_manager
+ service_names=["ConflictTest"],
+ own_file_name="a",
+ policy_manager=test_policy_manager,
)
assert not conflict_handler.problem_box.get_visible()
diff --git a/qubes_config/tests/test_global_config.py b/qubes_config/tests/test_global_config.py
index 93c50d5d..e311ed41 100644
--- a/qubes_config/tests/test_global_config.py
+++ b/qubes_config/tests/test_global_config.py
@@ -25,13 +25,17 @@
import time
from unittest.mock import patch
-from ..global_config.global_config import GlobalConfig, ClipboardHandler,\
- FileAccessHandler
+from ..global_config.global_config import (
+ GlobalConfig,
+ ClipboardHandler,
+ FileAccessHandler,
+)
from ..global_config.basics_handler import BasicSettingsHandler
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
# this entire file has a peculiar arrangement with mock signal registration:
@@ -40,15 +44,17 @@
# signals in "test" mode
-show_dialog_with_icon_path = \
- 'qubes_config.global_config.global_config.show_dialog_with_icon'
+show_dialog_with_icon_path = (
+ "qubes_config.global_config.global_config.show_dialog_with_icon"
+)
-@patch('subprocess.check_output')
-@patch('qubes_config.global_config.global_config.show_error')
-def test_global_config_init(mock_error, mock_subprocess,
- test_qapp, test_policy_manager, test_builder):
- mock_subprocess.return_value = b''
+@patch("subprocess.check_output")
+@patch("qubes_config.global_config.global_config.show_error")
+def test_global_config_init(
+ mock_error, mock_subprocess, test_qapp, test_policy_manager, test_builder
+):
+ mock_subprocess.return_value = b""
app = GlobalConfig(test_qapp, test_policy_manager)
# do not call do_activate - it will make Gtk confused and, in case
# of errors, spawn an entire screenful of windows
@@ -56,23 +62,31 @@ def test_global_config_init(mock_error, mock_subprocess,
assert test_builder
# switch across pages, nothing should happen
- while app.main_notebook.get_nth_page(
- app.main_notebook.get_current_page()).get_name() != 'thisdevice':
+ while (
+ app.main_notebook.get_nth_page(
+ app.main_notebook.get_current_page()
+ ).get_name()
+ != "thisdevice"
+ ):
app.main_notebook.next_page()
# find clipboard
app.main_notebook.set_current_page(0)
- while app.main_notebook.get_nth_page(
- app.main_notebook.get_current_page()).get_name() != 'clipboard':
+ while (
+ app.main_notebook.get_nth_page(
+ app.main_notebook.get_current_page()
+ ).get_name()
+ != "clipboard"
+ ):
app.main_notebook.next_page()
clipboard_page_num = app.main_notebook.get_current_page()
handler = app.get_current_page()
assert isinstance(handler, ClipboardHandler)
- assert handler.copy_combo.get_active_id() == 'default (Ctrl+Shift+C)'
- handler.copy_combo.set_active_id('Ctrl+Win+C')
+ assert handler.copy_combo.get_active_id() == "default (Ctrl+Shift+C)"
+ handler.copy_combo.set_active_id("Ctrl+Win+C")
# try to move away from page, we should get a warning
with patch(show_dialog_with_icon_path) as mock_ask:
@@ -82,35 +96,42 @@ def test_global_config_init(mock_error, mock_subprocess,
assert app.main_notebook.get_current_page() == clipboard_page_num + 1
app.main_notebook.set_current_page(clipboard_page_num)
- assert handler.copy_combo.get_active_id() == 'default (Ctrl+Shift+C)'
- handler.copy_combo.set_active_id('Ctrl+Win+C')
+ assert handler.copy_combo.get_active_id() == "default (Ctrl+Shift+C)"
+ handler.copy_combo.set_active_id("Ctrl+Win+C")
# try to move away from page, we should get a warning
- with patch(show_dialog_with_icon_path) as mock_ask, \
- patch('qubes_config.global_config.basics_handler.'
- 'apply_feature_change') as mock_apply:
+ with patch(show_dialog_with_icon_path) as mock_ask, patch(
+ "qubes_config.global_config.basics_handler.apply_feature_change"
+ ) as mock_apply:
mock_ask.return_value = Gtk.ResponseType.YES
app.main_notebook.set_current_page(clipboard_page_num + 1)
mock_apply.assert_called_with(
- test_qapp.domains['dom0'], 'gui-default-secure-copy-sequence',
- 'Ctrl-Mod4-c')
+ test_qapp.domains["dom0"],
+ "gui-default-secure-copy-sequence",
+ "Ctrl-Mod4-c",
+ )
mock_error.assert_not_called()
-@patch('subprocess.check_output')
-@patch('qubes_config.global_config.global_config.show_error')
-def test_global_config_page_change(mock_error, mock_subprocess,
- test_qapp, test_policy_manager, test_builder):
- mock_subprocess.return_value = b''
+@patch("subprocess.check_output")
+@patch("qubes_config.global_config.global_config.show_error")
+def test_global_config_page_change(
+ mock_error, mock_subprocess, test_qapp, test_policy_manager, test_builder
+):
+ mock_subprocess.return_value = b""
app = GlobalConfig(test_qapp, test_policy_manager)
# do not call do_activate - it will make Gtk confused and, in case
# of errors, spawn an entire screenful of windows
app.perform_setup()
assert test_builder
- while app.main_notebook.get_nth_page(
- app.main_notebook.get_current_page()).get_name() != 'file':
+ while (
+ app.main_notebook.get_nth_page(
+ app.main_notebook.get_current_page()
+ ).get_name()
+ != "file"
+ ):
app.main_notebook.next_page()
file_page_num = app.main_notebook.get_current_page()
@@ -124,11 +145,11 @@ def test_global_config_page_change(mock_error, mock_subprocess,
for child in handler.filecopy_handler.current_rows:
if child.editing:
child.activate()
- child.source_widget.model.select_value('sys-net')
+ child.source_widget.model.select_value("sys-net")
child.validate_and_save()
for row in handler.filecopy_handler.current_rows:
- if row.rule.source == 'sys-net':
+ if row.rule.source == "sys-net":
break
else:
assert False # didn't find the change
@@ -149,7 +170,7 @@ def test_global_config_page_change(mock_error, mock_subprocess,
# changes should not be made
for row in handler.filecopy_handler.current_rows:
- assert row.rule.source != 'sys-net'
+ assert row.rule.source != "sys-net"
# make changes and try to stick to them
handler.filecopy_handler.enable_radio.set_active(True)
@@ -158,15 +179,17 @@ def test_global_config_page_change(mock_error, mock_subprocess,
for child in handler.filecopy_handler.current_rows:
if child.editing:
child.activate()
- child.source_widget.model.select_value('sys-net')
+ child.source_widget.model.select_value("sys-net")
child.validate_and_save()
break
else:
assert False
# file should not yet exist
- assert handler.filecopy_handler.policy_file_name not in \
- test_policy_manager.policy_client.files
+ assert (
+ handler.filecopy_handler.policy_file_name
+ not in test_policy_manager.policy_client.files
+ )
# save changes
with patch(show_dialog_with_icon_path) as mock_ask:
@@ -179,17 +202,22 @@ def test_global_config_page_change(mock_error, mock_subprocess,
time.sleep(0.1)
# changes should have been done
- assert 'sys-net' in test_policy_manager.policy_client.files[
- handler.filecopy_handler.policy_file_name]
+ assert (
+ "sys-net"
+ in test_policy_manager.policy_client.files[
+ handler.filecopy_handler.policy_file_name
+ ]
+ )
mock_error.assert_not_called()
-@patch('subprocess.check_output')
-@patch('qubes_config.global_config.global_config.show_error')
-def test_global_config_failure(mock_error, mock_subprocess,
- test_qapp, test_policy_manager, test_builder):
- mock_subprocess.return_value = b''
+@patch("subprocess.check_output")
+@patch("qubes_config.global_config.global_config.show_error")
+def test_global_config_failure(
+ mock_error, mock_subprocess, test_qapp, test_policy_manager, test_builder
+):
+ mock_subprocess.return_value = b""
app = GlobalConfig(test_qapp, test_policy_manager)
# do not call do_activate - it will make Gtk confused and, in case
# of errors, spawn an entire screenful of windows
@@ -203,12 +231,12 @@ def test_global_config_failure(mock_error, mock_subprocess,
assert isinstance(handler, BasicSettingsHandler)
# change something manually
- handler.fullscreen_combo.set_active_id('disallow')
+ handler.fullscreen_combo.set_active_id("disallow")
# try to switch pages, error will occur on saving
- with patch(show_dialog_with_icon_path) as mock_ask, \
- patch('qubes_config.global_config.global_config.GLib.timeout_add') \
- as mock_timeout:
+ with patch(show_dialog_with_icon_path) as mock_ask, patch(
+ "qubes_config.global_config.global_config.GLib.timeout_add"
+ ) as mock_timeout:
mock_ask.return_value = Gtk.ResponseType.YES
app.main_notebook.next_page()
mock_ask.assert_called()
@@ -220,12 +248,16 @@ def test_global_config_failure(mock_error, mock_subprocess,
mock_timeout.assert_called()
-@patch('subprocess.check_output')
-@patch('qubes_config.global_config.global_config.show_error')
-def test_global_config_broken_system(mock_error, mock_subprocess,
- test_qapp_broken, test_policy_manager,
- test_builder):
- mock_subprocess.return_value = b''
+@patch("subprocess.check_output")
+@patch("qubes_config.global_config.global_config.show_error")
+def test_global_config_broken_system(
+ mock_error,
+ mock_subprocess,
+ test_qapp_broken,
+ test_policy_manager,
+ test_builder,
+):
+ mock_subprocess.return_value = b""
app = GlobalConfig(test_qapp_broken, test_policy_manager)
# do not call do_activate - it will make Gtk confused and, in case
# of errors, spawn an entire screenful of windows
@@ -233,26 +265,30 @@ def test_global_config_broken_system(mock_error, mock_subprocess,
assert test_builder
# switch across pages, nothing should happen
- while app.main_notebook.get_nth_page(
- app.main_notebook.get_current_page()).get_name() != 'thisdevice':
+ while (
+ app.main_notebook.get_nth_page(
+ app.main_notebook.get_current_page()
+ ).get_name()
+ != "thisdevice"
+ ):
app.main_notebook.next_page()
mock_error.assert_not_called()
-@patch('subprocess.check_output')
-@patch('qubes_config.global_config.global_config.show_error')
+@patch("subprocess.check_output")
+@patch("qubes_config.global_config.global_config.show_error")
def test_global_config_open_at(
- mock_error, mock_subprocess,
- test_qapp, test_policy_manager, real_builder):
- mock_subprocess.return_value = b''
+ mock_error, mock_subprocess, test_qapp, test_policy_manager, real_builder
+):
+ mock_subprocess.return_value = b""
app = GlobalConfig(test_qapp, test_policy_manager)
# do not call do_activate - it will make Gtk confused and, in case
# of errors, spawn an entire screenful of windows
app.perform_setup()
assert real_builder
- with patch('builtins.print', side_effect=print) as mock_print:
+ with patch("builtins.print", side_effect=print) as mock_print:
app.scroll_to_location("basics")
mock_print.assert_not_called()
app.scroll_to_location("basics#default_qubes")
diff --git a/qubes_config/tests/test_gtk_utils.py b/qubes_config/tests/test_gtk_utils.py
index fdc31acf..c9f95fd7 100644
--- a/qubes_config/tests/test_gtk_utils.py
+++ b/qubes_config/tests/test_gtk_utils.py
@@ -21,44 +21,54 @@
from unittest.mock import patch, call
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import GdkPixbuf, Gtk, Gdk
-from ..widgets.gtk_utils import load_icon, load_icon_at_gtk_size, \
- ask_question, show_error, is_theme_light
+from ..widgets.gtk_utils import (
+ load_icon,
+ load_icon_at_gtk_size,
+ ask_question,
+ show_error,
+ is_theme_light,
+)
+
def test_load_icon():
"""Test loading icon methods; tests if they don't error out and
return a pixbuf of correct size"""
# load from existing file
- icon_from_file = load_icon_at_gtk_size('../icons/question_icon.svg')
+ icon_from_file = load_icon_at_gtk_size("../icons/question_icon.svg")
# load from an existing icon
- icon_from_name = load_icon('xterm')
+ icon_from_name = load_icon("xterm")
# load from missing name
- icon_from_error = load_icon('qwertyuiop')
+ icon_from_error = load_icon("qwertyuiop")
assert (24, 24) == (icon_from_file.get_height(), icon_from_file.get_width())
assert (24, 24) == (icon_from_name.get_height(), icon_from_name.get_width())
- assert (24, 24) == \
- (icon_from_error.get_height(), icon_from_error.get_width())
+ assert (24, 24) == (
+ icon_from_error.get_height(),
+ icon_from_error.get_width(),
+ )
assert isinstance(icon_from_file, GdkPixbuf.Pixbuf)
assert isinstance(icon_from_name, GdkPixbuf.Pixbuf)
assert isinstance(icon_from_error, GdkPixbuf.Pixbuf)
+
def test_ask_question():
"""Simple test to see if the function does something
and if the function correctly executes run and destroy (instead of,
e.g., just show)"""
window = Gtk.Window()
- with patch('gi.repository.Gtk.Dialog') as mock_dialog:
+ with patch("gi.repository.Gtk.Dialog") as mock_dialog:
ask_question(window, "Text", "Text")
assert call.new().run() in mock_dialog.mock_calls
assert call.new().destroy() in mock_dialog.mock_calls
- with patch('gi.repository.Gtk.Dialog') as mock_dialog:
+ with patch("gi.repository.Gtk.Dialog") as mock_dialog:
show_error(window, "Text", "Text")
assert call.new().run() in mock_dialog.mock_calls
assert call.new().destroy() in mock_dialog.mock_calls
@@ -69,14 +79,17 @@ def test_get_theme():
# first test dark theme
screen = Gdk.Screen.get_default()
provider = Gtk.CssProvider()
- provider.load_from_data(b"""
+ provider.load_from_data(
+ b"""
label {
background: black;
color: red;
}
-""")
+"""
+ )
Gtk.StyleContext.add_provider_for_screen(
- screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
label = Gtk.Label()
@@ -86,13 +99,16 @@ def test_get_theme():
screen = Gdk.Screen.get_default()
provider = Gtk.CssProvider()
- provider.load_from_data(b"""
+ provider.load_from_data(
+ b"""
label {
background: white;
color: blue;
-}""")
+}"""
+ )
Gtk.StyleContext.add_provider_for_screen(
- screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
label = Gtk.Label()
diff --git a/qubes_config/tests/test_gtk_widgets.py b/qubes_config/tests/test_gtk_widgets.py
index 099be1c7..beaa8bce 100644
--- a/qubes_config/tests/test_gtk_widgets.py
+++ b/qubes_config/tests/test_gtk_widgets.py
@@ -21,7 +21,7 @@
# pylint: disable=missing-function-docstring
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from ..widgets import gtk_widgets
@@ -33,76 +33,76 @@ def test_token_name(test_qapp):
assert len(token_name.get_children()) == 1
child = token_name.get_children()[0]
assert isinstance(child, gtk_widgets.QubeName)
- assert child.get_style_context().has_class('qube-box-blue')
+ assert child.get_style_context().has_class("qube-box-blue")
- token_name.set_token('@anyvm')
+ token_name.set_token("@anyvm")
assert len(token_name.get_children()) == 1
child = token_name.get_children()[0]
assert isinstance(child, Gtk.Label)
- assert not child.get_style_context().has_class('qube-box-blue')
- assert child.get_style_context().has_class('qube-type')
+ assert not child.get_style_context().has_class("qube-box-blue")
+ assert child.get_style_context().has_class("qube-type")
- token_name.set_token('test-red')
+ token_name.set_token("test-red")
assert len(token_name.get_children()) == 1
child = token_name.get_children()[0]
assert isinstance(child, gtk_widgets.QubeName)
- assert not child.get_style_context().has_class('qube-box-blue')
- assert child.get_style_context().has_class('qube-box-red')
+ assert not child.get_style_context().has_class("qube-box-blue")
+ assert child.get_style_context().has_class("qube-box-red")
def test_token_name_categories(test_qapp):
- token_name = gtk_widgets.TokenName("@anyvm", test_qapp, {'@anyvm': 'Any'})
+ token_name = gtk_widgets.TokenName("@anyvm", test_qapp, {"@anyvm": "Any"})
assert len(token_name.get_children()) == 1
child = token_name.get_children()[0]
assert isinstance(child, Gtk.Label)
- assert child.get_text() == 'Any'
+ assert child.get_text() == "Any"
def test_qube_name(test_qapp):
# missing name
qube_name = gtk_widgets.QubeName(None)
assert qube_name.label.get_text() == "None"
- assert qube_name.get_style_context().has_class('qube-box-black')
- assert qube_name.get_style_context().has_class('qube-box-base')
+ assert qube_name.get_style_context().has_class("qube-box-black")
+ assert qube_name.get_style_context().has_class("qube-box-base")
- vm = test_qapp.domains['test-red']
+ vm = test_qapp.domains["test-red"]
qube_name = gtk_widgets.QubeName(vm)
assert qube_name.label.get_text() == "test-red"
- assert qube_name.get_style_context().has_class('qube-box-red')
- assert qube_name.get_style_context().has_class('qube-box-base')
+ assert qube_name.get_style_context().has_class("qube-box-red")
+ assert qube_name.get_style_context().has_class("qube-box-base")
def test_text_modeler_simple():
combobox = Gtk.ComboBoxText()
text_modeler = gtk_widgets.TextModeler(
- combobox=combobox,
- values= {'Pretty': 1, 'Ugly': 2})
+ combobox=combobox, values={"Pretty": 1, "Ugly": 2}
+ )
# expected initial setup
- assert combobox.get_active_text() == 'Pretty'
+ assert combobox.get_active_text() == "Pretty"
# select second value in combobox
combobox.set_active(1)
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() == 2
assert text_modeler.is_changed()
# reset to initial settings
text_modeler.reset()
- assert combobox.get_active_text() == 'Pretty'
+ assert combobox.get_active_text() == "Pretty"
assert text_modeler.get_selected() == 1
assert not text_modeler.is_changed()
# select second value via TextModeler
text_modeler.select_value(2)
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() == 2
assert text_modeler.is_changed()
# mark changes as saved
text_modeler.update_initial()
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() == 2
assert not text_modeler.is_changed()
@@ -112,40 +112,40 @@ def test_text_modeler_none():
combobox = Gtk.ComboBoxText()
text_modeler = gtk_widgets.TextModeler(
- combobox=combobox,
- values= {'Pretty': 1, 'Ugly': None})
+ combobox=combobox, values={"Pretty": 1, "Ugly": None}
+ )
# none will be selected as initial value if nothing else is provided
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() is None
assert not text_modeler.is_changed()
# select second value in combobox
combobox.set_active(0)
- assert combobox.get_active_text() == 'Pretty'
+ assert combobox.get_active_text() == "Pretty"
assert text_modeler.get_selected() == 1
assert text_modeler.is_changed()
# reset to initial settings
text_modeler.reset()
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() is None
assert not text_modeler.is_changed()
# select values via TextModeler
text_modeler.select_value(1)
- assert combobox.get_active_text() == 'Pretty'
+ assert combobox.get_active_text() == "Pretty"
assert text_modeler.get_selected() == 1
assert text_modeler.is_changed()
text_modeler.select_value(None)
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() is None
assert not text_modeler.is_changed()
# mark changes as saved
text_modeler.update_initial()
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() is None
assert not text_modeler.is_changed()
@@ -156,11 +156,12 @@ def test_text_modeler_missing():
text_modeler = gtk_widgets.TextModeler(
combobox=combobox,
- values= {'Good': 1, 'Ugly': 2, 'Bad': 3},
- selected_value='Very Strange')
+ values={"Good": 1, "Ugly": 2, "Bad": 3},
+ selected_value="Very Strange",
+ )
- assert combobox.get_active_text() == 'Very Strange'
- assert text_modeler.get_selected() == 'Very Strange'
+ assert combobox.get_active_text() == "Very Strange"
+ assert text_modeler.get_selected() == "Very Strange"
assert not text_modeler.is_changed()
@@ -170,10 +171,11 @@ def test_text_modeler_initial():
text_modeler = gtk_widgets.TextModeler(
combobox=combobox,
- values= {'Good': 1, 'Ugly': 2, 'Bad': 3},
- selected_value=2)
+ values={"Good": 1, "Ugly": 2, "Bad": 3},
+ selected_value=2,
+ )
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() == 2
assert not text_modeler.is_changed()
@@ -184,11 +186,11 @@ def test_text_modeler_initial_none():
text_modeler = gtk_widgets.TextModeler(
combobox=combobox,
- values= {'Pretty': 1, 'Ugly': None},
- selected_value=None
+ values={"Pretty": 1, "Ugly": None},
+ selected_value=None,
)
- assert combobox.get_active_text() == 'Ugly'
+ assert combobox.get_active_text() == "Ugly"
assert text_modeler.get_selected() is None
assert not text_modeler.is_changed()
@@ -198,28 +200,28 @@ def test_text_modeler_style_changes():
text_modeler = gtk_widgets.TextModeler(
combobox=combobox,
- values= {'Good': 1, 'Ugly': 2, 'Bad': 3},
+ values={"Good": 1, "Ugly": 2, "Bad": 3},
selected_value=1,
- style_changes=True
+ style_changes=True,
)
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
# change selected value manually
combobox.set_active(2)
- assert combobox.get_style_context().has_class('combo-changed')
+ assert combobox.get_style_context().has_class("combo-changed")
# and back to initial
combobox.set_active(0)
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
# change selected value with modeler
text_modeler.select_value(2)
- assert combobox.get_style_context().has_class('combo-changed')
+ assert combobox.get_style_context().has_class("combo-changed")
# and reset
text_modeler.reset()
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
def test_text_modeler_style_changes_none_val():
@@ -227,39 +229,41 @@ def test_text_modeler_style_changes_none_val():
text_modeler = gtk_widgets.TextModeler(
combobox=combobox,
- values= {'Good': 1, 'Ugly': None, 'Bad': 3},
+ values={"Good": 1, "Ugly": None, "Bad": 3},
selected_value=None,
- style_changes=True
+ style_changes=True,
)
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
# change selected value manually
combobox.set_active(2)
- assert combobox.get_style_context().has_class('combo-changed')
+ assert combobox.get_style_context().has_class("combo-changed")
# and back to initial
combobox.set_active(1)
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
# change selected value with modeler
text_modeler.select_value(3)
- assert combobox.get_style_context().has_class('combo-changed')
+ assert combobox.get_style_context().has_class("combo-changed")
# and reset
text_modeler.reset()
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
# change selected value with modeler v 2
text_modeler.select_value(3)
- assert combobox.get_style_context().has_class('combo-changed')
+ assert combobox.get_style_context().has_class("combo-changed")
text_modeler.select_value(None)
- assert not combobox.get_style_context().has_class('combo-changed')
+ assert not combobox.get_style_context().has_class("combo-changed")
+
#######################
### VMModeler tests ###
#######################
+
def get_selected_text(combobox: Gtk.ComboBox, col_no: int = 1):
tree_iter = combobox.get_active_iter()
model = combobox.get_model()
@@ -267,8 +271,10 @@ def get_selected_text(combobox: Gtk.ComboBox, col_no: int = 1):
# 2 is pixbuf, 3 is api_name
return model[tree_iter][col_no]
+
#### initial params
+
def test_vmmodeler_simple(test_qapp):
"""simplest vm modeler: no special params, just check it does not error
out"""
@@ -280,32 +286,31 @@ def test_vmmodeler_simple(test_qapp):
assert get_selected_text(combobox) is not None
+
def test_vmmodeler_selected(test_qapp):
"""simplest vm modeler: no special params, just check it does not error
out"""
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- initial_vm = test_qapp.domains['test-vm']
+ initial_vm = test_qapp.domains["test-vm"]
_ = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
current_value=initial_vm,
)
- assert get_selected_text(combobox) == 'test-vm'
+ assert get_selected_text(combobox) == "test-vm"
def test_vmmodeler_default(test_qapp):
"""simplest vm modeler: no special params, just check it does not error
out"""
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- default_vm = test_qapp.domains['test-blue']
+ default_vm = test_qapp.domains["test-blue"]
_ = gtk_widgets.VMListModeler(
- combobox=combobox,
- qapp=test_qapp,
- default_value=default_vm
+ combobox=combobox, qapp=test_qapp, default_value=default_vm
)
- assert get_selected_text(combobox) == 'test-blue (default)'
+ assert get_selected_text(combobox) == "test-blue (default)"
def test_vmmodeler_default_none(test_qapp):
@@ -316,41 +321,41 @@ def test_vmmodeler_default_none(test_qapp):
combobox=combobox,
qapp=test_qapp,
default_value="None",
- additional_options=gtk_widgets.NONE_CATEGORY
+ additional_options=gtk_widgets.NONE_CATEGORY,
)
- assert get_selected_text(combobox) == '(none) (default)'
+ assert get_selected_text(combobox) == "(none) (default)"
def test_vmmodeler_default_current(test_qapp):
"""simplest vm modeler: no special params, just check it does not error
out"""
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- initial_vm = test_qapp.domains['test-vm']
- default_vm = test_qapp.domains['test-blue']
+ initial_vm = test_qapp.domains["test-vm"]
+ default_vm = test_qapp.domains["test-blue"]
_ = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
current_value=initial_vm,
- default_value=default_vm
+ default_value=default_vm,
)
- assert get_selected_text(combobox) == 'test-vm'
+ assert get_selected_text(combobox) == "test-vm"
found = False
model = combobox.get_model()
for item in model:
- if item[1] == 'test-blue (default)':
+ if item[1] == "test-blue (default)":
found = True
assert found
def test_vmmodeler_filter(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- vms = ['test-vm', 'test-blue', 'test-red']
+ vms = ["test-vm", "test-blue", "test-red"]
_ = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- filter_function=lambda vm: str(vm) in vms
+ filter_function=lambda vm: str(vm) in vms,
)
selected_vms = []
@@ -366,70 +371,66 @@ def test_vmmodeler_categories_none(test_qapp):
_ = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- additional_options=gtk_widgets.NONE_CATEGORY
+ additional_options=gtk_widgets.NONE_CATEGORY,
)
- assert get_selected_text(combobox) == '(none)'
+ assert get_selected_text(combobox) == "(none)"
def test_vmmodeler_current_not_found(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.VMListModeler(
- combobox=combobox,
- qapp=test_qapp,
- current_value='RandomText'
+ combobox=combobox, qapp=test_qapp, current_value="RandomText"
)
- assert get_selected_text(combobox) == 'RandomText'
- assert modeler.get_selected() == 'RandomText'
+ assert get_selected_text(combobox) == "RandomText"
+ assert modeler.get_selected() == "RandomText"
def test_vmmodeler_str(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- vm = test_qapp.domains['test-blue']
+ vm = test_qapp.domains["test-blue"]
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='RandomText',
+ current_value="RandomText",
default_value=vm,
- additional_options=gtk_widgets.NONE_CATEGORY
+ additional_options=gtk_widgets.NONE_CATEGORY,
)
- assert str(modeler) == 'RandomText'
- modeler.select_value('test-blue')
- assert str(modeler) == 'test-blue (default)'
+ assert str(modeler) == "RandomText"
+ modeler.select_value("test-blue")
+ assert str(modeler) == "test-blue (default)"
modeler.select_value("None")
- assert str(modeler) == '(none)'
+ assert str(modeler) == "(none)"
def test_vmmodeler_simple_actions(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.VMListModeler(
- combobox=combobox,
- qapp=test_qapp,
- current_value='test-vm'
+ combobox=combobox, qapp=test_qapp, current_value="test-vm"
)
- vm = test_qapp.domains['test-vm']
- other_vm = test_qapp.domains['test-blue']
+ vm = test_qapp.domains["test-vm"]
+ other_vm = test_qapp.domains["test-blue"]
# expected initial setup
- assert combobox.get_child().get_text() == 'test-vm'
- assert get_selected_text(combobox) == 'test-vm'
+ assert combobox.get_child().get_text() == "test-vm"
+ assert get_selected_text(combobox) == "test-vm"
assert modeler.get_selected() == vm
assert not modeler.is_changed()
# select something else
- modeler.select_value('test-blue')
- assert combobox.get_child().get_text() == 'test-blue'
- assert get_selected_text(combobox) == 'test-blue'
+ modeler.select_value("test-blue")
+ assert combobox.get_child().get_text() == "test-blue"
+ assert get_selected_text(combobox) == "test-blue"
assert modeler.get_selected() == other_vm
assert modeler.is_changed()
# and reset
modeler.reset()
- assert combobox.get_child().get_text() == 'test-vm'
- assert get_selected_text(combobox) == 'test-vm'
+ assert combobox.get_child().get_text() == "test-vm"
+ assert get_selected_text(combobox) == "test-vm"
assert modeler.get_selected() == vm
assert not modeler.is_changed()
@@ -438,17 +439,17 @@ def test_vmmodeler_simple_actions(test_qapp):
assert modeler.is_changed()
# and back to start
- modeler.select_value('test-vm')
- assert combobox.get_child().get_text() == 'test-vm'
- assert get_selected_text(combobox) == 'test-vm'
+ modeler.select_value("test-vm")
+ assert combobox.get_child().get_text() == "test-vm"
+ assert get_selected_text(combobox) == "test-vm"
assert modeler.get_selected() == vm
assert not modeler.is_changed()
# select and save changes
- modeler.select_value('test-blue')
+ modeler.select_value("test-blue")
modeler.update_initial()
- assert combobox.get_child().get_text() == 'test-blue'
- assert get_selected_text(combobox) == 'test-blue'
+ assert combobox.get_child().get_text() == "test-blue"
+ assert get_selected_text(combobox) == "test-blue"
assert modeler.get_selected() == other_vm
assert not modeler.is_changed()
@@ -456,30 +457,30 @@ def test_vmmodeler_simple_actions(test_qapp):
def test_vmmodeler_actions_complex(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
additional_categories = gtk_widgets.NONE_CATEGORY.copy()
- additional_categories['@anyvm'] = 'Any Qube'
+ additional_categories["@anyvm"] = "Any Qube"
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='@anyvm',
- additional_options=additional_categories
+ current_value="@anyvm",
+ additional_options=additional_categories,
)
- vm = test_qapp.domains['test-vm']
+ vm = test_qapp.domains["test-vm"]
# expected initial setup
- assert get_selected_text(combobox) == 'Any Qube'
- assert modeler.get_selected() == '@anyvm'
+ assert get_selected_text(combobox) == "Any Qube"
+ assert modeler.get_selected() == "@anyvm"
assert not modeler.is_changed()
# select something else
- modeler.select_value('test-vm')
- assert get_selected_text(combobox) == 'test-vm'
+ modeler.select_value("test-vm")
+ assert get_selected_text(combobox) == "test-vm"
assert modeler.get_selected() == vm
assert modeler.is_changed()
# select none
modeler.select_value("None")
- assert get_selected_text(combobox) == '(none)'
+ assert get_selected_text(combobox) == "(none)"
assert modeler.get_selected() is None
assert modeler.is_changed()
@@ -493,89 +494,93 @@ def test_modeler_style_changes(test_qapp):
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='test-vm',
- style_changes=True
+ current_value="test-vm",
+ style_changes=True,
)
# switch selection back and forth
- assert not entry_box.get_style_context().has_class('combo-changed')
- modeler.select_value('test-blue')
- assert entry_box.get_style_context().has_class('combo-changed')
- modeler.select_value('test-vm')
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
+ modeler.select_value("test-blue")
+ assert entry_box.get_style_context().has_class("combo-changed")
+ modeler.select_value("test-vm")
+ assert not entry_box.get_style_context().has_class("combo-changed")
# switch and reset
- modeler.select_value('test-blue')
- assert entry_box.get_style_context().has_class('combo-changed')
+ modeler.select_value("test-blue")
+ assert entry_box.get_style_context().has_class("combo-changed")
modeler.reset()
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
# switch and update initial
- modeler.select_value('test-blue')
- assert entry_box.get_style_context().has_class('combo-changed')
+ modeler.select_value("test-blue")
+ assert entry_box.get_style_context().has_class("combo-changed")
modeler.update_initial()
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
+
def test_modeler_change_callback(test_qapp):
counter = []
+
def incr(*_args):
counter.append(1)
+
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='test-vm',
- event_callback=incr
+ current_value="test-vm",
+ event_callback=incr,
)
assert counter
counter.clear()
- modeler.select_value('test-blue')
+ modeler.select_value("test-blue")
assert counter
def test_modeler_change_callback_later(test_qapp):
counter = []
+
def incr(*_args):
counter.append(1)
+
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='test-vm',
+ current_value="test-vm",
)
assert not counter
modeler.connect_change_callback(incr)
assert not counter
- modeler.select_value('test-blue')
+ modeler.select_value("test-blue")
assert counter
+
def test_modeler_input_test(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
entry_box: Gtk.Entry = combobox.get_child()
modeler = gtk_widgets.VMListModeler(
combobox=combobox,
qapp=test_qapp,
- current_value='test-vm',
+ current_value="test-vm",
)
- entry_box.set_text('test-blue')
- assert modeler.get_selected() == test_qapp.domains['test-blue']
+ entry_box.set_text("test-blue")
+ assert modeler.get_selected() == test_qapp.domains["test-blue"]
- entry_box.set_text('test')
+ entry_box.set_text("test")
assert modeler.get_selected() is None
def test_modeler_is_available(test_qapp):
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
- vms = [test_qapp.domains['test-vm'], test_qapp.domains['test-blue']]
- other_vms = [test_qapp.domains['test-red']]
+ vms = [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
+ other_vms = [test_qapp.domains["test-red"]]
vm_modeler = gtk_widgets.VMListModeler(
- combobox=combobox,
- qapp=test_qapp,
- filter_function=lambda vm: vm in vms
+ combobox=combobox, qapp=test_qapp, filter_function=lambda vm: vm in vms
)
for vm in vms:
@@ -583,33 +588,37 @@ def test_modeler_is_available(test_qapp):
for vm in other_vms:
assert not vm_modeler.is_vm_available(vm)
+
########################
### Image List tests ###
########################
#### initial params
+
def test_imagemodeler_simple():
"""simplest image modeler: no special params, just check it does not error
out"""
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
_ = gtk_widgets.ImageListModeler(
- combobox=combobox,
- value_list={'1': {'icon': 'test', 'object': None}}
+ combobox=combobox, value_list={"1": {"icon": "test", "object": None}}
)
assert get_selected_text(combobox, 0) is not None
+
def test_imagemodeler_selected():
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
_ = gtk_widgets.ImageListModeler(
combobox=combobox,
- value_list={'a': {'icon': 'test', 'object': 1},
- 'b': {'icon': 'test', 'object': 2}},
- selected_value='b'
+ value_list={
+ "a": {"icon": "test", "object": 1},
+ "b": {"icon": "test", "object": 2},
+ },
+ selected_value="b",
)
- assert get_selected_text(combobox, 0) == 'b'
+ assert get_selected_text(combobox, 0) == "b"
def test_imagemodeler_simple_actions():
@@ -617,30 +626,31 @@ def test_imagemodeler_simple_actions():
modeler = gtk_widgets.ImageListModeler(
combobox=combobox,
- value_list={'a': {'icon': 'test', 'object': 1},
- 'b': {'icon': 'test', 'object': 2},
- 'c': {'icon': 'test', 'object': 3}
- },
- selected_value='b'
+ value_list={
+ "a": {"icon": "test", "object": 1},
+ "b": {"icon": "test", "object": 2},
+ "c": {"icon": "test", "object": 3},
+ },
+ selected_value="b",
)
# expected initial setup
- assert combobox.get_active_id() == 'b'
- assert get_selected_text(combobox, 0) == 'b'
+ assert combobox.get_active_id() == "b"
+ assert get_selected_text(combobox, 0) == "b"
assert modeler.get_selected() == 2
assert not modeler.is_changed()
# select something else
- modeler.select_name('c')
- assert combobox.get_active_id() == 'c'
- assert get_selected_text(combobox, 0) == 'c'
+ modeler.select_name("c")
+ assert combobox.get_active_id() == "c"
+ assert get_selected_text(combobox, 0) == "c"
assert modeler.get_selected() == 3
assert modeler.is_changed()
# and reset
modeler.reset()
- assert combobox.get_active_id() == 'b'
- assert get_selected_text(combobox, 0) == 'b'
+ assert combobox.get_active_id() == "b"
+ assert get_selected_text(combobox, 0) == "b"
assert modeler.get_selected() == 2
assert not modeler.is_changed()
@@ -649,17 +659,17 @@ def test_imagemodeler_simple_actions():
assert modeler.is_changed()
# and back to start
- modeler.select_name('b')
- assert combobox.get_active_id() == 'b'
- assert get_selected_text(combobox, 0) == 'b'
+ modeler.select_name("b")
+ assert combobox.get_active_id() == "b"
+ assert get_selected_text(combobox, 0) == "b"
assert modeler.get_selected() == 2
assert not modeler.is_changed()
# select and save changes
- modeler.select_name('c')
+ modeler.select_name("c")
modeler.update_initial()
- assert combobox.get_active_id() == 'c'
- assert get_selected_text(combobox, 0) == 'c'
+ assert combobox.get_active_id() == "c"
+ assert get_selected_text(combobox, 0) == "c"
assert modeler.get_selected() == 3
assert not modeler.is_changed()
@@ -669,84 +679,96 @@ def test_imagemodeler_style_changes():
entry_box = combobox.get_child()
modeler = gtk_widgets.ImageListModeler(
combobox=combobox,
- value_list={'a': {'icon': 'test', 'object': 1},
- 'b': {'icon': 'test', 'object': 2},
- 'c': {'icon': 'test', 'object': 3}
- },
- selected_value='b',
- style_changes=True
+ value_list={
+ "a": {"icon": "test", "object": 1},
+ "b": {"icon": "test", "object": 2},
+ "c": {"icon": "test", "object": 3},
+ },
+ selected_value="b",
+ style_changes=True,
)
# switch selection back and forth
- assert not entry_box.get_style_context().has_class('combo-changed')
- modeler.select_name('c')
- assert entry_box.get_style_context().has_class('combo-changed')
- modeler.select_name('b')
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
+ modeler.select_name("c")
+ assert entry_box.get_style_context().has_class("combo-changed")
+ modeler.select_name("b")
+ assert not entry_box.get_style_context().has_class("combo-changed")
# switch and reset
- modeler.select_name('c')
- assert entry_box.get_style_context().has_class('combo-changed')
+ modeler.select_name("c")
+ assert entry_box.get_style_context().has_class("combo-changed")
modeler.reset()
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
# switch and update initial
- modeler.select_name('c')
- assert entry_box.get_style_context().has_class('combo-changed')
+ modeler.select_name("c")
+ assert entry_box.get_style_context().has_class("combo-changed")
modeler.update_initial()
- assert not entry_box.get_style_context().has_class('combo-changed')
+ assert not entry_box.get_style_context().has_class("combo-changed")
def test_imagemodeler_change_callback():
counter = []
+
def incr(*_args):
counter.append(1)
+
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.ImageListModeler(
combobox=combobox,
- value_list={'a': {'icon': 'test', 'object': 1},
- 'b': {'icon': 'test', 'object': 2},
- 'c': {'icon': 'test', 'object': 3}
- },
- selected_value='a',
- event_callback=incr
+ value_list={
+ "a": {"icon": "test", "object": 1},
+ "b": {"icon": "test", "object": 2},
+ "c": {"icon": "test", "object": 3},
+ },
+ selected_value="a",
+ event_callback=incr,
)
assert counter
counter.clear()
- modeler.select_name('c')
+ modeler.select_name("c")
assert counter
def test_imagemodeler_change_callback_later():
counter = []
+
def incr(*_args):
counter.append(1)
+
combobox: Gtk.ComboBox = Gtk.ComboBox.new_with_entry()
modeler = gtk_widgets.ImageListModeler(
combobox=combobox,
- value_list={'a': {'icon': 'test', 'object': 1},
- 'b': {'icon': 'test', 'object': 2},
- 'c': {'icon': 'test', 'object': 3}
- },
- selected_value='a')
+ value_list={
+ "a": {"icon": "test", "object": 1},
+ "b": {"icon": "test", "object": 2},
+ "c": {"icon": "test", "object": 3},
+ },
+ selected_value="a",
+ )
assert not counter
modeler.connect_change_callback(incr)
assert not counter
- modeler.select_name('c')
+ modeler.select_name("c")
assert counter
#### ImageTextButton
+
def test_imagetextbutton():
clicks = []
button = gtk_widgets.ImageTextButton(
- "icon", "label", click_function=lambda *args: clicks.append(1),
- style_classes=['a_class'])
+ "icon",
+ "label",
+ click_function=lambda *args: clicks.append(1),
+ style_classes=["a_class"],
+ )
- assert button.get_style_context().has_class('a_class')
+ assert button.get_style_context().has_class("a_class")
assert not clicks
button.clicked()
assert len(clicks) == 1
@@ -754,11 +776,9 @@ def test_imagetextbutton():
# button contains two objects, label and image
assert len(button.get_child().get_children()) == 2
- button_without_label = gtk_widgets.ImageTextButton(
- "icon", None)
+ button_without_label = gtk_widgets.ImageTextButton("icon", None)
# button contains just image
assert len(button_without_label.get_child().get_children()) == 1
- insensitive_button = gtk_widgets.ImageTextButton(
- "icon", "text")
+ insensitive_button = gtk_widgets.ImageTextButton("icon", "text")
assert not insensitive_button.get_sensitive()
diff --git a/qubes_config/tests/test_new_qube/test_advanced_handler.py b/qubes_config/tests/test_new_qube/test_advanced_handler.py
index 758739d0..558dc1d2 100644
--- a/qubes_config/tests/test_new_qube/test_advanced_handler.py
+++ b/qubes_config/tests/test_new_qube/test_advanced_handler.py
@@ -60,12 +60,11 @@ def test_advanced_handler(test_qapp, new_qube_builder):
assert not handler.launch_settings_check.get_active()
assert not handler.get_launch_settings()
- handler._template_changed(None, 'fedora-35')
+ handler._template_changed(None, "fedora-35")
assert not handler.install_system_check.get_sensitive()
assert not handler.get_install_system()
-
# init ram
assert handler.initram.get_value() == 0
@@ -82,10 +81,10 @@ def test_advanced_handler(test_qapp, new_qube_builder):
# storage pool
assert handler.get_pool() is None
# defaults from conftest
- assert handler.pool.get_active_id() == 'default (file)'
+ assert handler.pool.get_active_id() == "default (file)"
- handler.pool.set_active_id('lvm')
- assert handler.get_pool() == 'lvm'
+ handler.pool.set_active_id("lvm")
+ assert handler.get_pool() == "lvm"
- handler.pool.set_active_id('default (file)')
+ handler.pool.set_active_id("default (file)")
assert handler.get_pool() is None
diff --git a/qubes_config/tests/test_new_qube/test_application_selector.py b/qubes_config/tests/test_new_qube/test_application_selector.py
index 2b1c49bb..0b0d0313 100644
--- a/qubes_config/tests/test_new_qube/test_application_selector.py
+++ b/qubes_config/tests/test_new_qube/test_application_selector.py
@@ -24,32 +24,40 @@
import time
from unittest.mock import patch
-from ...new_qube.application_selector import ApplicationBoxHandler, \
- ApplicationButton, AddButton, ApplicationRow
+from ...new_qube.application_selector import (
+ ApplicationBoxHandler,
+ ApplicationButton,
+ AddButton,
+ ApplicationRow,
+)
from ...new_qube.template_handler import TemplateHandler
-
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_app_handler(mock_subprocess, test_qapp, new_qube_builder):
def mock_output(command):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return b"test.desktop|Test App|"
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
mock_subprocess.side_effect = mock_output
template_handler = TemplateHandler(new_qube_builder, test_qapp)
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-36']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-36"]
+ )
app_selector = ApplicationBoxHandler(new_qube_builder, template_handler)
# default template is selected at start, so:
@@ -59,11 +67,11 @@ def mock_output(command):
for child in app_selector.flowbox.get_children():
if isinstance(child, ApplicationButton):
- assert child.appdata.name == 'Firefox'
+ assert child.appdata.name == "Firefox"
continue
assert isinstance(child, AddButton)
- assert app_selector.get_selected_apps() == ['firefox.desktop']
+ assert app_selector.get_selected_apps() == ["firefox.desktop"]
available_apps = []
for row in app_selector.apps_list.get_children():
@@ -71,29 +79,34 @@ def mock_output(command):
available_apps.append(row.appdata.ident)
# order depends on selection
- assert available_apps == ['firefox.desktop','egg.desktop', 'test2.desktop']
+ assert available_apps == ["firefox.desktop", "egg.desktop", "test2.desktop"]
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_app_handler_show_hide(mock_subprocess, test_qapp, new_qube_builder):
def mock_output(command):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return b"test.desktop|Test App|"
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
mock_subprocess.side_effect = mock_output
template_handler = TemplateHandler(new_qube_builder, test_qapp)
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-36']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-36"]
+ )
app_selector = ApplicationBoxHandler(new_qube_builder, template_handler)
# click the plus button
@@ -109,13 +122,15 @@ def mock_output(command):
# select another row:
for row in app_selector.apps_list.get_children():
assert isinstance(row, ApplicationRow)
- if row.appdata.name == 'Egg':
+ if row.appdata.name == "Egg":
row.activate()
app_selector.apps_close.clicked()
- assert app_selector.get_selected_apps() == ['egg.desktop',
- 'firefox.desktop']
+ assert app_selector.get_selected_apps() == [
+ "egg.desktop",
+ "firefox.desktop",
+ ]
assert len(app_selector.flowbox.get_children()) == 3
# and try again, now deselect something and select something else
@@ -128,45 +143,55 @@ def mock_output(command):
for row in app_selector.apps_list.get_children():
assert isinstance(row, ApplicationRow)
- if row.appdata.name in ('Egg', 'Test2 App'):
+ if row.appdata.name in ("Egg", "Test2 App"):
row.activate()
app_selector.apps_close.clicked()
- assert app_selector.get_selected_apps() == ['firefox.desktop',
- 'test2.desktop']
+ assert app_selector.get_selected_apps() == [
+ "firefox.desktop",
+ "test2.desktop",
+ ]
assert len(app_selector.flowbox.get_children()) == 3
-@patch('subprocess.check_output')
-def test_app_handler_change_template(mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output")
+def test_app_handler_change_template(
+ mock_subprocess, test_qapp, new_qube_builder
+):
def mock_output(command):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|\n' \
- b'tomato.desktop|Tomato|basil\n' \
- b'udon.desktop|Udon|noodles\n' \
- b'spaghetti.desktop|Spaghetti|pasta'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return (
+ b"test.desktop|Test App|\n"
+ b"tomato.desktop|Tomato|basil\n"
+ b"udon.desktop|Udon|noodles\n"
+ b"spaghetti.desktop|Spaghetti|pasta"
+ )
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
mock_subprocess.side_effect = mock_output
template_handler = TemplateHandler(new_qube_builder, test_qapp)
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-36']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-36"]
+ )
app_selector = ApplicationBoxHandler(new_qube_builder, template_handler)
- assert app_selector.get_selected_apps() == ['firefox.desktop']
+ assert app_selector.get_selected_apps() == ["firefox.desktop"]
- template_handler.select_template('fedora-35')
+ template_handler.select_template("fedora-35")
for child in app_selector.flowbox.get_children():
if isinstance(child, AddButton):
@@ -177,16 +202,18 @@ def mock_output(command):
for row in app_selector.apps_list.get_children():
assert isinstance(row, ApplicationRow)
- if row.appdata.name in ('Udon', 'Spaghetti'):
+ if row.appdata.name in ("Udon", "Spaghetti"):
row.activate()
app_selector.apps_close.clicked()
- assert app_selector.get_selected_apps() == ['spaghetti.desktop',
- 'udon.desktop']
+ assert app_selector.get_selected_apps() == [
+ "spaghetti.desktop",
+ "udon.desktop",
+ ]
# default is none
- template_handler.change_vm_type('qube_type_template')
+ template_handler.change_vm_type("qube_type_template")
assert template_handler.get_selected_template() is None
assert not app_selector.flowbox.get_visible()
@@ -194,7 +221,7 @@ def mock_output(command):
assert not app_selector.get_selected_apps()
# select some apps
- template_handler.select_template('fedora-35')
+ template_handler.select_template("fedora-35")
for child in app_selector.flowbox.get_children():
if isinstance(child, AddButton):
child.activate()
@@ -204,43 +231,51 @@ def mock_output(command):
for row in app_selector.apps_list.get_children():
assert isinstance(row, ApplicationRow)
- if row.appdata.name in ('Udon', 'Spaghetti'):
+ if row.appdata.name in ("Udon", "Spaghetti"):
row.activate()
app_selector.apps_close.clicked()
- assert app_selector.get_selected_apps() == ['spaghetti.desktop',
- 'udon.desktop']
+ assert app_selector.get_selected_apps() == [
+ "spaghetti.desktop",
+ "udon.desktop",
+ ]
# and now go back to none
template_handler.select_template(None)
assert not app_selector.get_selected_apps()
-@patch('subprocess.check_output')
-def test_app_handler_do_template(mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output")
+def test_app_handler_do_template(mock_subprocess, test_qapp, new_qube_builder):
def mock_output(command):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|\n' \
- b'tomato.desktop|Tomato|basil\n' \
- b'udon.desktop|Udon|noodles\n' \
- b'spaghetti.desktop|Spaghetti|pasta'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return (
+ b"test.desktop|Test App|\n"
+ b"tomato.desktop|Tomato|basil\n"
+ b"udon.desktop|Udon|noodles\n"
+ b"spaghetti.desktop|Spaghetti|pasta"
+ )
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
mock_subprocess.side_effect = mock_output
template_handler = TemplateHandler(new_qube_builder, test_qapp)
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-36']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-36"]
+ )
app_selector = ApplicationBoxHandler(new_qube_builder, template_handler)
for child in app_selector.flowbox.get_children():
@@ -251,7 +286,7 @@ def mock_output(command):
assert False # button not found
# try to find Udon
- app_selector.apps_search.set_text('Udon')
+ app_selector.apps_search.set_text("Udon")
assert app_selector.apps_list_placeholder.get_visible()
app_selector.load_all_button.clicked()
@@ -265,46 +300,56 @@ def mock_output(command):
# the widget in ask window should have been changed to new tmpl
assert app_selector.target_template_name_widget
- assert app_selector.target_template_name_widget.vm == \
- test_qapp.domains['fedora-35']
+ assert (
+ app_selector.target_template_name_widget.vm
+ == test_qapp.domains["fedora-35"]
+ )
app_selector.change_template_ok.clicked()
break
else:
assert False # didn't find udon
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-35']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-35"]
+ )
-@patch('subprocess.check_output')
-def test_app_handler_delete(mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output")
+def test_app_handler_delete(mock_subprocess, test_qapp, new_qube_builder):
def mock_output(command):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|\n' \
- b'tomato.desktop|Tomato|basil\n' \
- b'udon.desktop|Udon|noodles\n' \
- b'spaghetti.desktop|Spaghetti|pasta'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return (
+ b"test.desktop|Test App|\n"
+ b"tomato.desktop|Tomato|basil\n"
+ b"udon.desktop|Udon|noodles\n"
+ b"spaghetti.desktop|Spaghetti|pasta"
+ )
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
mock_subprocess.side_effect = mock_output
template_handler = TemplateHandler(new_qube_builder, test_qapp)
- assert template_handler.get_selected_template() == \
- test_qapp.domains['fedora-36']
+ assert (
+ template_handler.get_selected_template()
+ == test_qapp.domains["fedora-36"]
+ )
app_selector = ApplicationBoxHandler(new_qube_builder, template_handler)
- assert app_selector.get_selected_apps() == ['firefox.desktop']
+ assert app_selector.get_selected_apps() == ["firefox.desktop"]
- template_handler.select_template('fedora-35')
+ template_handler.select_template("fedora-35")
for child in app_selector.flowbox.get_children():
if isinstance(child, AddButton):
@@ -315,20 +360,22 @@ def mock_output(command):
for row in app_selector.apps_list.get_children():
assert isinstance(row, ApplicationRow)
- if row.appdata.name in ('Udon', 'Spaghetti'):
+ if row.appdata.name in ("Udon", "Spaghetti"):
row.activate()
app_selector.apps_close.clicked()
- assert app_selector.get_selected_apps() == ['spaghetti.desktop',
- 'udon.desktop']
+ assert app_selector.get_selected_apps() == [
+ "spaghetti.desktop",
+ "udon.desktop",
+ ]
for button in app_selector.flowbox.get_children():
if isinstance(button, ApplicationButton):
- if button.appdata.ident == 'udon.desktop':
+ if button.appdata.ident == "udon.desktop":
button.activate()
break
else:
assert False # app button not found
- assert app_selector.get_selected_apps() == ['spaghetti.desktop']
+ assert app_selector.get_selected_apps() == ["spaghetti.desktop"]
diff --git a/qubes_config/tests/test_new_qube/test_network_selector.py b/qubes_config/tests/test_new_qube/test_network_selector.py
index 1377ba69..891c1900 100644
--- a/qubes_config/tests/test_new_qube/test_network_selector.py
+++ b/qubes_config/tests/test_new_qube/test_network_selector.py
@@ -24,12 +24,13 @@
import qubesadmin
from ...new_qube.network_selector import NetworkSelector
+
def test_network_handler(test_qapp, new_qube_builder):
handler = NetworkSelector(new_qube_builder, test_qapp)
assert handler.get_selected_netvm() == qubesadmin.DEFAULT
assert handler.network_current_widget.vm == test_qapp.default_netvm
- assert handler.network_current_widget.vm == test_qapp.domains['sys-net']
+ assert handler.network_current_widget.vm == test_qapp.domains["sys-net"]
assert not handler.network_current_none.get_visible()
# select none
@@ -39,13 +40,12 @@ def test_network_handler(test_qapp, new_qube_builder):
assert handler.network_current_none.get_visible()
handler.network_custom.set_active(True)
- assert handler.get_selected_netvm() != test_qapp.domains['sys-net']
- handler.network_modeler.select_value('sys-net')
+ assert handler.get_selected_netvm() != test_qapp.domains["sys-net"]
+ handler.network_modeler.select_value("sys-net")
assert not handler.network_current_none.get_visible()
- assert handler.get_selected_netvm() == test_qapp.domains['sys-net']
- assert handler.network_current_widget.vm == \
- test_qapp.domains['sys-net']
+ assert handler.get_selected_netvm() == test_qapp.domains["sys-net"]
+ assert handler.network_current_widget.vm == test_qapp.domains["sys-net"]
handler.network_default.set_active(True)
assert handler.get_selected_netvm() == qubesadmin.DEFAULT
@@ -59,7 +59,10 @@ def test_network_handler_whonix(test_qapp_whonix, new_qube_builder):
handler.network_tor.set_active(True)
- assert handler.get_selected_netvm() == \
- test_qapp_whonix.domains['sys-whonix']
- assert handler.network_current_widget.vm == \
- test_qapp_whonix.domains['sys-whonix']
+ assert (
+ handler.get_selected_netvm() == test_qapp_whonix.domains["sys-whonix"]
+ )
+ assert (
+ handler.network_current_widget.vm
+ == test_qapp_whonix.domains["sys-whonix"]
+ )
diff --git a/qubes_config/tests/test_new_qube/test_new_qube.py b/qubes_config/tests/test_new_qube/test_new_qube.py
index 18ab90fe..c38bf536 100644
--- a/qubes_config/tests/test_new_qube/test_new_qube.py
+++ b/qubes_config/tests/test_new_qube/test_new_qube.py
@@ -26,27 +26,33 @@
from ...new_qube.new_qube_app import CreateNewQube
from ...new_qube.application_selector import AddButton
+
def mock_output(command, *_args, **_kwargs):
vm_name = command[-1]
- if command[1] == '--get-available':
- if vm_name == 'fedora-35':
- return b'green.desktop|Green App|\n' \
- b'eggs.desktop|Eggs App|\n' \
- b'ham.desktop|Ham App|'
- if vm_name == 'fedora-36':
- return b'test2.desktop|Test2 App|test2 desc\n' \
- b'egg.desktop|Egg|egg\n' \
- b'firefox.desktop|Firefox|firefox'
- elif command[1] == '--get-default-whitelist':
- if vm_name == 'fedora-36':
- return b'firefox.desktop'
- return b''
-
-
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_simple_new_qube(mock_error, mock_subprocess,
- test_qapp, new_qube_builder):
+ if command[1] == "--get-available":
+ if vm_name == "fedora-35":
+ return (
+ b"green.desktop|Green App|\n"
+ b"eggs.desktop|Eggs App|\n"
+ b"ham.desktop|Ham App|"
+ )
+ if vm_name == "fedora-36":
+ return (
+ b"test2.desktop|Test2 App|test2 desc\n"
+ b"egg.desktop|Egg|egg\n"
+ b"firefox.desktop|Firefox|firefox"
+ )
+ elif command[1] == "--get-default-whitelist":
+ if vm_name == "fedora-36":
+ return b"firefox.desktop"
+ return b""
+
+
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_simple_new_qube(
+ mock_error, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -59,49 +65,62 @@ def test_simple_new_qube(mock_error, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
assert new_qube_app.create_button.get_sensitive()
# the created qube should have default template (fedora-36), default label
# (red), default networking, default apps (which is firefox here), no
# providing network
- test_qapp.expected_calls[('dom0', 'admin.vm.Create.AppVM',
- 'fedora-36', b'name=test label=red')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.Create.AppVM", "fedora-36", b"name=test label=red")
+ ] = b"0\x00"
# netvm is default
- test_qapp.expected_calls[('test', 'admin.vm.property.Reset',
- 'netvm', None)] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Reset", "netvm", None)
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=AppVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen:
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=AppVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen:
mock_popen().__enter__().returncode = 0
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
- assert call(['qvm-appmenus', '--set-whitelist', '-',
- '--update', 'test'], stdin=-1) in mock_popen.mock_calls
- assert call().__enter__().communicate(b'firefox.desktop') \
- in mock_popen.mock_calls
+ assert (
+ call(
+ ["qvm-appmenus", "--set-whitelist", "-", "--update", "test"],
+ stdin=-1,
+ )
+ in mock_popen.mock_calls
+ )
+ assert (
+ call().__enter__().communicate(b"firefox.desktop")
+ in mock_popen.mock_calls
+ )
mock_error.assert_not_called()
# no more subprocess calls
assert num_setup_calls == len(mock_subprocess.mock_calls)
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_complex_new_qube(mock_error, mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_complex_new_qube(
+ mock_error, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -113,11 +132,11 @@ def test_complex_new_qube(mock_error, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
# select label
- new_qube_app.qube_label_combo.set_active_id('green')
+ new_qube_app.qube_label_combo.set_active_id("green")
# select template and networking
- new_qube_app.template_handler.select_template('fedora-35')
+ new_qube_app.template_handler.select_template("fedora-35")
new_qube_app.network_selector.network_none.set_active(True)
# choose some apps
@@ -128,8 +147,9 @@ def test_complex_new_qube(mock_error, mock_subprocess,
else:
assert False # button not found
for row in new_qube_app.app_box_handler.apps_list.get_children():
- if hasattr(row, 'appdata') and \
- (row.appdata.name in ['Green App', 'Eggs App']):
+ if hasattr(row, "appdata") and (
+ row.appdata.name in ["Green App", "Eggs App"]
+ ):
row.activate()
new_qube_app.app_box_handler.apps_close.clicked()
@@ -138,43 +158,55 @@ def test_complex_new_qube(mock_error, mock_subprocess,
# the created qube should have template fedora-35, label green, no
# networking, green.desktop and eggs.desktop apps and not provide network
- test_qapp.expected_calls[('dom0', 'admin.vm.Create.AppVM',
- 'fedora-35',
- b'name=test label=green')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.Create.AppVM", "fedora-35", b"name=test label=green")
+ ] = b"0\x00"
# netvm is None
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'netvm', b'')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "netvm", b"")
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=AppVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen:
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=AppVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen:
mock_popen().__enter__().returncode = 0
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
- assert call(['qvm-appmenus', '--set-whitelist', '-',
- '--update', 'test'], stdin=-1) in mock_popen.mock_calls
- assert call().__enter__().communicate(b'eggs.desktop\ngreen.desktop') \
- in mock_popen.mock_calls
+ assert (
+ call(
+ ["qvm-appmenus", "--set-whitelist", "-", "--update", "test"],
+ stdin=-1,
+ )
+ in mock_popen.mock_calls
+ )
+ assert (
+ call().__enter__().communicate(b"eggs.desktop\ngreen.desktop")
+ in mock_popen.mock_calls
+ )
mock_error.assert_not_called()
# 2 additional calls were made: to list available apps and default apps
assert num_setup_calls + 2 == len(mock_subprocess.mock_calls)
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_new_template_cloned(mock_error, mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_new_template_cloned(
+ mock_error, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -186,41 +218,48 @@ def test_new_template_cloned(mock_error, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
new_qube_app.qube_type_template.clicked()
- assert new_qube_app.template_handler.selected_type == 'qube_type_template'
+ assert new_qube_app.template_handler.selected_type == "qube_type_template"
# select source for cloning
- new_qube_app.template_handler.select_template('fedora-35')
+ new_qube_app.template_handler.select_template("fedora-35")
assert new_qube_app.create_button.get_sensitive()
# the created qube should be cloned from fedora-35, have green label
# (conftest default), default networking, no apps and not provide network
- test_qapp.expected_calls[('dom0', 'admin.vm.Create.TemplateVM',
- None, b'name=test label=green')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.Create.TemplateVM", None, b"name=test label=green")
+ ] = b"0\x00"
# but as user requested red label, we replace it
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'label', b'red')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "label", b"red")
+ ] = b"0\x00"
# netvm is default
- test_qapp.expected_calls[('test', 'admin.vm.property.Reset',
- 'netvm', None)] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Reset", "netvm", None)
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
test_qapp.domains.clear_cache()
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=TemplateVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen, \
- patch.object(test_qapp, 'clone_vm') as mock_clone:
- mock_clone.return_value = test_qapp.domains['test']
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=TemplateVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen, patch.object(
+ test_qapp, "clone_vm"
+ ) as mock_clone:
+ mock_clone.return_value = test_qapp.domains["test"]
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
assert not mock_popen.mock_calls # no apps added
@@ -231,11 +270,12 @@ def test_new_template_cloned(mock_error, mock_subprocess,
assert num_setup_calls + 2 == len(mock_subprocess.mock_calls)
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('subprocess.check_call')
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_new_standalone(mock_error, mock_check_call, mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("subprocess.check_call")
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_new_standalone(
+ mock_error, mock_check_call, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -247,59 +287,74 @@ def test_new_standalone(mock_error, mock_check_call, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
new_qube_app.qube_type_standalone.clicked()
- assert new_qube_app.template_handler.selected_type == 'qube_type_standalone'
+ assert new_qube_app.template_handler.selected_type == "qube_type_standalone"
assert new_qube_app.template_handler.get_selected_template() is None
assert new_qube_app.create_button.get_sensitive()
# the created qube should be empty, have red label, default
# networking, no apps and not provide network
- test_qapp.expected_calls[('dom0', 'admin.vm.Create.StandaloneVM',
- None, b'name=test label=red')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.Create.StandaloneVM", None, b"name=test label=red")
+ ] = b"0\x00"
# it's a standalone so it should be a hvm with no kernel
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'virt_mode', b'hvm')] = b'0\x00'
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'kernel', b'')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "virt_mode", b"hvm")
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "kernel", b"")
+ ] = b"0\x00"
# netvm is default
- test_qapp.expected_calls[('test', 'admin.vm.property.Reset',
- 'netvm', None)] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Reset", "netvm", None)
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=StandaloneVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen:
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=StandaloneVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen:
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
- assert call(['qubes-vm-boot-from-device', 'test']) \
- in mock_check_call.mock_calls # called install system to qube
+ assert (
+ call(["qubes-vm-boot-from-device", "test"])
+ in mock_check_call.mock_calls
+ ) # called install system to qube
# but no apps were added
- assert call(['qvm-appmenus', '--set-whitelist', '-',
- '--update', ANY], stdin=-1) not in mock_popen.mock_calls
+ assert (
+ call(
+ ["qvm-appmenus", "--set-whitelist", "-", "--update", ANY],
+ stdin=-1,
+ )
+ not in mock_popen.mock_calls
+ )
mock_error.assert_not_called()
# no more subprocess calls, no actual new template was selected
assert num_setup_calls == len(mock_subprocess.mock_calls)
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_new_disposable(mock_error, mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_new_disposable(
+ mock_error, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -311,44 +366,55 @@ def test_new_disposable(mock_error, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
new_qube_app.qube_type_disposable.clicked()
- assert new_qube_app.template_handler.selected_type == 'qube_type_disposable'
+ assert new_qube_app.template_handler.selected_type == "qube_type_disposable"
assert new_qube_app.template_handler.get_selected_template() is None
# select something better
- new_qube_app.template_handler.select_template('default-dvm')
+ new_qube_app.template_handler.select_template("default-dvm")
assert new_qube_app.create_button.get_sensitive()
# the created qube should be empty, have red label, default
# networking, no apps and not provide network
test_qapp.expected_calls[
- ('dom0', 'admin.vm.Create.DispVM',
- 'default-dvm', b'name=test label=red')] = b'0\x00'
+ (
+ "dom0",
+ "admin.vm.Create.DispVM",
+ "default-dvm",
+ b"name=test label=red",
+ )
+ ] = b"0\x00"
# it's a standalone so it should be a hvm with no kernel
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'virt_mode', b'hvm')] = b'0\x00'
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'kernel', b'')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "virt_mode", b"hvm")
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "kernel", b"")
+ ] = b"0\x00"
# netvm is default
- test_qapp.expected_calls[('test', 'admin.vm.property.Reset',
- 'netvm', None)] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Reset", "netvm", None)
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=StandaloneVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen:
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=StandaloneVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen:
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
assert not mock_popen.mock_calls # no apps added
@@ -359,11 +425,12 @@ def test_new_disposable(mock_error, mock_subprocess,
assert num_setup_calls + 2 == len(mock_subprocess.mock_calls)
-@patch('subprocess.check_output', side_effect = mock_output)
-@patch('subprocess.check_call')
-@patch('qubes_config.new_qube.new_qube_app.show_error')
-def test_advanced_new_qube(mock_error, _mock_check_call, mock_subprocess,
- test_qapp, new_qube_builder):
+@patch("subprocess.check_output", side_effect=mock_output)
+@patch("subprocess.check_call")
+@patch("qubes_config.new_qube.new_qube_app.show_error")
+def test_advanced_new_qube(
+ mock_error, _mock_check_call, mock_subprocess, test_qapp, new_qube_builder
+):
# the builder fixture must be called to register needed signals and
# only do it once
assert new_qube_builder
@@ -376,7 +443,7 @@ def test_advanced_new_qube(mock_error, _mock_check_call, mock_subprocess,
assert not new_qube_app.create_button.get_sensitive()
# enter name
- new_qube_app.qube_name.set_text('test')
+ new_qube_app.qube_name.set_text("test")
assert new_qube_app.create_button.get_sensitive()
@@ -384,42 +451,59 @@ def test_advanced_new_qube(mock_error, _mock_check_call, mock_subprocess,
# template
new_qube_app.advanced_handler.launch_settings_check.set_active(True)
new_qube_app.advanced_handler.initram.set_value(400)
- new_qube_app.advanced_handler.pool_handler.select_value('lvm')
+ new_qube_app.advanced_handler.pool_handler.select_value("lvm")
# the created qube should have default template (fedora-36), default label
# (red), default networking, default apps (which is firefox here), no
# providing network, launch settings after creation, initram 400 and
# misc pool
test_qapp.expected_calls[
- ('dom0', 'admin.vm.CreateInPool.AppVM', 'fedora-36',
- b'name=test label=red pool=lvm')] = b'0\x00'
+ (
+ "dom0",
+ "admin.vm.CreateInPool.AppVM",
+ "fedora-36",
+ b"name=test label=red pool=lvm",
+ )
+ ] = b"0\x00"
# netvm is default
- test_qapp.expected_calls[('test', 'admin.vm.property.Reset',
- 'netvm', None)] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Reset", "netvm", None)
+ ] = b"0\x00"
# not provide network
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'provides_network', b'False')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "provides_network", b"False")
+ ] = b"0\x00"
# memory
- test_qapp.expected_calls[('test', 'admin.vm.property.Set',
- 'memory', b'400')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("test", "admin.vm.property.Set", "memory", b"400")
+ ] = b"0\x00"
# also add a call we do after adding the vm:
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
- test_qapp.expected_calls[('dom0', 'admin.vm.List', None, None)] + \
- 'test class=AppVM state=Halted\n'.encode()
-
- with patch('qubes_config.new_qube.new_qube_app.Gtk.MessageDialog') \
- as mock_dialog, patch('subprocess.Popen') as mock_popen:
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)] = (
+ test_qapp.expected_calls[("dom0", "admin.vm.List", None, None)]
+ + "test class=AppVM state=Halted\n".encode()
+ )
+
+ with patch(
+ "qubes_config.new_qube.new_qube_app.Gtk.MessageDialog"
+ ) as mock_dialog, patch("subprocess.Popen") as mock_popen:
mock_popen().__enter__().returncode = 0
new_qube_app.create_button.clicked()
assert mock_dialog.mock_calls # called to tell us about the success
- assert call(['qvm-appmenus', '--set-whitelist', '-',
- '--update', 'test'], stdin=-1) in mock_popen.mock_calls
- assert call().__enter__().communicate(b'firefox.desktop') \
- in mock_popen.mock_calls
+ assert (
+ call(
+ ["qvm-appmenus", "--set-whitelist", "-", "--update", "test"],
+ stdin=-1,
+ )
+ in mock_popen.mock_calls
+ )
+ assert (
+ call().__enter__().communicate(b"firefox.desktop")
+ in mock_popen.mock_calls
+ )
mock_error.assert_not_called()
# no more subprocess calls
diff --git a/qubes_config/tests/test_new_qube/test_template_handlers.py b/qubes_config/tests/test_new_qube/test_template_handlers.py
index 8ebceb12..c0504e04 100644
--- a/qubes_config/tests/test_new_qube/test_template_handlers.py
+++ b/qubes_config/tests/test_new_qube/test_template_handlers.py
@@ -27,54 +27,56 @@
from ...new_qube.application_selector import ApplicationData
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_template_handler_normal(mock_subprocess, test_qapp, new_qube_builder):
- mock_subprocess.return_value = b''
+ mock_subprocess.return_value = b""
handler = TemplateHandler(new_qube_builder, test_qapp)
# assert we start at app
- assert handler.selected_type == 'qube_type_app'
+ assert handler.selected_type == "qube_type_app"
# check selected template
assert handler.get_selected_template()
assert handler.get_selected_template() == test_qapp.default_template
- assert handler.get_selected_template() == test_qapp.domains['fedora-36']
+ assert handler.get_selected_template() == test_qapp.domains["fedora-36"]
# select another
- handler.select_template('fedora-35')
- assert handler.get_selected_template() == test_qapp.domains['fedora-35']
+ handler.select_template("fedora-35")
+ assert handler.get_selected_template() == test_qapp.domains["fedora-35"]
- assert handler.is_given_template_available(test_qapp.domains['fedora-36'])
- assert handler.is_given_template_available(test_qapp.domains['fedora-35'])
- assert not handler.is_given_template_available(test_qapp.domains['dom0'])
- assert not handler.is_given_template_available(test_qapp.domains['test-vm'])
+ assert handler.is_given_template_available(test_qapp.domains["fedora-36"])
+ assert handler.is_given_template_available(test_qapp.domains["fedora-35"])
+ assert not handler.is_given_template_available(test_qapp.domains["dom0"])
+ assert not handler.is_given_template_available(test_qapp.domains["test-vm"])
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_template_handler_none(mock_subprocess, test_qapp, new_qube_builder):
- mock_subprocess.return_value = b''
+ mock_subprocess.return_value = b""
handler = TemplateHandler(new_qube_builder, test_qapp)
# assert we start at app
- assert handler.selected_type == 'qube_type_app'
- handler.change_vm_type('qube_type_template')
+ assert handler.selected_type == "qube_type_app"
+ handler.change_vm_type("qube_type_template")
# templates are available
- assert handler.is_given_template_available(test_qapp.domains['fedora-36'])
- assert handler.is_given_template_available(test_qapp.domains['fedora-35'])
+ assert handler.is_given_template_available(test_qapp.domains["fedora-36"])
+ assert handler.is_given_template_available(test_qapp.domains["fedora-35"])
assert not handler.is_given_template_available(
- test_qapp.domains['test-standalone'])
- assert not handler.is_given_template_available(test_qapp.domains['dom0'])
- assert not handler.is_given_template_available(test_qapp.domains['test-vm'])
+ test_qapp.domains["test-standalone"]
+ )
+ assert not handler.is_given_template_available(test_qapp.domains["dom0"])
+ assert not handler.is_given_template_available(test_qapp.domains["test-vm"])
- radio_none = new_qube_builder.get_object('radio_template_none')
- radio_some = new_qube_builder.get_object('radio_template_template')
- combo: Gtk.ComboBox = new_qube_builder.get_object('combo_template_template')
+ radio_none = new_qube_builder.get_object("radio_template_none")
+ radio_some = new_qube_builder.get_object("radio_template_template")
+ combo: Gtk.ComboBox = new_qube_builder.get_object("combo_template_template")
# none is selected
assert handler.get_selected_template() is None
@@ -92,96 +94,98 @@ def test_template_handler_none(mock_subprocess, test_qapp, new_qube_builder):
# select something
radio_some.set_active(True)
- combo.set_active_id('fedora-36')
- assert handler.get_selected_template() == test_qapp.domains['fedora-36']
+ combo.set_active_id("fedora-36")
+ assert handler.get_selected_template() == test_qapp.domains["fedora-36"]
-@patch('subprocess.check_output')
-def test_template_handler_select_vm(mock_subprocess,
- test_qapp, new_qube_builder):
- mock_subprocess.return_value = b''
+@patch("subprocess.check_output")
+def test_template_handler_select_vm(
+ mock_subprocess, test_qapp, new_qube_builder
+):
+ mock_subprocess.return_value = b""
handler = TemplateHandler(new_qube_builder, test_qapp)
# assert we start at app
- assert handler.selected_type == 'qube_type_app'
+ assert handler.selected_type == "qube_type_app"
# selecting template works
- assert handler.get_selected_template() == test_qapp.domains['fedora-36']
- handler.select_template('fedora-35')
- assert handler.get_selected_template() == test_qapp.domains['fedora-35']
+ assert handler.get_selected_template() == test_qapp.domains["fedora-36"]
+ handler.select_template("fedora-35")
+ assert handler.get_selected_template() == test_qapp.domains["fedora-35"]
# switch to a type with None
- handler.change_vm_type('qube_type_template')
+ handler.change_vm_type("qube_type_template")
assert handler.get_selected_template() is None
- handler.select_template('fedora-36')
- assert handler.get_selected_template() == test_qapp.domains['fedora-36']
+ handler.select_template("fedora-36")
+ assert handler.get_selected_template() == test_qapp.domains["fedora-36"]
handler.select_template(None)
assert handler.get_selected_template() is None
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_get_appdata(mock_subprocess, test_qapp, new_qube_builder):
def mock_output(command):
vm_name = command[-1]
- if vm_name == 'fedora-35':
- return b'test.desktop|Test App|test desc'
- return b''
+ if vm_name == "fedora-35":
+ return b"test.desktop|Test App|test desc"
+ return b""
+
mock_subprocess.side_effect = mock_output
handler = TemplateHandler(new_qube_builder, test_qapp)
- fedora35 = test_qapp.domains['fedora-35']
- testvm = test_qapp.domains['test-vm']
+ fedora35 = test_qapp.domains["fedora-35"]
+ testvm = test_qapp.domains["test-vm"]
assert handler.get_available_apps(testvm) == []
assert len(handler.get_available_apps(fedora35)) == 1
app_data: ApplicationData = handler.get_available_apps(fedora35)[0]
- assert app_data.name == 'Test App'
- assert app_data.ident == 'test.desktop'
+ assert app_data.name == "Test App"
+ assert app_data.ident == "test.desktop"
assert app_data.template == fedora35
assert handler.get_available_apps() == [app_data]
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
def test_template_emit_signal(mock_subprocess, test_qapp, new_qube_builder):
- mock_subprocess.return_value = b''
+ mock_subprocess.return_value = b""
handler = TemplateHandler(new_qube_builder, test_qapp)
mock_emit = Mock()
- handler.main_window.connect('template-changed', mock_emit)
+ handler.main_window.connect("template-changed", mock_emit)
- handler.change_vm_type('qube_type_template')
+ handler.change_vm_type("qube_type_template")
mock_emit.assert_called_with(ANY, None)
- radio_none = new_qube_builder.get_object('radio_template_none')
- radio_some = new_qube_builder.get_object('radio_template_template')
- combo: Gtk.ComboBox = new_qube_builder.get_object('combo_template_template')
+ radio_none = new_qube_builder.get_object("radio_template_none")
+ radio_some = new_qube_builder.get_object("radio_template_template")
+ combo: Gtk.ComboBox = new_qube_builder.get_object("combo_template_template")
radio_some.set_active(True)
# first available template
- mock_emit.assert_called_with(ANY, 'fedora-35')
+ mock_emit.assert_called_with(ANY, "fedora-35")
- combo.set_active_id('fedora-36')
- assert handler.get_selected_template() == test_qapp.domains['fedora-36']
+ combo.set_active_id("fedora-36")
+ assert handler.get_selected_template() == test_qapp.domains["fedora-36"]
# two calls because comboboxes are weird
- mock_emit.assert_called_with(ANY, 'fedora-36')
+ mock_emit.assert_called_with(ANY, "fedora-36")
radio_none.set_active(True)
mock_emit.assert_called_with(ANY, None)
- handler.change_vm_type('qube_type_app')
+ handler.change_vm_type("qube_type_app")
# default template
- mock_emit.assert_called_with(ANY, 'fedora-36')
+ mock_emit.assert_called_with(ANY, "fedora-36")
- handler.select_template(test_qapp.domains['fedora-35'])
+ handler.select_template(test_qapp.domains["fedora-35"])
- mock_emit.assert_called_with(ANY, 'fedora-35')
+ mock_emit.assert_called_with(ANY, "fedora-35")
diff --git a/qubes_config/tests/test_policy_editor.py b/qubes_config/tests/test_policy_editor.py
index d3b1c789..0546f8d4 100644
--- a/qubes_config/tests/test_policy_editor.py
+++ b/qubes_config/tests/test_policy_editor.py
@@ -25,335 +25,422 @@
from ..policy_editor.policy_editor import PolicyEditor
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
def test_open_file(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
def test_open_file_not_found(test_policy_client):
- with patch('qubes_config.policy_editor.policy_editor.ask_question') \
- as mock_ask:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.CANCEL
- policy_editor = PolicyEditor('new-file', test_policy_client)
- with patch.object(policy_editor, '_quit') as mock_quit:
+ policy_editor = PolicyEditor("new-file", test_policy_client)
+ with patch.object(policy_editor, "_quit") as mock_quit:
policy_editor.perform_setup()
# should have quit
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == ""
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == ""
+ )
assert mock_quit.call_count == 1
- with patch('qubes_config.policy_editor.policy_editor.ask_question') \
- as mock_ask:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.NO
- policy_editor = PolicyEditor('new-file', test_policy_client)
- with patch.object(policy_editor, '_quit') as mock_quit:
+ policy_editor = PolicyEditor("new-file", test_policy_client)
+ with patch.object(policy_editor, "_quit") as mock_quit:
policy_editor.perform_setup()
# should not have quit, but file is not editable
assert not policy_editor.source_view.get_sensitive()
assert mock_quit.call_count == 0
- with patch('qubes_config.policy_editor.policy_editor.ask_question') \
- as mock_ask:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.YES
- policy_editor = PolicyEditor('new-file', test_policy_client)
- with patch.object(policy_editor, '_quit') as mock_quit:
+ policy_editor = PolicyEditor("new-file", test_policy_client)
+ with patch.object(policy_editor, "_quit") as mock_quit:
policy_editor.perform_setup()
# should not have quit, file is empty and editable
assert policy_editor.source_view.get_sensitive()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == ""
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == ""
+ )
assert mock_quit.call_count == 0
+
def test_detect_changes(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert not policy_editor.builder.get_object('button_save').get_sensitive()
+ assert not policy_editor.builder.get_object("button_save").get_sensitive()
assert not policy_editor.builder.get_object(
- 'button_save_exit').get_sensitive()
- assert policy_editor.error_info.get_style_context().has_class('error_ok')
+ "button_save_exit"
+ ).get_sensitive()
+ assert policy_editor.error_info.get_style_context().has_class("error_ok")
assert not policy_editor.error_info.get_style_context().has_class(
- 'error_bad')
+ "error_bad"
+ )
- policy_editor.source_buffer.set_text('Test * @anyvm @anyvm allow')
+ policy_editor.source_buffer.set_text("Test * @anyvm @anyvm allow")
- assert policy_editor.builder.get_object('button_save').get_sensitive()
- assert policy_editor.builder.get_object('button_save_exit').get_sensitive()
- assert policy_editor.error_info.get_style_context().has_class('error_ok')
+ assert policy_editor.builder.get_object("button_save").get_sensitive()
+ assert policy_editor.builder.get_object("button_save_exit").get_sensitive()
+ assert policy_editor.error_info.get_style_context().has_class("error_ok")
assert not policy_editor.error_info.get_style_context().has_class(
- 'error_bad')
+ "error_bad"
+ )
- policy_editor.source_buffer.set_text('Test * @anyvm @anyvm andruty')
+ policy_editor.source_buffer.set_text("Test * @anyvm @anyvm andruty")
- assert not policy_editor.builder.get_object('button_save').get_sensitive()
+ assert not policy_editor.builder.get_object("button_save").get_sensitive()
assert not policy_editor.builder.get_object(
- 'button_save_exit').get_sensitive()
- assert policy_editor.error_info.get_style_context().has_class('error_bad')
+ "button_save_exit"
+ ).get_sensitive()
+ assert policy_editor.error_info.get_style_context().has_class("error_bad")
assert not policy_editor.error_info.get_style_context().has_class(
- 'error_ok')
+ "error_ok"
+ )
- policy_editor.source_buffer.set_text('Test +any work @anyvm allow')
+ policy_editor.source_buffer.set_text("Test +any work @anyvm allow")
- assert policy_editor.builder.get_object('button_save').get_sensitive()
- assert policy_editor.builder.get_object('button_save_exit').get_sensitive()
- assert policy_editor.error_info.get_style_context().has_class('error_ok')
+ assert policy_editor.builder.get_object("button_save").get_sensitive()
+ assert policy_editor.builder.get_object("button_save_exit").get_sensitive()
+ assert policy_editor.error_info.get_style_context().has_class("error_ok")
assert not policy_editor.error_info.get_style_context().has_class(
- 'error_bad')
+ "error_bad"
+ )
def test_open_another_file(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
assert not policy_editor.file_select_handler.dialog_window.get_visible()
- policy_editor.action_items['open'].activate()
+ policy_editor.action_items["open"].activate()
assert policy_editor.file_select_handler.dialog_window.get_visible()
for row in policy_editor.file_select_handler.file_list.get_children():
- if row.filename == 'b-test':
+ if row.filename == "b-test":
policy_editor.file_select_handler.file_list.select_row(row)
break
policy_editor.file_select_handler.ok_button.clicked()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- """Test * test-vm @anyvm allow\n
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == """Test * test-vm @anyvm allow\n
Test * test-red test-blue deny"""
+ )
+ assert not policy_editor.builder.get_object("button_save").get_sensitive()
assert not policy_editor.builder.get_object(
- 'button_save').get_sensitive()
- assert not policy_editor.builder.get_object(
- 'button_save_exit').get_sensitive()
+ "button_save_exit"
+ ).get_sensitive()
def test_save_changes(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
- assert test_policy_client.files['a-test'] == 'Test * @anyvm @anyvm deny'
-
- policy_editor.source_buffer.set_text('Test * @anyvm @anyvm allow')
- policy_editor.action_items['save'].activate()
-
- assert test_policy_client.files['a-test'] == 'Test * @anyvm @anyvm allow'
- assert not policy_editor.builder.get_object('button_save').get_sensitive()
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
+ assert test_policy_client.files["a-test"] == "Test * @anyvm @anyvm deny"
+
+ policy_editor.source_buffer.set_text("Test * @anyvm @anyvm allow")
+ policy_editor.action_items["save"].activate()
+
+ assert test_policy_client.files["a-test"] == "Test * @anyvm @anyvm allow"
+ assert not policy_editor.builder.get_object("button_save").get_sensitive()
assert not policy_editor.builder.get_object(
- 'button_save_exit').get_sensitive()
- assert policy_editor.error_info.get_style_context().has_class('error_ok')
+ "button_save_exit"
+ ).get_sensitive()
+ assert policy_editor.error_info.get_style_context().has_class("error_ok")
assert not policy_editor.error_info.get_style_context().has_class(
- 'error_bad')
+ "error_bad"
+ )
def test_save_from_new(test_policy_client):
- policy_editor = PolicyEditor('c-test', test_policy_client)
- with patch('qubes_config.policy_editor.policy_editor.ask_question') \
- as mock_ask:
+ policy_editor = PolicyEditor("c-test", test_policy_client)
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.YES
policy_editor.perform_setup()
- assert 'c-test' not in test_policy_client.files
+ assert "c-test" not in test_policy_client.files
policy_editor.source_buffer.set_modified(True)
- policy_editor.source_buffer.set_text('Test * vm1 vm2 allow')
+ policy_editor.source_buffer.set_text("Test * vm1 vm2 allow")
- assert policy_editor.builder.get_object('button_save').get_sensitive()
- policy_editor.action_items['save'].activate()
- assert test_policy_client.files['c-test'] == 'Test * vm1 vm2 allow'
+ assert policy_editor.builder.get_object("button_save").get_sensitive()
+ policy_editor.action_items["save"].activate()
+ assert test_policy_client.files["c-test"] == "Test * vm1 vm2 allow"
def test_deselect_file_on_hide(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- policy_editor.action_items['open'].activate()
+ policy_editor.action_items["open"].activate()
assert policy_editor.file_select_handler.dialog_window.get_visible()
for row in policy_editor.file_select_handler.file_list.get_children():
- if row.filename == 'b-test':
+ if row.filename == "b-test":
policy_editor.file_select_handler.file_list.select_row(row)
break
policy_editor.file_select_handler.cancel_button.clicked()
assert not policy_editor.file_select_handler.dialog_window.get_visible()
- policy_editor.action_items['open'].activate()
+ policy_editor.action_items["open"].activate()
assert policy_editor.file_select_handler.dialog_window.get_visible()
- assert policy_editor.file_select_handler.file_list.get_selected_row() \
- is None
+ assert (
+ policy_editor.file_select_handler.file_list.get_selected_row() is None
+ )
policy_editor.file_select_handler.cancel_button.clicked()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
def test_new_file_action(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- with patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.MessageDialog.run') as mock_run, \
- patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.Entry.get_text') as mock_get:
- mock_get.return_value = 'new-file'
+ with patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.MessageDialog.run"
+ ) as mock_run, patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.Entry.get_text"
+ ) as mock_get:
+ mock_get.return_value = "new-file"
mock_run.return_value = Gtk.ResponseType.OK
- policy_editor.action_items['new'].activate()
+ policy_editor.action_items["new"].activate()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == ""
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == ""
+ )
policy_editor.source_buffer.set_modified(True)
- policy_editor.source_buffer.set_text('Test * vm1 vm2 allow')
+ policy_editor.source_buffer.set_text("Test * vm1 vm2 allow")
- assert policy_editor.builder.get_object('button_save').get_sensitive()
- policy_editor.action_items['save'].activate()
- assert test_policy_client.files['new-file'] == 'Test * vm1 vm2 allow'
+ assert policy_editor.builder.get_object("button_save").get_sensitive()
+ policy_editor.action_items["save"].activate()
+ assert test_policy_client.files["new-file"] == "Test * vm1 vm2 allow"
def test_new_file_action_invalid(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- policy_editor.source_buffer.set_text('changed.service * vm1 vm2 allow')
-
- with patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.MessageDialog.run') as mock_run, \
- patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.Entry.get_text') as mock_get, \
- patch('qubes_config.policy_editor.policy_editor.show_error'), \
- patch('qubes_config.policy_editor.policy_editor.ask_question') as \
- mock_ask:
+ policy_editor.source_buffer.set_text("changed.service * vm1 vm2 allow")
+
+ with patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.MessageDialog.run"
+ ) as mock_run, patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.Entry.get_text"
+ ) as mock_get, patch(
+ "qubes_config.policy_editor.policy_editor.show_error"
+ ), patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.NO # don't save changes
- mock_get.return_value = '???!!!'
+ mock_get.return_value = "???!!!"
mock_run.return_value = Gtk.ResponseType.OK
- policy_editor.action_items['new'].activate()
-
- assert policy_editor.filename == 'a-test'
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "changed.service * vm1 vm2 allow"
-
- with patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.MessageDialog.run') as mock_run, \
- patch('qubes_config.policy_editor.policy_editor.'
- 'Gtk.Entry.get_text') as mock_get, \
- patch('qubes_config.policy_editor.policy_editor.show_error'), \
- patch('qubes_config.policy_editor.policy_editor.ask_question') as \
- mock_ask:
+ policy_editor.action_items["new"].activate()
+
+ assert policy_editor.filename == "a-test"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "changed.service * vm1 vm2 allow"
+ )
+
+ with patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.MessageDialog.run"
+ ) as mock_run, patch(
+ "qubes_config.policy_editor.policy_editor.Gtk.Entry.get_text"
+ ) as mock_get, patch(
+ "qubes_config.policy_editor.policy_editor.show_error"
+ ), patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.NO # don't save changes
- mock_get.return_value = 'b-test'
+ mock_get.return_value = "b-test"
mock_run.return_value = Gtk.ResponseType.OK
- policy_editor.action_items['new'].activate()
+ policy_editor.action_items["new"].activate()
+
+ assert policy_editor.filename == "a-test"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "changed.service * vm1 vm2 allow"
+ )
- assert policy_editor.filename == 'a-test'
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "changed.service * vm1 vm2 allow"
def test_unsaved_exit(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
- policy_editor.source_buffer.set_text('Test * vm1 vm2 allow')
+ policy_editor.source_buffer.set_text("Test * vm1 vm2 allow")
- with patch('qubes_config.policy_editor.policy_editor.ask_question') as \
- mock_ask, patch.object(policy_editor, '_quit') as mock_quit:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask, patch.object(policy_editor, "_quit") as mock_quit:
mock_ask.return_value = Gtk.ResponseType.CANCEL
- policy_editor.builder.get_object('button_cancel').clicked()
+ policy_editor.builder.get_object("button_cancel").clicked()
# no quit, no save
assert mock_quit.call_count == 0
- assert test_policy_client.files['a-test'] == 'Test * @anyvm @anyvm deny'
+ assert test_policy_client.files["a-test"] == "Test * @anyvm @anyvm deny"
- with patch('qubes_config.policy_editor.policy_editor.ask_question') as \
- mock_ask, patch.object(policy_editor, '_quit') as mock_quit:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask, patch.object(policy_editor, "_quit") as mock_quit:
mock_ask.return_value = Gtk.ResponseType.NO
- policy_editor.builder.get_object('button_cancel').clicked()
+ policy_editor.builder.get_object("button_cancel").clicked()
# quit, no save
assert mock_quit.call_count == 1
- assert test_policy_client.files['a-test'] == 'Test * @anyvm @anyvm deny'
+ assert test_policy_client.files["a-test"] == "Test * @anyvm @anyvm deny"
def test_unsaved_exit_save(test_policy_client):
- policy_editor = PolicyEditor('a-test', test_policy_client)
+ policy_editor = PolicyEditor("a-test", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "Test * @anyvm @anyvm deny"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "Test * @anyvm @anyvm deny"
+ )
- policy_editor.source_buffer.set_text('Test * vm1 vm2 allow')
+ policy_editor.source_buffer.set_text("Test * vm1 vm2 allow")
- with patch('qubes_config.policy_editor.policy_editor.ask_question') as \
- mock_ask, patch.object(policy_editor, '_quit') as mock_quit:
+ with patch(
+ "qubes_config.policy_editor.policy_editor.ask_question"
+ ) as mock_ask, patch.object(policy_editor, "_quit") as mock_quit:
mock_ask.return_value = Gtk.ResponseType.YES
- policy_editor.builder.get_object('button_cancel').clicked()
+ policy_editor.builder.get_object("button_cancel").clicked()
# quit, save
assert mock_quit.call_count == 1
- assert test_policy_client.files['a-test'] == 'Test * vm1 vm2 allow'
+ assert test_policy_client.files["a-test"] == "Test * vm1 vm2 allow"
+
def test_includes(test_policy_client):
- policy_editor = PolicyEditor('include/include-1', test_policy_client)
+ policy_editor = PolicyEditor("include/include-1", test_policy_client)
policy_editor.perform_setup()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- "!include include/include-2"
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == "!include include/include-2"
+ )
- policy_editor.source_buffer.set_text('!include include/include-2\n'
- 'Test * vm1 vm2 allow')
+ policy_editor.source_buffer.set_text(
+ "!include include/include-2\nTest * vm1 vm2 allow"
+ )
- policy_editor.action_items['save'].activate()
+ policy_editor.action_items["save"].activate()
- assert test_policy_client.include_files['include-1'] == \
- '!include include/include-2\nTest * vm1 vm2 allow'
+ assert (
+ test_policy_client.include_files["include-1"]
+ == "!include include/include-2\nTest * vm1 vm2 allow"
+ )
# open another
- policy_editor.action_items['open'].activate()
+ policy_editor.action_items["open"].activate()
assert policy_editor.file_select_handler.dialog_window.get_visible()
for row in policy_editor.file_select_handler.file_list.get_children():
- if row.filename == 'include/include-2':
+ if row.filename == "include/include-2":
policy_editor.file_select_handler.file_list.select_row(row)
break
policy_editor.file_select_handler.ok_button.clicked()
- assert policy_editor.source_buffer.get_text(
- policy_editor.source_buffer.get_start_iter(),
- policy_editor.source_buffer.get_end_iter(), False) == \
- """Test.Test +argument @anyvm @anyvm allow"""
+ assert (
+ policy_editor.source_buffer.get_text(
+ policy_editor.source_buffer.get_start_iter(),
+ policy_editor.source_buffer.get_end_iter(),
+ False,
+ )
+ == """Test.Test +argument @anyvm @anyvm allow"""
+ )
diff --git a/qubes_config/tests/test_policy_exceptions_handler.py b/qubes_config/tests/test_policy_exceptions_handler.py
index 5d54019c..192ecc8b 100644
--- a/qubes_config/tests/test_policy_exceptions_handler.py
+++ b/qubes_config/tests/test_policy_exceptions_handler.py
@@ -25,117 +25,146 @@
from ..widgets.utils import compare_rule_lists
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
# policyexctest -> missing tests!
# dispvmexctest
-show_dialog_with_icon_path = \
- 'qubes_config.global_config.policy_handler.show_dialog_with_icon'
+show_dialog_with_icon_path = (
+ "qubes_config.global_config.policy_handler.show_dialog_with_icon"
+)
def test_policy_exc_handler_empty(test_builder, test_qapp, test_policy_manager):
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="Test2",
- policy_file_name="test")
+ policy_file_name="test",
+ )
# this should have completely empty policy
assert not handler.list_handler.current_rules
def test_policy_exc_handler_load_state(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny
TestService * test-blue @dispvm ask default_target=@dispvm:default-dvm"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
assert handler.list_handler.current_rules
- rules = [str(rule).replace("\t", " ")
- for rule in handler.list_handler.current_rules]
- expected_rules = [rule.replace("\t", " ")
- for rule in current_policy.split('\n')]
+ rules = [
+ str(rule).replace("\t", " ")
+ for rule in handler.list_handler.current_rules
+ ]
+ expected_rules = [
+ rule.replace("\t", " ") for rule in current_policy.split("\n")
+ ]
assert sorted(rules) == sorted(expected_rules)
def test_policy_exc_add_rule(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
- add_rule(handler.list_handler,
- source='test-blue', action='ask', target='default-dvm')
+ add_rule(
+ handler.list_handler,
+ source="test-blue",
+ action="ask",
+ target="default-dvm",
+ )
expected_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny
TestService * test-blue @dispvm ask default_target=@dispvm:default-dvm"""
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
handler.save()
assert compare_rule_lists(
- test_policy_manager.get_rules_from_filename('c-test', '')[0],
- expected_policy_rules)
+ test_policy_manager.get_rules_from_filename("c-test", "")[0],
+ expected_policy_rules,
+ )
def test_policy_exc_add_rule_error(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# error should have occurred
- add_rule(handler.list_handler, source='test-red', target='default-dvm',
- action='allow', expect_error=True)
+ add_rule(
+ handler.list_handler,
+ source="test-red",
+ target="default-dvm",
+ action="allow",
+ expect_error=True,
+ )
# no superfluous rules were added
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# but the row is being edited
edited_row = None
@@ -147,7 +176,7 @@ def test_policy_exc_add_rule_error(
# and it can be fixed
assert edited_row
- edited_row.source_widget.model.select_value('test-blue')
+ edited_row.source_widget.model.select_value("test-blue")
edited_row.validate_and_save()
expected_policy = """TestService * test-vm @dispvm allow target=@dispvm
@@ -156,35 +185,42 @@ def test_policy_exc_add_rule_error(
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
+
def test_policy_exc_add_rule_twice(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# click add_rule twice
handler.list_handler.add_button.clicked()
handler.list_handler.add_button.clicked()
# no superfluous rules were yet added
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# but there is a singular row is being edited
edited_row = None
@@ -206,76 +242,86 @@ def test_policy_exc_add_rule_twice(
assert False # wrong, we just closed an edited row
# no superfluous rules were added
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
def test_policy_exc_edit_rule(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
for row in handler.list_handler.current_rows:
- if row.rule.source == 'test-red':
+ if row.rule.source == "test-red":
row.activate()
assert row.editing
# not visible combobox - we have deny here
assert not row.target_widget.combobox.get_visible()
- row.action_widget.model.select_value('allow')
+ row.action_widget.model.select_value("allow")
# now it should be visible
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('default-dvm')
+ row.target_widget.model.select_value("default-dvm")
row.validate_and_save()
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
expected_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm allow target=@dispvm:default-dvm"""
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
def test_policy_exc_edit_double_click(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
for row in handler.list_handler.current_rows:
- if row.rule.source == 'test-red':
+ if row.rule.source == "test-red":
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('@dispvm')
+ row.target_widget.model.select_value("@dispvm")
# second activation cannot cause the changes to be discarded
with patch(show_dialog_with_icon_path) as mock_ask:
row.activate()
@@ -284,48 +330,54 @@ def test_policy_exc_edit_double_click(
row.validate_and_save()
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
expected_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm"""
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
def test_policy_exc_edit_cancel(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
for row in handler.list_handler.current_rows:
- if row.rule.source == 'test-red':
+ if row.rule.source == "test-red":
found_row = row
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('@dispvm')
+ row.target_widget.model.select_value("@dispvm")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# click another row, dismiss message
with patch(show_dialog_with_icon_path) as mock_ask:
@@ -336,22 +388,23 @@ def test_policy_exc_edit_cancel(
break
assert mock_ask.mock_calls
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
# now do the same, but do not dismiss the message
for row in handler.list_handler.current_rows:
- if row.rule.source == 'test-red':
+ if row.rule.source == "test-red":
found_row = row
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
# check the old selection was reset
- assert str(row.target_widget.model.get_selected()) == 'default-dvm'
- row.target_widget.model.select_value('@dispvm')
+ assert str(row.target_widget.model.get_selected()) == "default-dvm"
+ row.target_widget.model.select_value("@dispvm")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
with patch(show_dialog_with_icon_path) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.YES
@@ -365,44 +418,49 @@ def test_policy_exc_edit_cancel(
TestService * test-red @dispvm ask default_target=@dispvm"""
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
def test_policy_exc_close_all_fail(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
for row in handler.list_handler.current_rows:
- if row.rule.source == 'test-red':
+ if row.rule.source == "test-red":
found_row = row
row.activate()
assert row.editing
assert row.source_widget.combobox.get_visible()
- row.source_widget.model.select_value('test-vm')
+ row.source_widget.model.select_value("test-vm")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
# click another row, but, say you want to save changes, fail
- with patch(show_dialog_with_icon_path) as mock_ask, \
- patch('qubes_config.global_config.rule_list_widgets.show_error') \
- as mock_error:
+ with patch(show_dialog_with_icon_path) as mock_ask, patch(
+ "qubes_config.global_config.rule_list_widgets.show_error"
+ ) as mock_error:
mock_ask.return_value = Gtk.ResponseType.YES
for row in handler.list_handler.current_rows:
if row != found_row:
@@ -411,63 +469,75 @@ def test_policy_exc_close_all_fail(
assert mock_ask.mock_calls
assert mock_error.mock_calls
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
def test_policy_handler_reset(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
- add_rule(handler.list_handler, source='test-blue', action='deny')
+ add_rule(handler.list_handler, source="test-blue", action="deny")
expected_policy = """TestService * test-vm @dispvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm
TestService * test-blue @dispvm deny"""
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
- assert compare_rule_lists(handler.list_handler.current_rules,
- expected_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, expected_policy_rules
+ )
handler.reset()
- assert compare_rule_lists(handler.list_handler.current_rules,
- current_policy_rules)
+ assert compare_rule_lists(
+ handler.list_handler.current_rules, current_policy_rules
+ )
handler.save()
assert compare_rule_lists(
- test_policy_manager.get_rules_from_filename('c-test', '')[0],
- current_policy_rules)
+ test_policy_manager.get_rules_from_filename("c-test", "")[0],
+ current_policy_rules,
+ )
+
def test_policy_exc_get_unsupported(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm @anyvm allow target=@dispvm
TestService * test-red @dispvm ask default_target=@dispvm:default-dvm"""
# current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = DispvmExceptionHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='dispvmexctest',
+ prefix="dispvmexctest",
policy_manager=test_policy_manager,
service_name="TestService",
- policy_file_name="c-test")
+ policy_file_name="c-test",
+ )
assert not handler.get_unsaved()
assert handler.list_handler.error_handler.get_errors()
diff --git a/qubes_config/tests/test_policy_handler.py b/qubes_config/tests/test_policy_handler.py
index 34e7aa9b..0b0c8864 100644
--- a/qubes_config/tests/test_policy_handler.py
+++ b/qubes_config/tests/test_policy_handler.py
@@ -22,22 +22,28 @@
from ..global_config.policy_manager import PolicyManager
from ..global_config.policy_handler import PolicyHandler, VMSubsetPolicyHandler
-from ..global_config.policy_rules import SimpleVerbDescription, RuleSimple, \
- RuleTargeted
+from ..global_config.policy_rules import (
+ SimpleVerbDescription,
+ RuleSimple,
+ RuleTargeted,
+)
from ..widgets.utils import compare_rule_lists
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
-show_dialog_with_icon_path = \
- 'qubes_config.global_config.policy_handler.show_dialog_with_icon'
+show_dialog_with_icon_path = (
+ "qubes_config.global_config.policy_handler.show_dialog_with_icon"
+)
-def add_rule(handler, source = None, target = None,
- action = None, expect_error: bool = False):
+def add_rule(
+ handler, source=None, target=None, action=None, expect_error: bool = False
+):
# attempt to add a new rule
handler.add_button.clicked()
# find the clicked row
@@ -53,8 +59,9 @@ def add_rule(handler, source = None, target = None,
assert row.action_widget.combobox.get_visible()
row.action_widget.model.select_value(action)
if expect_error:
- with patch('qubes_config.global_config.rule_list_widgets.'
- 'show_error') as mock_error:
+ with patch(
+ "qubes_config.global_config.rule_list_widgets.show_error"
+ ) as mock_error:
assert not mock_error.mock_calls
row.validate_and_save()
assert mock_error.mock_calls
@@ -64,10 +71,12 @@ def add_rule(handler, source = None, target = None,
else:
assert False
+
def get_raw_rules(handler: PolicyHandler):
text_buffer = handler.raw_handler.raw_text.get_buffer()
- raw_text = text_buffer.get_text(text_buffer.get_start_iter(),
- text_buffer.get_end_iter(), False)
+ raw_text = text_buffer.get_text(
+ text_buffer.get_start_iter(), text_buffer.get_end_iter(), False
+ )
rules = handler.policy_manager.text_to_rules(raw_text)
return rules
@@ -76,13 +85,14 @@ def test_policy_handler_empty(test_builder, test_qapp, test_policy_manager):
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy="",
service_name="Test2",
policy_file_name="test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert not handler.current_rules
@@ -90,7 +100,8 @@ def test_policy_handler_empty(test_builder, test_qapp, test_policy_manager):
def test_policy_handler_default_policy(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -98,13 +109,14 @@ def test_policy_handler_default_policy(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -113,7 +125,8 @@ def test_policy_handler_default_policy(
def test_policy_handler_non_default_policy(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -122,19 +135,21 @@ def test_policy_handler_non_default_policy(
TestService * @anyvm @anyvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
assert handler.enable_radio.get_active()
assert compare_rule_lists(handler.current_rules, current_policy_rules)
@@ -149,7 +164,8 @@ def test_policy_handler_non_default_policy(
def test_policy_handler_enforce_deny_all(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
@@ -157,30 +173,35 @@ def test_policy_handler_enforce_deny_all(
current_policy_with_deny = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
current_policy_rules_with_deny = test_policy_manager.text_to_rules(
- current_policy_with_deny)
+ current_policy_with_deny
+ )
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert handler.enable_radio.get_active()
- assert compare_rule_lists(handler.current_rules,
- current_policy_rules_with_deny)
+ assert compare_rule_lists(
+ handler.current_rules, current_policy_rules_with_deny
+ )
def test_policy_handler_add_rule(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -188,13 +209,14 @@ def test_policy_handler_add_rule(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -202,7 +224,7 @@ def test_policy_handler_add_rule(
handler.enable_radio.set_active(True)
- add_rule(handler, source='test-red', action='allow')
+ add_rule(handler, source="test-red", action="allow")
expected_policy = """TestService * test-red @anyvm allow
TestService * test-vm test-blue allow
@@ -213,11 +235,14 @@ def test_policy_handler_add_rule(
handler.save()
assert compare_rule_lists(
- test_policy_manager.get_rules_from_filename('c-test', '')[0],
- expected_policy_rules)
+ test_policy_manager.get_rules_from_filename("c-test", "")[0],
+ expected_policy_rules,
+ )
+
def test_policy_handler_add_rule_error(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -225,13 +250,14 @@ def test_policy_handler_add_rule_error(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -240,8 +266,13 @@ def test_policy_handler_add_rule_error(
handler.enable_radio.set_active(True)
# error should have occurred
- add_rule(handler, source='test-vm', target='test-blue', action='allow',
- expect_error=True)
+ add_rule(
+ handler,
+ source="test-vm",
+ target="test-blue",
+ action="allow",
+ expect_error=True,
+ )
# no superfluous rules were added
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -255,7 +286,7 @@ def test_policy_handler_add_rule_error(
# and it can be fixed
assert edited_row
- edited_row.target_widget.model.select_value('test-red')
+ edited_row.target_widget.model.select_value("test-red")
edited_row.validate_and_save()
expected_policy = """TestService * test-vm test-blue allow
@@ -267,7 +298,8 @@ def test_policy_handler_add_rule_error(
def test_policy_handler_add_rule_twice(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -275,13 +307,14 @@ def test_policy_handler_add_rule_twice(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
handler.enable_radio.set_active(True)
@@ -316,33 +349,35 @@ def test_policy_handler_add_rule_twice(
def test_policy_handler_edit_rule(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
handler.enable_radio.set_active(True)
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('test-red')
+ row.target_widget.model.select_value("test-red")
row.validate_and_save()
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
expected_policy = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
@@ -351,29 +386,31 @@ def test_policy_handler_edit_rule(
def test_policy_handler_edit_double_click(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
handler.enable_radio.set_active(True)
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('test-red')
+ row.target_widget.model.select_value("test-red")
# second activation cannot cause the changes to be discarded
with patch(show_dialog_with_icon_path) as mock_ask:
row.activate()
@@ -382,7 +419,7 @@ def test_policy_handler_edit_double_click(
row.validate_and_save()
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
expected_policy = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
@@ -391,7 +428,8 @@ def test_policy_handler_edit_double_click(
def test_policy_handler_edit_cancel(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -399,26 +437,27 @@ def test_policy_handler_edit_cancel(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
handler.enable_radio.set_active(True)
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
found_row = row
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('test-red')
+ row.target_widget.model.select_value("test-red")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -435,17 +474,17 @@ def test_policy_handler_edit_cancel(
# now do the same, but do not dismiss the message
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
found_row = row
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
# check the old selection was reset
- assert str(row.target_widget.model.get_selected()) == 'test-blue'
- row.target_widget.model.select_value('test-red')
+ assert str(row.target_widget.model.get_selected()) == "test-blue"
+ row.target_widget.model.select_value("test-red")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
with patch(show_dialog_with_icon_path) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.YES
@@ -463,7 +502,8 @@ def test_policy_handler_edit_cancel(
def test_policy_handler_close_all_fail(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * test-vm test-blue allow
TestService * test-vm test-red allow
@@ -473,31 +513,32 @@ def test_policy_handler_close_all_fail(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
handler.enable_radio.set_active(True)
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
found_row = row
row.activate()
assert row.editing
assert row.target_widget.combobox.get_visible()
- row.target_widget.model.select_value('test-red')
+ row.target_widget.model.select_value("test-red")
break
else:
- assert False # expected rule to edit not found!
+ assert False # expected rule to edit not found!
# click another row, but, say you want to save changes, fail
- with patch(show_dialog_with_icon_path) as mock_ask, \
- patch('qubes_config.global_config.rule_list_widgets.show_error') \
- as mock_error:
+ with patch(show_dialog_with_icon_path) as mock_ask, patch(
+ "qubes_config.global_config.rule_list_widgets.show_error"
+ ) as mock_error:
mock_ask.return_value = Gtk.ResponseType.YES
for row in handler.current_rows:
if row != found_row:
@@ -510,7 +551,8 @@ def test_policy_handler_close_all_fail(
def test_policy_handler_reset(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -518,13 +560,14 @@ def test_policy_handler_reset(
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# this should have completely empty policy, enabled default policy
assert compare_rule_lists(handler.current_rules, default_policy_rules)
@@ -532,7 +575,7 @@ def test_policy_handler_reset(
handler.enable_radio.set_active(True)
- add_rule(handler, source='test-red', action='allow')
+ add_rule(handler, source="test-red", action="allow")
expected_policy = """TestService * test-red @anyvm allow
TestService * test-vm test-blue allow
@@ -547,12 +590,14 @@ def test_policy_handler_reset(
handler.save()
assert compare_rule_lists(
- test_policy_manager.get_rules_from_filename('c-test', '')[0],
- default_policy_rules)
+ test_policy_manager.get_rules_from_filename("c-test", "")[0],
+ default_policy_rules,
+ )
def test_policy_handler_view_raw(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
default_policy_rules = test_policy_manager.text_to_rules(default_policy)
@@ -561,19 +606,21 @@ def test_policy_handler_view_raw(
TestService * @anyvm @anyvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
assert compare_rule_lists(get_raw_rules(handler), current_policy_rules)
handler.disable_radio.set_active(True)
@@ -581,7 +628,7 @@ def test_policy_handler_view_raw(
handler.enable_radio.set_active(True)
assert compare_rule_lists(get_raw_rules(handler), current_policy_rules)
- add_rule(handler, 'test-vm', '@anyvm', 'ask')
+ add_rule(handler, "test-vm", "@anyvm", "ask")
expected_policy = """TestService * test-vm test-red allow
TestService * test-vm @anyvm ask
@@ -592,26 +639,29 @@ def test_policy_handler_view_raw(
def test_policy_handler_edit_raw(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
current_policy = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
expected_policy = """TestService * test-vm test-red allow
TestService * test-vm @anyvm ask
@@ -625,34 +675,38 @@ def test_policy_handler_edit_raw(
def test_policy_handler_edit_raw_error(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
current_policy = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
current_policy_rules = test_policy_manager.text_to_rules(current_policy)
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
expected_policy = """TestService * test-vm test-red escargot
TestService * test-vm @anyvm framboise
TestService * @anyvm @anyvm deny"""
handler.raw_handler.text_buffer.set_text(expected_policy)
- with patch('qubes_config.global_config.policy_handler.show_error') as \
- mock_error:
+ with patch(
+ "qubes_config.global_config.policy_handler.show_error"
+ ) as mock_error:
assert not mock_error.mock_calls
handler.raw_handler.raw_save.clicked()
assert mock_error.mock_calls
@@ -670,25 +724,28 @@ def test_policy_handler_edit_raw_error(
def test_policy_handler_edit_raw_close(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
current_policy = """TestService * test-vm test-red allow
TestService * @anyvm @anyvm deny"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
# start adding a row
handler.add_button.clicked()
@@ -703,7 +760,7 @@ def test_policy_handler_edit_raw_close(
assert False # somehow the row didn't get added, bad
# but now expand raw policy
- handler.raw_handler.raw_event_button.emit('clicked')
+ handler.raw_handler.raw_event_button.emit("clicked")
for row in handler.current_rows:
if row.editing:
assert False # the row should have closed
@@ -711,19 +768,21 @@ def test_policy_handler_edit_raw_close(
def test_policy_handler_sorting(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
raw_policy = """
TestService * @anyvm @anyvm deny
@@ -755,20 +814,22 @@ def test_policy_handler_sorting(
def test_policy_handler_get_unsaved(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """TestService * test-vm test-blue allow
TestService * @anyvm @anyvm deny"""
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
@@ -777,13 +838,13 @@ def test_policy_handler_get_unsaved(
assert not handler.get_unsaved()
# add a rule
- add_rule(handler, 'test-vm', 'test-red', 'deny')
+ add_rule(handler, "test-vm", "test-red", "deny")
assert handler.get_unsaved()
# remove the rule added
for row in handler.current_rows:
- if row.rule.target == 'test-red':
+ if row.rule.target == "test-red":
row._do_delete_self(force=True) # pylint: disable=protected-access
break
else:
@@ -794,8 +855,8 @@ def test_policy_handler_get_unsaved(
# modify a rule and save
for row in handler.current_rows:
- if row.rule.target == 'test-blue':
- row.target_widget.model.select_value('test-red')
+ if row.rule.target == "test-blue":
+ row.target_widget.model.select_value("test-red")
row.validate_and_save()
assert handler.get_unsaved()
handler.save()
@@ -803,23 +864,26 @@ def test_policy_handler_get_unsaved(
def test_policy_handler_get_unsaved_unsupported(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * test-vm test-blue allow target=test-red
TestService * @anyvm @anyvm deny"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy="",
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
assert handler.error_handler.get_errors()
@@ -835,15 +899,17 @@ def test_policy_handler_get_unsaved_unsupported(
####### Subset handler
-def test_subset_handler(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+
+def test_subset_handler(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * @anyvm test-blue allow"""
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -851,7 +917,8 @@ def test_subset_handler(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleSimple)
+ exception_rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
handler.enable_radio.set_active(True)
@@ -859,9 +926,10 @@ def test_subset_handler(test_builder, test_qapp,
# add main rule
handler.add_select_button.clicked()
assert handler.add_select_box.get_visible()
- handler.select_qube_model.select_value('vault')
- with patch('qubes_config.global_config.policy_handler.'
- 'ask_question') as mock_ask:
+ handler.select_qube_model.select_value("vault")
+ with patch(
+ "qubes_config.global_config.policy_handler.ask_question"
+ ) as mock_ask:
handler.add_select_confirm.clicked()
# vault is not networked
assert not mock_ask.mock_calls
@@ -869,16 +937,16 @@ def test_subset_handler(test_builder, test_qapp,
expected_policy = """
TestService * @anyvm test-blue allow
TestService * @anyvm vault ask"""
- expected_policy_rules = test_policy_manager.text_to_rules(
- expected_policy)
+ expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
assert compare_rule_lists(handler.current_rules, expected_policy_rules)
# and another
handler.add_select_button.clicked()
assert handler.add_select_box.get_visible()
- handler.select_qube_model.select_value('test-red')
- with patch('qubes_config.global_config.policy_handler.'
- 'ask_question') as mock_ask:
+ handler.select_qube_model.select_value("test-red")
+ with patch(
+ "qubes_config.global_config.policy_handler.ask_question"
+ ) as mock_ask:
handler.add_select_confirm.clicked()
# test-red is networked
assert mock_ask.mock_calls
@@ -890,23 +958,27 @@ def test_subset_handler(test_builder, test_qapp,
expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
assert compare_rule_lists(handler.current_rules, expected_policy_rules)
+
def test_subset_handler_get_unsaved_unsupported(
- test_builder, test_qapp, test_policy_manager: PolicyManager):
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
current_policy = """TestService * @anyvm test-blue allow target=test=red"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = PolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy="",
service_name="TestService",
policy_file_name="c-test",
verb_description=SimpleVerbDescription({}),
- rule_class=RuleSimple)
+ rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
assert handler.error_handler.get_errors()
@@ -920,8 +992,9 @@ def test_subset_handler_get_unsaved_unsupported(
assert not handler.get_unsaved()
-def test_subset_handler_limited_choice(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+def test_subset_handler_limited_choice(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * @anyvm test-blue allow
TestService * @anyvm test-red allow"""
@@ -929,7 +1002,7 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -937,7 +1010,8 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleSimple)
+ exception_rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
handler.enable_radio.set_active(True)
@@ -946,15 +1020,15 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
handler.add_button.clicked()
for row in handler.current_rows:
if row.editing:
- test_blue = test_qapp.domains['test-blue']
- test_red = test_qapp.domains['test-red']
- vault = test_qapp.domains['vault']
+ test_blue = test_qapp.domains["test-blue"]
+ test_red = test_qapp.domains["test-red"]
+ vault = test_qapp.domains["vault"]
assert row.target_widget.model.is_vm_available(test_blue)
assert row.target_widget.model.is_vm_available(test_red)
assert not row.target_widget.model.is_vm_available(vault)
- row.source_widget.model.select_value('test-vm')
- row.target_widget.model.select_value('test-blue')
- row.action_widget.model.select_value('deny')
+ row.source_widget.model.select_value("test-vm")
+ row.target_widget.model.select_value("test-blue")
+ row.action_widget.model.select_value("deny")
row.validate_and_save()
edited_row = row
break
@@ -965,8 +1039,7 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
TestService * test-vm test-blue deny
TestService * @anyvm test-blue allow
TestService * @anyvm test-red allow"""
- expected_policy_rules = test_policy_manager.text_to_rules(
- expected_policy)
+ expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
assert compare_rule_lists(handler.current_rules, expected_policy_rules)
# wait, I changed my mind...
@@ -978,17 +1051,17 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
# check that previous row is no longer being edited
assert not edited_row.editing
- handler.select_qube_model.select_value('vault')
+ handler.select_qube_model.select_value("vault")
handler.add_select_confirm.clicked()
# check that choice was updated
handler.add_button.clicked()
for row in handler.current_rows:
if row.editing:
- test_vm = test_qapp.domains['test-vm']
- test_blue = test_qapp.domains['test-blue']
- test_red = test_qapp.domains['test-red']
- vault = test_qapp.domains['vault']
+ test_vm = test_qapp.domains["test-vm"]
+ test_blue = test_qapp.domains["test-blue"]
+ test_red = test_qapp.domains["test-red"]
+ vault = test_qapp.domains["vault"]
assert row.target_widget.model.is_vm_available(test_blue)
assert row.target_widget.model.is_vm_available(test_red)
assert row.target_widget.model.is_vm_available(vault)
@@ -1007,12 +1080,13 @@ def test_subset_handler_limited_choice(test_builder, test_qapp,
TestService * @anyvm test-blue allow
TestService * @anyvm test-red allow
TestService * @anyvm vault ask"""
- expected_policy_rules = test_policy_manager.text_to_rules(
- expected_policy)
+ expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
assert compare_rule_lists(handler.current_rules, expected_policy_rules)
-def test_subset_handler_remove_choice(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+
+def test_subset_handler_remove_choice(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * test-vm test-blue deny
TestService * @anyvm test-blue allow
@@ -1021,7 +1095,7 @@ def test_subset_handler_remove_choice(test_builder, test_qapp,
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -1029,16 +1103,18 @@ def test_subset_handler_remove_choice(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleSimple)
+ exception_rule_class=RuleSimple,
+ )
assert not handler.get_unsaved()
handler.enable_radio.set_active(True)
# remove vault from key qubes
for row in handler.main_list_box.get_children():
- if row.rule.target == 'vault':
- with patch('qubes_config.global_config.rule_list_widgets.'
- 'ask_question') as mock_ask:
+ if row.rule.target == "vault":
+ with patch(
+ "qubes_config.global_config.rule_list_widgets.ask_question"
+ ) as mock_ask:
mock_ask.return_value = Gtk.ResponseType.YES
row._delete_self() # pylint: disable=protected-access
assert mock_ask.mock_calls
@@ -1047,9 +1123,9 @@ def test_subset_handler_remove_choice(test_builder, test_qapp,
handler.add_button.clicked()
for row in handler.current_rows:
if row.editing:
- test_blue = test_qapp.domains['test-blue']
- test_red = test_qapp.domains['test-red']
- vault = test_qapp.domains['vault']
+ test_blue = test_qapp.domains["test-blue"]
+ test_red = test_qapp.domains["test-red"]
+ vault = test_qapp.domains["vault"]
assert row.target_widget.model.is_vm_available(test_blue)
assert not row.target_widget.model.is_vm_available(test_red)
assert not row.target_widget.model.is_vm_available(vault)
@@ -1059,22 +1135,23 @@ def test_subset_handler_remove_choice(test_builder, test_qapp,
# remove test blue too
for row in handler.main_list_box.get_children():
- if row.rule.target == 'test-blue':
+ if row.rule.target == "test-blue":
row._do_delete_self(force=True) # pylint: disable=protected-access
# there should be nothing left
assert compare_rule_lists(handler.current_rules, [])
-def test_subset_handler_duplicates(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+def test_subset_handler_duplicates(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * @anyvm vault ask"""
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -1082,13 +1159,12 @@ def test_subset_handler_duplicates(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleTargeted)
+ exception_rule_class=RuleTargeted,
+ )
handler.enable_radio.set_active(True)
- add_rule(handler, source='test-blue', target='vault',
- action='allow')
- add_rule(handler, source='test-red', target='vault',
- action='ask')
+ add_rule(handler, source="test-blue", target="vault", action="allow")
+ add_rule(handler, source="test-red", target="vault", action="ask")
expected_policy = """
TestService * test-blue @default allow target=vault
@@ -1098,13 +1174,13 @@ def test_subset_handler_duplicates(test_builder, test_qapp,
TestService * @anyvm vault ask
"""
- expected_policy_rules = test_policy_manager.text_to_rules(
- expected_policy)
+ expected_policy_rules = test_policy_manager.text_to_rules(expected_policy)
assert compare_rule_lists(handler.current_rules, expected_policy_rules)
-def test_subset_handler_duplicates_load(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+def test_subset_handler_duplicates_load(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
TestService * test-blue @default allow target=vault
TestService * test-blue vault allow
@@ -1114,7 +1190,7 @@ def test_subset_handler_duplicates_load(test_builder, test_qapp,
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -1122,7 +1198,8 @@ def test_subset_handler_duplicates_load(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleTargeted)
+ exception_rule_class=RuleTargeted,
+ )
handler.enable_radio.set_active(True)
@@ -1130,8 +1207,9 @@ def test_subset_handler_duplicates_load(test_builder, test_qapp,
assert len(handler.exception_list_box.get_children()) == 1
-def test_subset_handler_unsupported(test_builder, test_qapp,
- test_policy_manager: PolicyManager):
+def test_subset_handler_unsupported(
+ test_builder, test_qapp, test_policy_manager: PolicyManager
+):
default_policy = """
"""
current_policy = """TestService * test-blue @default allow target=vault
@@ -1139,13 +1217,14 @@ def test_subset_handler_unsupported(test_builder, test_qapp,
TestService * @anyvm vault allow target=test-blue
"""
- test_policy_manager.policy_client.policy_replace('c-test',
- current_policy, 'any')
+ test_policy_manager.policy_client.policy_replace(
+ "c-test", current_policy, "any"
+ )
handler = VMSubsetPolicyHandler(
qapp=test_qapp,
gtk_builder=test_builder,
- prefix='policytest',
+ prefix="policytest",
policy_manager=test_policy_manager,
default_policy=default_policy,
service_name="TestService",
@@ -1153,7 +1232,8 @@ def test_subset_handler_unsupported(test_builder, test_qapp,
main_verb_description=SimpleVerbDescription({}),
main_rule_class=RuleSimple,
exception_verb_description=SimpleVerbDescription({}),
- exception_rule_class=RuleTargeted)
+ exception_rule_class=RuleTargeted,
+ )
assert handler.error_handler.error_box.get_visible()
# no rules should have been loaded
diff --git a/qubes_config/tests/test_policy_manager.py b/qubes_config/tests/test_policy_manager.py
index e74c69be..a4e7682f 100644
--- a/qubes_config/tests/test_policy_manager.py
+++ b/qubes_config/tests/test_policy_manager.py
@@ -28,34 +28,41 @@
def test_conflict_files():
def return_files(service_name):
- if service_name == 'test':
+ if service_name == "test":
return ["a-test", "b-test", "c-test"]
- return ['']
+ return [""]
manager = PolicyManager()
- with patch("qubes_config.global_config.policy_manager."
- "PolicyClient.policy_get_files") as mock_get:
+ with patch(
+ "qubes_config.global_config.policy_manager."
+ "PolicyClient.policy_get_files"
+ ) as mock_get:
mock_get.side_effect = return_files
- assert manager.get_conflicting_policy_files('test', 'd-test') == \
- ["a-test", "b-test", "c-test"]
- assert manager.get_conflicting_policy_files('test', 'c-test') == \
- ["a-test", "b-test"]
- assert manager.get_conflicting_policy_files('test', 'b-test') == \
- ["a-test"]
- assert not manager.get_conflicting_policy_files('test', 'a-test')
- assert not manager.get_conflicting_policy_files('other', 'test')
-
-@patch("qubes_config.global_config.policy_manager."
- "PolicyClient.policy_get")
-@patch("qubes_config.global_config.policy_manager."
- "PolicyClient.policy_replace")
+ assert manager.get_conflicting_policy_files("test", "d-test") == [
+ "a-test",
+ "b-test",
+ "c-test",
+ ]
+ assert manager.get_conflicting_policy_files("test", "c-test") == [
+ "a-test",
+ "b-test",
+ ]
+ assert manager.get_conflicting_policy_files("test", "b-test") == [
+ "a-test"
+ ]
+ assert not manager.get_conflicting_policy_files("test", "a-test")
+ assert not manager.get_conflicting_policy_files("other", "test")
+
+
+@patch("qubes_config.global_config.policy_manager.PolicyClient.policy_get")
+@patch("qubes_config.global_config.policy_manager.PolicyClient.policy_replace")
def test_get_policy_from_file_new_no_default(mock_replace, mock_get):
manager = PolicyManager()
- mock_get.side_effect = subprocess.CalledProcessError(2, 'test')
+ mock_get.side_effect = subprocess.CalledProcessError(2, "test")
- assert manager.get_rules_from_filename('test', '') == ([], None)
+ assert manager.get_rules_from_filename("test", "") == ([], None)
assert not mock_replace.mock_calls
@@ -67,7 +74,7 @@ def __init__(self):
def policy_get(self, filename):
if filename in self.files:
return self.files[filename], filename
- raise subprocess.CalledProcessError(2, 'test')
+ raise subprocess.CalledProcessError(2, "test")
def policy_replace(self, filename, text):
self.files[filename] = text
@@ -75,37 +82,38 @@ def policy_replace(self, filename, text):
manager = PolicyManager()
manager.policy_client = MockPolicy()
- test_default = 'Test\t*\t@anyvm\t@anyvm\tdeny'
+ test_default = "Test\t*\t@anyvm\t@anyvm\tdeny"
# token should be None (the file is new), but rules should be appropriate
- got_rules, token = manager.get_rules_from_filename('test', test_default)
+ got_rules, token = manager.get_rules_from_filename("test", test_default)
assert token is None
assert len(got_rules) == 1
assert str(got_rules[0]) == test_default
- assert 'test' not in manager.policy_client.files
+ assert "test" not in manager.policy_client.files
def test_get_policy_from_file_existing():
manager = PolicyManager()
- rules = 'Test\t*\t@anyvm\t@anyvm\tdeny'
+ rules = "Test\t*\t@anyvm\t@anyvm\tdeny"
def get_file(filename):
- if filename == 'test':
- return rules, 'test'
- return '', ''
+ if filename == "test":
+ return rules, "test"
+ return "", ""
- with patch("qubes_config.global_config.policy_manager."
- "PolicyClient.policy_get") as mock_get:
+ with patch(
+ "qubes_config.global_config.policy_manager.PolicyClient.policy_get"
+ ) as mock_get:
mock_get.side_effect = get_file
- got_rules, token = manager.get_rules_from_filename('test', '')
- assert token == 'test'
+ got_rules, token = manager.get_rules_from_filename("test", "")
+ assert token == "test"
assert len(got_rules) == 1
assert str(got_rules[0]) == rules
- got_rules, token = manager.get_rules_from_filename('test2', '')
- assert token == ''
+ got_rules, token = manager.get_rules_from_filename("test2", "")
+ assert token == ""
assert len(got_rules) == 0
@@ -118,19 +126,26 @@ def test_compare_rules_to_text():
rule_text_3 = """Test * @anyvm @anyvm deny
Test * work @anyvm allow"""
- rules_1 = [Rule.from_line(None, "Test * @anyvm @anyvm deny",
- filepath=None, lineno=0)]
+ rules_1 = [
+ Rule.from_line(
+ None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0
+ )
+ ]
rules_2 = [
- Rule.from_line(None, "Test * @anyvm @anyvm deny",
- filepath=None, lineno=0),
- Rule.from_line(None, "Test +Test2 work @anyvm allow",
- filepath=None, lineno=0)
+ Rule.from_line(
+ None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0
+ ),
+ Rule.from_line(
+ None, "Test +Test2 work @anyvm allow", filepath=None, lineno=0
+ ),
]
rules_3 = [
- Rule.from_line(None, "Test * @anyvm @anyvm deny",
- filepath=None, lineno=0),
- Rule.from_line(None, "Test * work @anyvm allow",
- filepath=None, lineno=0)
+ Rule.from_line(
+ None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0
+ ),
+ Rule.from_line(
+ None, "Test * work @anyvm allow", filepath=None, lineno=0
+ ),
]
assert manager.compare_rules_to_text(rules_1, rule_text_1)
@@ -146,26 +161,39 @@ def test_new_rule():
manager = PolicyManager()
rule_1 = Rule.from_line(
- None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0)
+ None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0
+ )
rule_2 = Rule.from_line(
- None, "Test +Test @anyvm vault allow target=dom0",
- filepath=None, lineno=0)
+ None,
+ "Test +Test @anyvm vault allow target=dom0",
+ filepath=None,
+ lineno=0,
+ )
+
+ assert str(manager.new_rule("Test", "@anyvm", "@anyvm", "deny")) == str(
+ rule_1
+ )
+ assert str(
+ manager.new_rule(
+ service="Test",
+ source="@anyvm",
+ target="vault",
+ action="allow target=dom0",
+ argument="+Test",
+ )
+ ) == str(rule_2)
- assert str(manager.new_rule('Test', '@anyvm', '@anyvm', 'deny')) == \
- str(rule_1)
- assert str(manager.new_rule(
- service='Test', source='@anyvm', target='vault',
- action='allow target=dom0', argument='+Test')) == str(rule_2)
def test_save_policy():
manager = PolicyManager()
- rule_text = 'Test\t*\t@anyvm\t@anyvm\tdeny'
+ rule_text = "Test\t*\t@anyvm\t@anyvm\tdeny"
rule = Rule.from_line(
- None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0)
+ None, "Test * @anyvm @anyvm deny", filepath=None, lineno=0
+ )
def replace_file(file_name: str, new_text: str, _token):
- if file_name == 'test':
+ if file_name == "test":
if not new_text.startswith(manager.policy_disclaimer):
assert False
if not rule_text in new_text:
@@ -173,7 +201,9 @@ def replace_file(file_name: str, new_text: str, _token):
return
assert False
- with patch("qubes_config.global_config.policy_manager."
- "PolicyClient.policy_replace") as mock_replace:
+ with patch(
+ "qubes_config.global_config.policy_manager."
+ "PolicyClient.policy_replace"
+ ) as mock_replace:
mock_replace.side_effect = replace_file
- manager.save_rules('test', [rule], 'any')
+ manager.save_rules("test", [rule], "any")
diff --git a/qubes_config/tests/test_policy_rules.py b/qubes_config/tests/test_policy_rules.py
index 76f62047..dff99078 100644
--- a/qubes_config/tests/test_policy_rules.py
+++ b/qubes_config/tests/test_policy_rules.py
@@ -23,420 +23,447 @@
import pytest
from qrexec.policy.parser import Rule
-from ..global_config.policy_rules import RuleSimple, RuleTargeted,\
- RuleDispVM, RuleTargetedAdminVM
+from ..global_config.policy_rules import (
+ RuleSimple,
+ RuleTargeted,
+ RuleDispVM,
+ RuleTargetedAdminVM,
+)
+
def make_rule(source, target, action):
return Rule.from_line(
- None, f"Service\t*\t{source}\t{target}\t{action}",
- filepath=None, lineno=0)
+ None,
+ f"Service\t*\t{source}\t{target}\t{action}",
+ filepath=None,
+ lineno=0,
+ )
def test_simple_rule():
- basic_rule = make_rule('vm1', 'vm2', 'allow')
+ basic_rule = make_rule("vm1", "vm2", "allow")
wrapped_rule = RuleSimple(basic_rule)
assert wrapped_rule.raw_rule == basic_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'allow'
-
- wrapped_rule.source = 'vm2'
- assert str(wrapped_rule.raw_rule) == str(make_rule('vm2', 'vm2', 'allow'))
- wrapped_rule.target = 'vm1'
- assert str(wrapped_rule.raw_rule) == str(make_rule('vm2', 'vm1', 'allow'))
- wrapped_rule.action = 'deny'
- assert str(wrapped_rule.raw_rule) == str(make_rule('vm2', 'vm1', 'deny'))
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "allow"
+
+ wrapped_rule.source = "vm2"
+ assert str(wrapped_rule.raw_rule) == str(make_rule("vm2", "vm2", "allow"))
+ wrapped_rule.target = "vm1"
+ assert str(wrapped_rule.raw_rule) == str(make_rule("vm2", "vm1", "allow"))
+ wrapped_rule.action = "deny"
+ assert str(wrapped_rule.raw_rule) == str(make_rule("vm2", "vm1", "deny"))
assert not wrapped_rule.is_rule_fundamental()
- assert wrapped_rule.is_rule_conflicting(other_action='deny',
- other_source='vm2',
- other_target='vm1')
- fundamental_rule = make_rule('@anyvm', '@anyvm', 'deny')
+ assert wrapped_rule.is_rule_conflicting(
+ other_action="deny", other_source="vm2", other_target="vm1"
+ )
+ fundamental_rule = make_rule("@anyvm", "@anyvm", "deny")
assert RuleSimple(fundamental_rule).is_rule_fundamental()
- assert RuleSimple.get_rule_errors('@anyvm', '@anyvm', 'deny') is None
+ assert RuleSimple.get_rule_errors("@anyvm", "@anyvm", "deny") is None
with pytest.raises(ValueError):
- wrapped_rule.action = 'allow target=dom0'
+ wrapped_rule.action = "allow target=dom0"
- wrong_rule_3 = make_rule('vm1', 'vm2', 'allow')
- wrong_rule_3.argument = '+alamakota'
+ wrong_rule_3 = make_rule("vm1", "vm2", "allow")
+ wrong_rule_3.argument = "+alamakota"
with pytest.raises(ValueError):
RuleSimple(wrong_rule_3)
def test_targeted_rule():
- deny_rule = make_rule('vm1', 'vm2', 'deny')
+ deny_rule = make_rule("vm1", "vm2", "deny")
wrapped_rule = RuleTargeted(deny_rule)
assert wrapped_rule.raw_rule == deny_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'deny'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "deny"
- allow_rule = make_rule('vm1', '@default', 'allow target=vm2')
+ allow_rule = make_rule("vm1", "@default", "allow target=vm2")
wrapped_rule = RuleTargeted(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "allow"
- ask_rule = make_rule('vm1', '@default', 'ask default_target=vm2')
+ ask_rule = make_rule("vm1", "@default", "ask default_target=vm2")
wrapped_rule = RuleTargeted(ask_rule)
assert wrapped_rule.raw_rule == ask_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'ask'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "ask"
- wrong_rule = make_rule('vm1', '@default', 'ask default_target=vm2')
- wrong_rule.argument = '+alamakota'
+ wrong_rule = make_rule("vm1", "@default", "ask default_target=vm2")
+ wrong_rule.argument = "+alamakota"
with pytest.raises(ValueError):
RuleTargeted(wrong_rule)
def test_targeted_adminvm_rule():
- deny_rule = make_rule('vm1', '@adminvm', 'deny')
+ deny_rule = make_rule("vm1", "@adminvm", "deny")
wrapped_rule = RuleTargetedAdminVM(deny_rule)
assert wrapped_rule.raw_rule == deny_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == '@adminvm'
- assert wrapped_rule.action == 'deny'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "@adminvm"
+ assert wrapped_rule.action == "deny"
- allow_rule = make_rule('vm1', '@adminvm', 'allow')
+ allow_rule = make_rule("vm1", "@adminvm", "allow")
wrapped_rule = RuleTargetedAdminVM(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == '@adminvm'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "@adminvm"
+ assert wrapped_rule.action == "allow"
- ask_rule = make_rule('vm1', '@adminvm', 'ask default_target=@adminvm')
+ ask_rule = make_rule("vm1", "@adminvm", "ask default_target=@adminvm")
wrapped_rule = RuleTargetedAdminVM(ask_rule)
assert wrapped_rule.raw_rule == ask_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == '@adminvm'
- assert wrapped_rule.action == 'ask'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "@adminvm"
+ assert wrapped_rule.action == "ask"
def test_targeted_rule_weird_cases():
# if we get fed wrong-ish values:
- allow_rule = make_rule('vm1', 'vm2', 'allow')
+ allow_rule = make_rule("vm1", "vm2", "allow")
wrapped_rule = RuleTargeted(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "allow"
# but it should get converted to expected things if set
- wrapped_rule.target = 'vm3'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@default', 'allow target=vm3'))
+ wrapped_rule.target = "vm3"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@default", "allow target=vm3")
+ )
- ask_rule = make_rule('vm1', 'vm2', 'ask')
+ ask_rule = make_rule("vm1", "vm2", "ask")
wrapped_rule = RuleTargeted(ask_rule)
assert wrapped_rule.raw_rule == ask_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'ask'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "ask"
# and same here
- wrapped_rule.target = 'vm3'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@default', 'ask default_target=vm3'))
+ wrapped_rule.target = "vm3"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@default", "ask default_target=vm3")
+ )
- wrong_rule = make_rule('vm1', '@adminvm', 'ask default_target=sys-usb')
+ wrong_rule = make_rule("vm1", "@adminvm", "ask default_target=sys-usb")
with pytest.raises(ValueError):
RuleTargetedAdminVM(wrong_rule)
- wrong_rule_2 = make_rule('vm1', 'sys-usb', 'ask default_target=@adminvm')
+ wrong_rule_2 = make_rule("vm1", "sys-usb", "ask default_target=@adminvm")
with pytest.raises(ValueError):
RuleTargetedAdminVM(wrong_rule_2)
- wrong_rule_3 = make_rule('vm1', 'vm2', 'allow')
- wrong_rule_3.argument = '+alamakota'
+ wrong_rule_3 = make_rule("vm1", "vm2", "allow")
+ wrong_rule_3.argument = "+alamakota"
with pytest.raises(ValueError):
RuleTargetedAdminVM(wrong_rule_3)
+
def test_targeted_tokens():
# can't make a rule with @anyvm target here
- allow_rule = make_rule('vm1', '@anyvm', 'allow')
+ allow_rule = make_rule("vm1", "@anyvm", "allow")
with pytest.raises(ValueError):
RuleTargeted(allow_rule)
# but dispvm is ok and should be treated like normal target
- allow_rule = make_rule('vm1', '@dispvm', 'allow')
+ allow_rule = make_rule("vm1", "@dispvm", "allow")
wrapped_rule = RuleTargeted(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == '@dispvm'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "@dispvm"
+ assert wrapped_rule.action == "allow"
- wrapped_rule.target = 'vm3'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@default', 'allow target=vm3'))
+ wrapped_rule.target = "vm3"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@default", "allow target=vm3")
+ )
- allow_rule = make_rule('vm1', '@default', 'allow target=vm2')
+ allow_rule = make_rule("vm1", "@default", "allow target=vm2")
wrapped_rule = RuleTargeted(allow_rule)
- wrapped_rule.target = '@anyvm'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@anyvm', 'allow'))
+ wrapped_rule.target = "@anyvm"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@anyvm", "allow")
+ )
- ask_rule = make_rule('vm1', '@default', 'ask default_target=vm2')
+ ask_rule = make_rule("vm1", "@default", "ask default_target=vm2")
wrapped_rule = RuleTargeted(ask_rule)
- wrapped_rule.target = '@anyvm'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@anyvm', 'ask'))
+ wrapped_rule.target = "@anyvm"
+ assert str(wrapped_rule.raw_rule) == str(make_rule("vm1", "@anyvm", "ask"))
def test_targeted_change_action():
- allow_rule = make_rule('vm1', '@default', 'allow target=vm2')
- ask_rule = make_rule('vm1', '@default', 'ask default_target=vm2')
+ allow_rule = make_rule("vm1", "@default", "allow target=vm2")
+ ask_rule = make_rule("vm1", "@default", "ask default_target=vm2")
wrapped_allow = RuleTargeted(allow_rule)
wrapped_ask = RuleTargeted(ask_rule)
- wrapped_allow.action = 'ask'
+ wrapped_allow.action = "ask"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
- allow_rule = make_rule('vm1', '@default', 'allow target=vm2')
- ask_rule = make_rule('vm1', '@default', 'ask default_target=vm2')
+ allow_rule = make_rule("vm1", "@default", "allow target=vm2")
+ ask_rule = make_rule("vm1", "@default", "ask default_target=vm2")
wrapped_allow = RuleTargeted(allow_rule)
wrapped_ask = RuleTargeted(ask_rule)
- wrapped_ask.action = 'allow'
+ wrapped_ask.action = "allow"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
def test_targeted_adminvm_change_action():
- allow_rule = make_rule('vm1', '@adminvm', 'allow')
- ask_rule = make_rule('vm1', '@adminvm', 'ask default_target=@adminvm')
+ allow_rule = make_rule("vm1", "@adminvm", "allow")
+ ask_rule = make_rule("vm1", "@adminvm", "ask default_target=@adminvm")
wrapped_allow = RuleTargetedAdminVM(allow_rule)
wrapped_ask = RuleTargetedAdminVM(ask_rule)
- wrapped_allow.action = 'ask'
+ wrapped_allow.action = "ask"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
- allow_rule = make_rule('vm1', '@adminvm', 'allow')
- ask_rule = make_rule('vm1', '@adminvm', 'ask default_target=@adminvm')
+ allow_rule = make_rule("vm1", "@adminvm", "allow")
+ ask_rule = make_rule("vm1", "@adminvm", "ask default_target=@adminvm")
wrapped_allow = RuleTargetedAdminVM(allow_rule)
wrapped_ask = RuleTargetedAdminVM(ask_rule)
- wrapped_ask.action = 'allow'
+ wrapped_ask.action = "allow"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
def test_targeted_adminvm_change_tokens():
- allow_rule = make_rule('vm1', '@adminvm', 'allow')
- ask_rule = make_rule('vm1', '@adminvm', 'ask default_target=@adminvm')
- deny_rule = make_rule('vm1', '@adminvm', 'deny')
+ allow_rule = make_rule("vm1", "@adminvm", "allow")
+ ask_rule = make_rule("vm1", "@adminvm", "ask default_target=@adminvm")
+ deny_rule = make_rule("vm1", "@adminvm", "deny")
wrapped_allow = RuleTargetedAdminVM(allow_rule)
wrapped_ask = RuleTargetedAdminVM(ask_rule)
wrapped_deny = RuleTargetedAdminVM(deny_rule)
with pytest.raises(ValueError):
- wrapped_allow.target = 'vm2'
+ wrapped_allow.target = "vm2"
with pytest.raises(ValueError):
- wrapped_ask.target = 'vm2'
+ wrapped_ask.target = "vm2"
with pytest.raises(ValueError):
- wrapped_deny.target = 'vm2'
+ wrapped_deny.target = "vm2"
- wrapped_allow.source = 'vm2'
- wrapped_ask.source = 'vm2'
- wrapped_deny.source = 'vm2'
+ wrapped_allow.source = "vm2"
+ wrapped_ask.source = "vm2"
+ wrapped_deny.source = "vm2"
- assert str(wrapped_allow.raw_rule) == \
- str(make_rule('vm2', '@adminvm', 'allow'))
- assert str(wrapped_ask.raw_rule) == \
- str(make_rule('vm2', '@adminvm', 'ask default_target=@adminvm'))
- assert str(wrapped_deny.raw_rule) == \
- str(make_rule('vm2', '@adminvm', 'deny'))
+ assert str(wrapped_allow.raw_rule) == str(
+ make_rule("vm2", "@adminvm", "allow")
+ )
+ assert str(wrapped_ask.raw_rule) == str(
+ make_rule("vm2", "@adminvm", "ask default_target=@adminvm")
+ )
+ assert str(wrapped_deny.raw_rule) == str(
+ make_rule("vm2", "@adminvm", "deny")
+ )
def test_targeted_fundamental():
- fundamental_rule = make_rule('@anyvm', '@anyvm', 'ask')
+ fundamental_rule = make_rule("@anyvm", "@anyvm", "ask")
assert RuleTargeted(fundamental_rule).is_rule_fundamental()
- fundamental_rule = make_rule('@anyvm', '@dispvm', 'allow')
+ fundamental_rule = make_rule("@anyvm", "@dispvm", "allow")
fundamental_wrapped = RuleTargeted(fundamental_rule)
assert fundamental_wrapped.is_rule_fundamental()
- fundamental_wrapped.target = 'vm2'
+ fundamental_wrapped.target = "vm2"
assert not fundamental_wrapped.is_rule_fundamental()
def test_targeted_validity():
- assert RuleTargeted.get_rule_errors(source='vm1', target='@anyvm',
- action='ask')
- assert RuleTargeted.get_rule_errors(source='vm1', target='@anyvm',
- action='allow')
- assert not RuleTargeted.get_rule_errors(source='vm1', target='@anyvm',
- action='deny')
-
- assert not RuleTargeted.get_rule_errors(source='vm1', target='@dispvm',
- action='ask')
- assert not RuleTargeted.get_rule_errors(source='vm1', target='@dispvm',
- action='allow')
- assert not RuleTargeted.get_rule_errors(source='vm1', target='@dispvm',
- action='deny')
-
- assert not RuleTargeted.get_rule_errors(source='vm1', target='vm2',
- action='ask')
- assert not RuleTargeted.get_rule_errors(source='vm1', target='vm2',
- action='allow')
- assert not RuleTargeted.get_rule_errors(source='vm1', target='vm2',
- action='deny')
+ assert RuleTargeted.get_rule_errors(
+ source="vm1", target="@anyvm", action="ask"
+ )
+ assert RuleTargeted.get_rule_errors(
+ source="vm1", target="@anyvm", action="allow"
+ )
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="@anyvm", action="deny"
+ )
+
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="@dispvm", action="ask"
+ )
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="@dispvm", action="allow"
+ )
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="@dispvm", action="deny"
+ )
+
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="vm2", action="ask"
+ )
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="vm2", action="allow"
+ )
+ assert not RuleTargeted.get_rule_errors(
+ source="vm1", target="vm2", action="deny"
+ )
def test_targeted_conflict():
- rule_allow = make_rule('vm1', '@default', 'allow target=vm2')
- rule_ask = make_rule('vm1', '@default', 'ask default_target=vm2')
- rule_deny = make_rule('vm1', 'vm2', 'deny')
+ rule_allow = make_rule("vm1", "@default", "allow target=vm2")
+ rule_ask = make_rule("vm1", "@default", "ask default_target=vm2")
+ rule_deny = make_rule("vm1", "vm2", "deny")
wrapped_allow = RuleTargeted(rule_allow)
wrapped_ask = RuleTargeted(rule_ask)
wrapped_deny = RuleTargeted(rule_deny)
- assert wrapped_allow.is_rule_conflicting(other_source='vm1',
- other_target='vm2',
- other_action='deny')
- assert wrapped_allow.is_rule_conflicting(other_source='vm1',
- other_target='vm2',
- other_action='ask')
- assert wrapped_allow.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='allow')
-
- assert wrapped_ask.is_rule_conflicting(other_source='vm1',
- other_target='vm2',
- other_action='deny')
- assert not wrapped_ask.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='allow')
- assert not wrapped_ask.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='ask')
-
- assert wrapped_deny.is_rule_conflicting(other_source='vm1',
- other_target='vm2',
- other_action='allow')
- assert not wrapped_deny.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='ask')
- assert not wrapped_deny.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='allow')
+ assert wrapped_allow.is_rule_conflicting(
+ other_source="vm1", other_target="vm2", other_action="deny"
+ )
+ assert wrapped_allow.is_rule_conflicting(
+ other_source="vm1", other_target="vm2", other_action="ask"
+ )
+ assert wrapped_allow.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="allow"
+ )
+
+ assert wrapped_ask.is_rule_conflicting(
+ other_source="vm1", other_target="vm2", other_action="deny"
+ )
+ assert not wrapped_ask.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="allow"
+ )
+ assert not wrapped_ask.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="ask"
+ )
+
+ assert wrapped_deny.is_rule_conflicting(
+ other_source="vm1", other_target="vm2", other_action="allow"
+ )
+ assert not wrapped_deny.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="ask"
+ )
+ assert not wrapped_deny.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="allow"
+ )
+
def test_dispvm_rule():
- deny_rule = make_rule('vm1', '@dispvm', 'deny')
+ deny_rule = make_rule("vm1", "@dispvm", "deny")
wrapped_rule = RuleDispVM(deny_rule)
assert wrapped_rule.raw_rule == deny_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == ''
- assert wrapped_rule.action == 'deny'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == ""
+ assert wrapped_rule.action == "deny"
- allow_rule = make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm2')
+ allow_rule = make_rule("vm1", "@dispvm", "allow target=@dispvm:vm2")
wrapped_rule = RuleDispVM(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "allow"
- ask_rule = make_rule('vm1', '@dispvm', 'ask default_target=@dispvm:vm2')
+ ask_rule = make_rule("vm1", "@dispvm", "ask default_target=@dispvm:vm2")
wrapped_rule = RuleDispVM(ask_rule)
assert wrapped_rule.raw_rule == ask_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'ask'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "ask"
def test_dispvm_tokens():
# treat dispvm correctly
- allow_rule = make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm2')
+ allow_rule = make_rule("vm1", "@dispvm", "allow target=@dispvm:vm2")
wrapped_rule = RuleDispVM(allow_rule)
assert wrapped_rule.raw_rule == allow_rule
- assert wrapped_rule.source == 'vm1'
- assert wrapped_rule.target == 'vm2'
- assert wrapped_rule.action == 'allow'
+ assert wrapped_rule.source == "vm1"
+ assert wrapped_rule.target == "vm2"
+ assert wrapped_rule.action == "allow"
- wrapped_rule.target = 'vm3'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm3'))
- wrapped_rule.target = '@dispvm'
- assert str(wrapped_rule.raw_rule) == \
- str(make_rule('vm1', '@dispvm', 'allow target=@dispvm'))
+ wrapped_rule.target = "vm3"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@dispvm", "allow target=@dispvm:vm3")
+ )
+ wrapped_rule.target = "@dispvm"
+ assert str(wrapped_rule.raw_rule) == str(
+ make_rule("vm1", "@dispvm", "allow target=@dispvm")
+ )
def test_dispvm_change_action():
- allow_rule = make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm2')
- ask_rule = make_rule('vm1', '@dispvm', 'ask default_target=@dispvm:vm2')
+ allow_rule = make_rule("vm1", "@dispvm", "allow target=@dispvm:vm2")
+ ask_rule = make_rule("vm1", "@dispvm", "ask default_target=@dispvm:vm2")
wrapped_allow = RuleDispVM(allow_rule)
wrapped_ask = RuleDispVM(ask_rule)
- wrapped_allow.action = 'ask'
+ wrapped_allow.action = "ask"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
- allow_rule = make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm2')
- ask_rule = make_rule('vm1', '@dispvm', 'ask default_target=@dispvm:vm2')
- deny_rule = make_rule('vm1', '@dispvm', 'deny')
+ allow_rule = make_rule("vm1", "@dispvm", "allow target=@dispvm:vm2")
+ ask_rule = make_rule("vm1", "@dispvm", "ask default_target=@dispvm:vm2")
+ deny_rule = make_rule("vm1", "@dispvm", "deny")
wrapped_allow = RuleDispVM(allow_rule)
wrapped_ask = RuleDispVM(ask_rule)
wrapped_deny = RuleDispVM(deny_rule)
- wrapped_ask.action = 'allow'
+ wrapped_ask.action = "allow"
assert str(wrapped_allow.raw_rule) == str(wrapped_ask.raw_rule)
- wrapped_ask.action = 'deny'
+ wrapped_ask.action = "deny"
assert str(wrapped_deny.raw_rule) == str(wrapped_ask.raw_rule)
def test_dispvm_validity():
with pytest.raises(ValueError):
- rule = make_rule('vm1', 'vm2', 'deny')
+ rule = make_rule("vm1", "vm2", "deny")
RuleDispVM(rule)
with pytest.raises(ValueError):
- rule = make_rule('vm1', '@dispvm', 'ask')
+ rule = make_rule("vm1", "@dispvm", "ask")
RuleDispVM(rule)
with pytest.raises(ValueError):
- rule = make_rule('vm1', '@dispvm', 'allow')
+ rule = make_rule("vm1", "@dispvm", "allow")
RuleDispVM(rule)
with pytest.raises(ValueError):
- rule = make_rule('vm1', 'vm2', 'allow target=vm2')
+ rule = make_rule("vm1", "vm2", "allow target=vm2")
RuleDispVM(rule)
with pytest.raises(ValueError):
- rule = make_rule('vm1', '@dispvm', 'allow target=vm2')
+ rule = make_rule("vm1", "@dispvm", "allow target=vm2")
RuleDispVM(rule)
with pytest.raises(ValueError):
- rule = make_rule('vm1', '@dispvm', 'allow target=vm2')
+ rule = make_rule("vm1", "@dispvm", "allow target=vm2")
rule.argument = "+alamakota"
RuleDispVM(rule)
def test_dispvm_conflict():
- rule_allow = make_rule('vm1', '@dispvm', 'allow target=@dispvm:vm2')
- rule_ask = make_rule('vm1', '@dispvm', 'ask default_target=@dispvm:vm2')
- rule_deny = make_rule('vm1', '@dispvm', 'deny')
+ rule_allow = make_rule("vm1", "@dispvm", "allow target=@dispvm:vm2")
+ rule_ask = make_rule("vm1", "@dispvm", "ask default_target=@dispvm:vm2")
+ rule_deny = make_rule("vm1", "@dispvm", "deny")
wrapped_allow = RuleDispVM(rule_allow)
wrapped_ask = RuleDispVM(rule_ask)
wrapped_deny = RuleDispVM(rule_deny)
for rule in [wrapped_ask, wrapped_allow, wrapped_deny]:
- assert rule.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='deny')
+ assert rule.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="deny"
+ )
- assert rule.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='ask')
+ assert rule.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="ask"
+ )
- assert rule.is_rule_conflicting(other_source='vm1',
- other_target='vm3',
- other_action='allow')
+ assert rule.is_rule_conflicting(
+ other_source="vm1", other_target="vm3", other_action="allow"
+ )
diff --git a/qubes_config/tests/test_rule_list_widgets.py b/qubes_config/tests/test_rule_list_widgets.py
index d9070e9a..5fbe06d8 100644
--- a/qubes_config/tests/test_rule_list_widgets.py
+++ b/qubes_config/tests/test_rule_list_widgets.py
@@ -24,29 +24,41 @@
from qrexec.policy.parser import Rule
from ..global_config.policy_handler import PolicyHandler
from ..global_config.policy_rules import RuleSimple, SimpleVerbDescription
-from ..global_config.rule_list_widgets import VMWidget, ActionWidget,\
- RuleListBoxRow, NoActionListBoxRow, FilteredListBoxRow
+from ..global_config.rule_list_widgets import (
+ VMWidget,
+ ActionWidget,
+ RuleListBoxRow,
+ NoActionListBoxRow,
+ FilteredListBoxRow,
+)
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
def make_rule(source, target, action):
- return RuleSimple(Rule.from_line(
- None, f"Service\t*\t{source}\t{target}\t{action}",
- filepath=None, lineno=0))
+ return RuleSimple(
+ Rule.from_line(
+ None,
+ f"Service\t*\t{source}\t{target}\t{action}",
+ filepath=None,
+ lineno=0,
+ )
+ )
+
+
+VERB_DESCR = SimpleVerbDescription(
+ {"ask": "ASK", "allow": "ALLOW", "deny": "DENY"}
+)
-VERB_DESCR = SimpleVerbDescription({
- 'ask': 'ASK',
- 'allow': 'ALLOW',
- 'deny': 'DENY'
-})
def test_vm_widget(test_qapp):
- simple_widget = VMWidget(qapp=test_qapp, categories=None,
- initial_value='test-vm')
+ simple_widget = VMWidget(
+ qapp=test_qapp, categories=None, initial_value="test-vm"
+ )
# test if parts of the widgets behave as expected
# at the start, name should be visible and combo hidden
@@ -59,38 +71,39 @@ def test_vm_widget(test_qapp):
def test_vm_widget_changes(test_qapp):
- simple_widget = VMWidget(qapp=test_qapp, categories=None,
- initial_value='test-vm')
+ simple_widget = VMWidget(
+ qapp=test_qapp, categories=None, initial_value="test-vm"
+ )
assert not simple_widget.is_changed()
- assert str(simple_widget.get_selected()) == 'test-vm'
+ assert str(simple_widget.get_selected()) == "test-vm"
simple_widget.set_editable(True)
- simple_widget.combobox.set_active_id('test-blue')
+ simple_widget.combobox.set_active_id("test-blue")
assert simple_widget.is_changed()
# get back to ineditable, change should be discarded
simple_widget.set_editable(False)
- assert simple_widget.name_widget.token_name == 'test-vm'
- assert simple_widget.combobox.get_active_id() == 'test-vm'
- assert str(simple_widget.get_selected()) == 'test-vm'
+ assert simple_widget.name_widget.token_name == "test-vm"
+ assert simple_widget.combobox.get_active_id() == "test-vm"
+ assert str(simple_widget.get_selected()) == "test-vm"
assert not simple_widget.is_changed()
# let's change stuff for real
simple_widget.set_editable(True)
- simple_widget.combobox.set_active_id('test-blue')
+ simple_widget.combobox.set_active_id("test-blue")
assert simple_widget.is_changed()
simple_widget.save()
simple_widget.set_editable(False)
assert not simple_widget.is_changed()
- assert simple_widget.name_widget.token_name == 'test-blue'
- assert simple_widget.combobox.get_active_id() == 'test-blue'
- assert str(simple_widget.get_selected()) == 'test-blue'
+ assert simple_widget.name_widget.token_name == "test-blue"
+ assert simple_widget.combobox.get_active_id() == "test-blue"
+ assert str(simple_widget.get_selected()) == "test-blue"
def test_action_widget():
- rule = make_rule('vm1', 'vm2', 'allow')
+ rule = make_rule("vm1", "vm2", "allow")
action_widget = ActionWidget(rule.ACTION_CHOICES, VERB_DESCR, rule)
assert action_widget.name_widget.get_visible()
@@ -100,81 +113,92 @@ def test_action_widget():
assert not action_widget.name_widget.get_visible()
assert action_widget.combobox.get_visible()
+
def test_action_widget_choices():
# RuleSimple names:
- rule = make_rule('vm1', 'vm2', 'allow')
+ rule = make_rule("vm1", "vm2", "allow")
action_widget = ActionWidget(rule.ACTION_CHOICES, None, rule)
assert not action_widget.is_changed()
- assert str(action_widget.get_selected()) == 'allow'
- assert action_widget.name_widget.get_text() == \
- RuleSimple.ACTION_CHOICES['allow']
+ assert str(action_widget.get_selected()) == "allow"
+ assert (
+ action_widget.name_widget.get_text()
+ == RuleSimple.ACTION_CHOICES["allow"]
+ )
action_widget.set_editable(True)
- action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES['ask'])
+ action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES["ask"])
assert action_widget.is_changed()
# get back to ineditable, change should be discarded
action_widget.set_editable(False)
- assert str(action_widget.get_selected()) == 'allow'
- assert action_widget.name_widget.get_text() == \
- RuleSimple.ACTION_CHOICES['allow']
- assert action_widget.combobox.get_active_id() == \
- RuleSimple.ACTION_CHOICES['allow']
+ assert str(action_widget.get_selected()) == "allow"
+ assert (
+ action_widget.name_widget.get_text()
+ == RuleSimple.ACTION_CHOICES["allow"]
+ )
+ assert (
+ action_widget.combobox.get_active_id()
+ == RuleSimple.ACTION_CHOICES["allow"]
+ )
assert not action_widget.is_changed()
# let's change stuff for real
action_widget.set_editable(True)
- action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES['ask'])
+ action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES["ask"])
assert action_widget.is_changed()
action_widget.save()
action_widget.set_editable(False)
assert not action_widget.is_changed()
- assert str(action_widget.get_selected()) == 'ask'
- assert action_widget.name_widget.get_text() == \
- RuleSimple.ACTION_CHOICES['ask']
- assert action_widget.combobox.get_active_id() == \
- RuleSimple.ACTION_CHOICES['ask']
+ assert str(action_widget.get_selected()) == "ask"
+ assert (
+ action_widget.name_widget.get_text() == RuleSimple.ACTION_CHOICES["ask"]
+ )
+ assert (
+ action_widget.combobox.get_active_id()
+ == RuleSimple.ACTION_CHOICES["ask"]
+ )
+
def test_action_widget_verbdescr():
# RuleSimple names:
- rule = make_rule('vm1', 'vm2', 'ask')
+ rule = make_rule("vm1", "vm2", "ask")
action_widget = ActionWidget(rule.ACTION_CHOICES, VERB_DESCR, rule)
assert action_widget.additional_text_widget
ask_text = action_widget.additional_text_widget.get_text()
- assert ask_text.endswith('ASK')
+ assert ask_text.endswith("ASK")
- action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES['allow'])
+ action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES["allow"])
allow_text = action_widget.additional_text_widget.get_text()
- assert allow_text.endswith('ALLOW')
+ assert allow_text.endswith("ALLOW")
- action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES['deny'])
+ action_widget.combobox.set_active_id(RuleSimple.ACTION_CHOICES["deny"])
deny_text = action_widget.additional_text_widget.get_text()
- assert deny_text.endswith('DENY')
+ assert deny_text.endswith("DENY")
assert len(ask_text) == len(allow_text) == len(deny_text)
+
def test_rule_row(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = RuleListBoxRow(
- parent_handler=mock_handler,
- rule=rule,
- qapp=test_qapp)
+ parent_handler=mock_handler, rule=rule, qapp=test_qapp
+ )
# it should start in a non-editable mode
assert not rule_row.action_widget.combobox.get_visible()
assert not rule_row.target_widget.combobox.get_visible()
assert not rule_row.source_widget.combobox.get_visible()
- assert rule_row.action_widget.get_selected() == 'ask'
- assert rule_row.source_widget.get_selected() == 'test-blue'
- assert rule_row.target_widget.get_selected() == 'test-red'
+ assert rule_row.action_widget.get_selected() == "ask"
+ assert rule_row.source_widget.get_selected() == "test-blue"
+ assert rule_row.target_widget.get_selected() == "test-red"
# but when we set it to editable, it should work
rule_row.set_edit_mode(True)
@@ -183,95 +207,90 @@ def test_rule_row(test_qapp):
assert rule_row.source_widget.combobox.get_visible()
# when changes are made and reverted, they should be not shown
- rule_row.source_widget.model.select_value('test-vm')
- assert rule_row.source_widget.get_selected() == 'test-vm'
+ rule_row.source_widget.model.select_value("test-vm")
+ assert rule_row.source_widget.get_selected() == "test-vm"
assert rule_row.is_changed()
rule_row.revert()
- assert rule_row.source_widget.get_selected() == 'test-blue'
+ assert rule_row.source_widget.get_selected() == "test-blue"
assert not rule_row.is_changed()
# and let's try making some changes
rule_row.set_edit_mode(True)
- rule_row.source_widget.model.select_value('test-vm')
+ rule_row.source_widget.model.select_value("test-vm")
assert rule_row.is_changed()
# the patch is necessary because the row will try to invalidate
# sorting... all well and good, but it has no parent here
- with patch.object(rule_row, 'get_parent'):
+ with patch.object(rule_row, "get_parent"):
assert rule_row.validate_and_save()
- assert str(rule_row.rule.raw_rule) == \
- str(make_rule('test-vm', 'test-red', 'ask').raw_rule)
+ assert str(rule_row.rule.raw_rule) == str(
+ make_rule("test-vm", "test-red", "ask").raw_rule
+ )
def test_rule_delete_new(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = RuleListBoxRow(
- parent_handler=mock_handler,
- rule=rule,
- qapp=test_qapp,
- is_new_row=True
+ parent_handler=mock_handler, rule=rule, qapp=test_qapp, is_new_row=True
)
rule_row.set_edit_mode(True)
- with patch('qubes_config.global_config.rule_list_widgets.'
- 'RuleListBoxRow._do_delete_self') as mock_delete:
+ with patch(
+ "qubes_config.global_config.rule_list_widgets."
+ "RuleListBoxRow._do_delete_self"
+ ) as mock_delete:
# check that rule will try to delete itself if exiting edit mode
# with no changes
rule_row.set_edit_mode(False)
assert mock_delete.mock_calls
rule_row = RuleListBoxRow(
- parent_handler=mock_handler,
- rule=rule,
- qapp=test_qapp,
- is_new_row=True
+ parent_handler=mock_handler, rule=rule, qapp=test_qapp, is_new_row=True
)
rule_row.set_edit_mode(True)
- rule_row.source_widget.model.select_value('test-vm')
- with patch('qubes_config.global_config.rule_list_widgets.'
- 'RuleListBoxRow._do_delete_self') as mock_delete, \
- patch.object(rule_row, 'get_parent'):
+ rule_row.source_widget.model.select_value("test-vm")
+ with patch(
+ "qubes_config.global_config.rule_list_widgets."
+ "RuleListBoxRow._do_delete_self"
+ ) as mock_delete, patch.object(rule_row, "get_parent"):
# check that rule will NOT try to delete itself if saving with changes
assert rule_row.validate_and_save()
assert not mock_delete.mock_calls
# and now for a harder problem: let's try an invalid rule; it should
# not save itself and try to delete itself
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = RuleListBoxRow(
- parent_handler=mock_handler,
- rule=rule,
- qapp=test_qapp,
- is_new_row=True
+ parent_handler=mock_handler, rule=rule, qapp=test_qapp, is_new_row=True
)
rule_row.set_edit_mode(True)
- rule_row.source_widget.model.select_value('test-vm')
+ rule_row.source_widget.model.select_value("test-vm")
assert rule_row.is_changed()
# the rule should not have exited the edit mode
- with patch.object(rule, 'get_rule_errors', return_value='a'), \
- patch('qubes_config.global_config.rule_list_widgets.'
- 'show_error') as mock_error:
+ with patch.object(rule, "get_rule_errors", return_value="a"), patch(
+ "qubes_config.global_config.rule_list_widgets.show_error"
+ ) as mock_error:
rule_row.validate_and_save()
assert mock_error.mock_calls
+
def test_no_action_row(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = NoActionListBoxRow(
- parent_handler=mock_handler,
- rule=rule,
- qapp=test_qapp)
+ parent_handler=mock_handler, rule=rule, qapp=test_qapp
+ )
assert not rule_row.action_widget.get_visible()
rule_row.set_edit_mode(True)
@@ -283,18 +302,18 @@ def test_no_action_row(test_qapp):
def test_limited_selection_row(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = FilteredListBoxRow(
parent_handler=mock_handler,
rule=rule,
qapp=test_qapp,
- filter_target=lambda vm: 'test' in vm.name
+ filter_target=lambda vm: "test" in vm.name,
)
- vm_blue = test_qapp.domains['test-blue']
- vm_red = test_qapp.domains['test-red']
- vm_test = test_qapp.domains['test-vm']
- vm_vault = test_qapp.domains['vault']
+ vm_blue = test_qapp.domains["test-blue"]
+ vm_red = test_qapp.domains["test-red"]
+ vm_test = test_qapp.domains["test-vm"]
+ vm_vault = test_qapp.domains["vault"]
# target available vms should be limited:
assert rule_row.target_widget.model.is_vm_available(vm_blue)
@@ -306,13 +325,13 @@ def test_limited_selection_row(test_qapp):
def test_rule_row_init_delete(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = RuleListBoxRow(
parent_handler=mock_handler,
rule=rule,
qapp=test_qapp,
- enable_delete=False
+ enable_delete=False,
)
# try to find a non-sensitive delete button
@@ -322,12 +341,11 @@ def test_rule_row_init_delete(test_qapp):
else:
assert False # failed to find a non-working delete button
-
rule_row = RuleListBoxRow(
parent_handler=mock_handler,
rule=rule,
qapp=test_qapp,
- enable_delete=True
+ enable_delete=True,
)
# try to find a sensitive delete button
@@ -337,16 +355,18 @@ def test_rule_row_init_delete(test_qapp):
else:
assert False # failed to find a non-working delete button
+
def test_rule_row_init_edit_vm(test_qapp):
mock_handler = Mock(spec=PolicyHandler)
mock_handler.verify_new_rule.return_value = None
- rule = make_rule('test-blue', 'test-red', 'ask')
+ rule = make_rule("test-blue", "test-red", "ask")
rule_row = RuleListBoxRow(
parent_handler=mock_handler,
rule=rule,
qapp=test_qapp,
- enable_vm_edit=False)
+ enable_vm_edit=False,
+ )
assert not rule_row.source_widget.combobox.get_visible()
assert not rule_row.target_widget.combobox.get_visible()
diff --git a/qubes_config/tests/test_update_handler.py b/qubes_config/tests/test_update_handler.py
index d8e96ee2..1b04d998 100644
--- a/qubes_config/tests/test_update_handler.py
+++ b/qubes_config/tests/test_update_handler.py
@@ -27,17 +27,22 @@
import pytest
import qubesadmin.exc
-from ..global_config.updates_handler import RepoHandler, UpdateCheckerHandler, \
- UpdateProxy, UpdatesHandler
+from ..global_config.updates_handler import (
+ RepoHandler,
+ UpdateCheckerHandler,
+ UpdateProxy,
+ UpdatesHandler,
+)
from ..global_config.rule_list_widgets import NoActionListBoxRow
import gi
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MockProcess:
- def __init__(self, stdout=b'', returncode=0, stderr=None):
+ def __init__(self, stdout=b"", returncode=0, stderr=None):
self.stderr = stderr
self.returncode = returncode
self.stdout = stdout
@@ -64,7 +69,7 @@ def __init__(self, stdout=b'', returncode=0, stderr=None):
MISSING = """qubes-dom0-current\0c\0enabled"""
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
def test_repo_handler_all(mock_output, real_builder):
mock_output.return_value = ALL_ENABLED
handler = RepoHandler(real_builder)
@@ -75,7 +80,7 @@ def test_repo_handler_all(mock_output, real_builder):
assert handler.template_community_testing.get_active()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
def test_repo_handler_minimal(mock_output, real_builder):
mock_output.return_value = MINIMAL
handler = RepoHandler(real_builder)
@@ -87,7 +92,7 @@ def test_repo_handler_minimal(mock_output, real_builder):
assert not handler.template_community_testing.get_sensitive()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
def test_repo_handler_missing_repos(mock_output, real_builder):
mock_output.return_value = MISSING
handler = RepoHandler(real_builder)
@@ -99,7 +104,7 @@ def test_repo_handler_missing_repos(mock_output, real_builder):
assert not handler.template_community_testing.get_sensitive()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
def test_repo_handler_error(mock_output, real_builder):
mock_output.side_effect = RuntimeError()
handler = RepoHandler(real_builder)
@@ -112,25 +117,28 @@ def test_repo_handler_error(mock_output, real_builder):
assert handler.problems_repo_box.get_visible()
-def mock_qrexec(_vm, service, arg, repo_list="",
- enable_repos=None, disable_repos=None):
- if 'qubes.repos.List' in service:
+def mock_qrexec(
+ _vm, service, arg, repo_list="", enable_repos=None, disable_repos=None
+):
+ if "qubes.repos.List" in service:
return repo_list
- if 'qubes.repos.Enable' in service:
+ if "qubes.repos.Enable" in service:
if enable_repos and arg in enable_repos:
- return 'ok\n'
+ return "ok\n"
- if 'qubes.repos.Disable' in service:
+ if "qubes.repos.Disable" in service:
if disable_repos and arg in disable_repos:
- return 'ok\n'
+ return "ok\n"
assert False
def test_repo_handler_save(real_builder):
- with patch('qubes_config.global_config.updates_handler.qrexec_call',
- partial(mock_qrexec, repo_list=ALL_ENABLED)):
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call",
+ partial(mock_qrexec, repo_list=ALL_ENABLED),
+ ):
handler = RepoHandler(real_builder)
assert handler.dom0_stable_radio.get_sensitive()
@@ -149,21 +157,32 @@ def test_repo_handler_save(real_builder):
qubes-templates-community-testing\0c\0disabled
qubes-templates-community\0c\0enabled"""
- with patch('qubes_config.global_config.updates_handler.qrexec_call',
- partial(mock_qrexec, repo_list=changed_result,
- enable_repos=['qubes-dom0-current',
- 'qubes-templates-itl',
- 'qubes-templates-community',],
- disable_repos=['qubes-dom0-security-testing',
- 'qubes-dom0-current-testing',
- 'qubes-templates-itl-testing',
- 'qubes-templates-community-testing'])):
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call",
+ partial(
+ mock_qrexec,
+ repo_list=changed_result,
+ enable_repos=[
+ "qubes-dom0-current",
+ "qubes-templates-itl",
+ "qubes-templates-community",
+ ],
+ disable_repos=[
+ "qubes-dom0-security-testing",
+ "qubes-dom0-current-testing",
+ "qubes-templates-itl-testing",
+ "qubes-templates-community-testing",
+ ],
+ ),
+ ):
handler.save()
def test_repo_handler_save_2(real_builder):
- with patch('qubes_config.global_config.updates_handler.qrexec_call',
- partial(mock_qrexec, repo_list=ALL_ENABLED)):
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call",
+ partial(mock_qrexec, repo_list=ALL_ENABLED),
+ ):
handler = RepoHandler(real_builder)
assert handler.dom0_stable_radio.get_sensitive()
@@ -178,55 +197,65 @@ def test_repo_handler_save_2(real_builder):
qubes-templates-community-testing\0c\0enabled
qubes-templates-community\0c\0enabled"""
- with patch('qubes_config.global_config.updates_handler.qrexec_call',
- partial(mock_qrexec, repo_list=changed_result,
- enable_repos=['qubes-dom0-current',
- 'qubes-dom0-security-testing',
- 'qubes-templates-itl',
- 'qubes-templates-community',
- 'qubes-templates-itl-testing',
- 'qubes-templates-community-testing'
- ],
- disable_repos=['qubes-dom0-current-testing'])):
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call",
+ partial(
+ mock_qrexec,
+ repo_list=changed_result,
+ enable_repos=[
+ "qubes-dom0-current",
+ "qubes-dom0-security-testing",
+ "qubes-templates-itl",
+ "qubes-templates-community",
+ "qubes-templates-itl-testing",
+ "qubes-templates-community-testing",
+ ],
+ disable_repos=["qubes-dom0-current-testing"],
+ ),
+ ):
handler.save()
-
def test_repo_handler_save_fail(real_builder):
- with patch('qubes_config.global_config.updates_handler.qrexec_call',
- partial(mock_qrexec, repo_list=ALL_ENABLED)):
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call",
+ partial(mock_qrexec, repo_list=ALL_ENABLED),
+ ):
handler = RepoHandler(real_builder)
handler.dom0_stable_radio.set_active(True)
- with patch('qubes_config.global_config.updates_handler.qrexec_call') as \
- mock_output:
+ with patch(
+ "qubes_config.global_config.updates_handler.qrexec_call"
+ ) as mock_output:
mock_output.side_effect = RuntimeError()
with pytest.raises(qubesadmin.exc.QubesException):
handler.save()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
def test_repo_handler_unsaved(mock_output, real_builder):
mock_output.return_value = MINIMAL
handler = RepoHandler(real_builder)
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
handler.dom0_testing_radio.set_active(True)
- assert handler.get_unsaved() == 'dom0 update source'
+ assert handler.get_unsaved() == "dom0 update source"
handler.dom0_stable_radio.set_active(True)
handler.template_official_testing.set_active(True)
- assert handler.get_unsaved() == 'Official template update source'
+ assert handler.get_unsaved() == "Official template update source"
handler.template_community.set_active(True)
handler.template_community_testing.set_active(True)
- assert handler.get_unsaved() == 'Official template update source\n' \
- 'Community template update source'
+ assert (
+ handler.get_unsaved() == "Official template update source\n"
+ "Community template update source"
+ )
handler.reset()
@@ -236,26 +265,26 @@ def test_repo_handler_unsaved(mock_output, real_builder):
assert not handler.template_community_testing.get_active()
assert not handler.template_community.get_active()
assert not handler.template_community_testing.get_sensitive()
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
def test_updates_checker_dom0(real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x001'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x001"
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert handler.dom0_update_check.get_active()
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert not handler.dom0_update_check.get_active()
# default for this feature is Enabled
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00feature1\x00'
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"2\x00QubesFeatureNotFoundError\x00\x00feature1\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert handler.dom0_update_check.get_active()
@@ -269,64 +298,99 @@ def test_updates_checker_init_state(real_builder, test_qapp):
assert not handler.flowbox_handler.selected_vms
# disable enable check for 2 vms, explicitly enable for one
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ ("test-vm", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert handler.enable_radio.get_active()
assert handler.exceptions_check.get_active()
- assert handler.flowbox_handler.selected_vms == \
- [test_qapp.domains['test-blue'], test_qapp.domains['test-red']]
+ assert handler.flowbox_handler.selected_vms == [
+ test_qapp.domains["test-blue"],
+ test_qapp.domains["test-red"],
+ ]
+
def test_updates_checker_init_disabled(real_builder, test_qapp):
# set default to disabled (remember system default is still enabled)
# disable it in two qubes explicitly
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'0\x00'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
# names for easier comparison in case of errors
- expected_qubes = [vm.name for vm in test_qapp.domains
- if vm.name not in ['test-red', 'test-blue', 'dom0']]
+ expected_qubes = [
+ vm.name
+ for vm in test_qapp.domains
+ if vm.name not in ["test-red", "test-blue", "dom0"]
+ ]
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert handler.disable_radio.get_active()
assert handler.exceptions_check.get_active()
- assert [str(vm) for vm in handler.flowbox_handler.selected_vms] == \
- expected_qubes
+ assert [
+ str(vm) for vm in handler.flowbox_handler.selected_vms
+ ] == expected_qubes
def test_updates_checker_exceptions(real_builder, test_qapp):
# explicit enable in one vm, explicit disable in one vm
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
# no default
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00config.default.' \
- b'qubes-update-check\x00'
-
- disabled_vms = ['test-blue']
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00config.default."
+ b"qubes-update-check\x00"
+ )
+
+ disabled_vms = ["test-blue"]
handler = UpdateCheckerHandler(real_builder, test_qapp)
assert handler.enable_radio.get_active()
assert handler.exceptions_check.get_active()
- assert [str(vm) for vm in handler.flowbox_handler.selected_vms] == \
- disabled_vms
+ assert [
+ str(vm) for vm in handler.flowbox_handler.selected_vms
+ ] == disabled_vms
# switch to disable
handler.disable_radio.set_active(True)
@@ -346,34 +410,46 @@ def test_updates_checker_get_unsaved(real_builder, test_qapp):
assert handler.get_unsaved() == ""
handler.dom0_update_check.set_active(False)
- assert 'dom0' in handler.get_unsaved()
+ assert "dom0" in handler.get_unsaved()
handler.dom0_update_check.set_active(True)
assert handler.get_unsaved() == ""
handler.disable_radio.set_active(True)
- assert 'Default' in handler.get_unsaved()
+ assert "Default" in handler.get_unsaved()
handler.enable_radio.set_active(True)
assert handler.get_unsaved() == ""
handler.disable_radio.set_active(True)
handler.dom0_update_check.set_active(False)
- assert 'dom0' in handler.get_unsaved()
- assert 'Default' in handler.get_unsaved()
+ assert "dom0" in handler.get_unsaved()
+ assert "Default" in handler.get_unsaved()
def test_updates_checker_get_unsaved_choice(real_builder, test_qapp):
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
# no default
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00config.default.' \
- b'qubes-update-check\x00'
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00config.default."
+ b"qubes-update-check\x00"
+ )
handler = UpdateCheckerHandler(real_builder, test_qapp)
@@ -381,18 +457,18 @@ def test_updates_checker_get_unsaved_choice(real_builder, test_qapp):
assert handler.enable_radio.get_active()
# add a qube
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-red'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-red"])
- assert 'Qubes' in handler.get_unsaved()
+ assert "Qubes" in handler.get_unsaved()
# disable and enable
handler.disable_radio.set_active(True)
- assert 'Default' in handler.get_unsaved()
- assert 'Qubes' in handler.get_unsaved()
+ assert "Default" in handler.get_unsaved()
+ assert "Qubes" in handler.get_unsaved()
handler.enable_radio.set_active(True)
- assert 'Default' not in handler.get_unsaved()
- assert 'Qubes' in handler.get_unsaved()
+ assert "Default" not in handler.get_unsaved()
+ assert "Qubes" in handler.get_unsaved()
# reset flowbox
handler.exceptions_check.set_active(True)
@@ -401,7 +477,7 @@ def test_updates_checker_get_unsaved_choice(real_builder, test_qapp):
assert handler.get_unsaved() == ""
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
def test_updates_checker_save_dom0(mock_feature, real_builder, test_qapp):
handler = UpdateCheckerHandler(real_builder, test_qapp)
@@ -409,17 +485,25 @@ def test_updates_checker_save_dom0(mock_feature, real_builder, test_qapp):
handler.save()
# only this feature was changed
- mock_feature.assert_called_with('dom0', handler.FEATURE_NAME, False)
+ mock_feature.assert_called_with("dom0", handler.FEATURE_NAME, False)
assert len(mock_feature.mock_calls) == 1
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_updates_checker_save_dom0_initial_none(mock_feature,
- real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- UpdateCheckerHandler.FEATURE_NAME, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00service.' \
- b'qubes-update-check\x00'
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_updates_checker_save_dom0_initial_none(
+ mock_feature, real_builder, test_qapp
+):
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ UpdateCheckerHandler.FEATURE_NAME,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00service."
+ b"qubes-update-check\x00"
+ )
handler = UpdateCheckerHandler(real_builder, test_qapp)
@@ -429,221 +513,299 @@ def test_updates_checker_save_dom0_initial_none(mock_feature,
# nothing should have been changed
assert not mock_feature.mock_calls
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_updates_checker_save_add_exception(mock_feature,
- real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_updates_checker_save_add_exception(
+ mock_feature, real_builder, test_qapp
+):
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-blue'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-blue"])
handler.save()
assert len(mock_feature.mock_calls) == 1
- mock_feature.assert_called_with(test_qapp.domains['test-blue'],
- handler.FEATURE_NAME, False)
-
-
-@patch('qubes_config.global_config.vm_flowbox.ask_question')
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_updates_checker_save_del_exception(mock_feature,
- mock_question,
- real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ mock_feature.assert_called_with(
+ test_qapp.domains["test-blue"], handler.FEATURE_NAME, False
+ )
+
+
+@patch("qubes_config.global_config.vm_flowbox.ask_question")
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_updates_checker_save_del_exception(
+ mock_feature, mock_question, real_builder, test_qapp
+):
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
mock_question.return_value = Gtk.ResponseType.YES
for child in handler.flowbox_handler.flowbox.get_children():
- if hasattr(child, 'vm') and str(child.vm) == 'test-blue':
+ if hasattr(child, "vm") and str(child.vm) == "test-blue":
child._remove_self()
assert mock_question.mock_calls
handler.save()
assert len(mock_feature.mock_calls) == 1
- mock_feature.assert_called_with(test_qapp.domains['test-blue'],
- handler.FEATURE_NAME, None)
+ mock_feature.assert_called_with(
+ test_qapp.domains["test-blue"], handler.FEATURE_NAME, None
+ )
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_updates_checker_save_change_default(mock_feature,
- real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_updates_checker_save_change_default(
+ mock_feature, real_builder, test_qapp
+):
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
handler.disable_radio.set_active(True)
handler.exceptions_check.set_active(True)
# add a qube
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-blue'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-blue"])
# re-add test-red
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-red'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-red"])
handler.save()
- assert call(test_qapp.domains['dom0'],
- 'config.default.qubes-update-check', False) \
- in mock_feature.mock_calls
+ assert (
+ call(
+ test_qapp.domains["dom0"],
+ "config.default.qubes-update-check",
+ False,
+ )
+ in mock_feature.mock_calls
+ )
counter = 1
for vm in test_qapp.domains:
- if vm.klass == 'AdminVM':
+ if vm.klass == "AdminVM":
continue
- if vm.name == 'test-blue':
+ if vm.name == "test-blue":
# this vm wasn't actually changed
continue
- state = False if vm.name != 'test-red' else None
+ state = False if vm.name != "test-red" else None
assert call(vm, handler.FEATURE_NAME, state) in mock_feature.mock_calls
counter += 1
assert len(mock_feature.mock_calls) == counter
-@patch('qubes_config.global_config.vm_flowbox.ask_question')
+
+@patch("qubes_config.global_config.vm_flowbox.ask_question")
def test_updates_check_reset(mock_question, real_builder, test_qapp):
- test_qapp.expected_calls[('dom0', 'admin.vm.feature.Get',
- 'config.default.qubes-update-check', None)] = \
- b'0\x001'
- test_qapp.expected_calls[('test-red', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
- test_qapp.expected_calls[('test-blue', 'admin.vm.feature.Get',
- 'service.qubes-update-check', None)] = \
- b'0\x00'
+ test_qapp.expected_calls[
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "config.default.qubes-update-check",
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ ("test-red", "admin.vm.feature.Get", "service.qubes-update-check", None)
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ (
+ "test-blue",
+ "admin.vm.feature.Get",
+ "service.qubes-update-check",
+ None,
+ )
+ ] = b"0\x00"
handler = UpdateCheckerHandler(real_builder, test_qapp)
# make some changes
handler.dom0_update_check.set_active(False)
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-vm'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-vm"])
mock_question.return_value = Gtk.ResponseType.YES
for child in handler.flowbox_handler.flowbox.get_children():
- if hasattr(child, 'vm') and str(child.vm) == 'test-vm':
+ if hasattr(child, "vm") and str(child.vm) == "test-vm":
child._remove_self()
assert mock_question.mock_calls
handler.disable_radio.set_active(True)
handler.exceptions_check.set_active(True)
- handler.flowbox_handler.add_selected_vm(test_qapp.domains['test-vm'])
+ handler.flowbox_handler.add_selected_vm(test_qapp.domains["test-vm"])
# reset
handler.reset()
assert handler.dom0_update_check.get_active()
assert handler.enable_radio.get_active()
assert handler.exceptions_check.get_active()
- assert handler.flowbox_handler.selected_vms == \
- [test_qapp.domains['test-blue'], test_qapp.domains['test-red']]
+ assert handler.flowbox_handler.selected_vms == [
+ test_qapp.domains["test-blue"],
+ test_qapp.domains["test-red"],
+ ]
-def test_update_proxy_init_no_whonix(real_builder, test_qapp,
- test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp, test_policy_manager,
- 'proxy-test', 'proxy')
+def test_update_proxy_init_no_whonix(
+ real_builder, test_qapp, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder, test_qapp, test_policy_manager, "proxy-test", "proxy"
+ )
# check default state
assert not handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-net'
+ assert handler.updatevm_model.get_selected() == "sys-net"
assert not handler.current_exception_rules
assert not handler.whonix_updatevm_box.get_visible()
-def test_update_proxy_init_whonix(real_builder, test_qapp_whonix,
- test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-test', 'proxy')
+def test_update_proxy_init_whonix(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-test",
+ "proxy",
+ )
# check default state
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-net'
- assert handler.whonix_updatevm_model.get_selected() == 'sys-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-net"
+ assert handler.whonix_updatevm_model.get_selected() == "sys-whonix"
assert not handler.current_exception_rules
assert handler.whonix_updatevm_box.get_visible()
-def test_update_proxy_init_policy_no_whonix(real_builder, test_qapp,
- test_policy_manager):
+def test_update_proxy_init_policy_no_whonix(
+ real_builder, test_qapp, test_policy_manager
+):
policy = """
Proxy * @type:TemplateVM @default allow target=sys-firewall
"""
- test_policy_manager.policy_client.policy_replace('proxy-file', policy)
+ test_policy_manager.policy_client.policy_replace("proxy-file", policy)
- handler = UpdateProxy(real_builder, test_qapp, test_policy_manager,
- 'proxy-file', 'Proxy')
+ handler = UpdateProxy(
+ real_builder, test_qapp, test_policy_manager, "proxy-file", "Proxy"
+ )
assert not handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
-def test_update_proxy_init_policy_whonix_new(real_builder, test_qapp_whonix,
- test_policy_manager):
+def test_update_proxy_init_policy_whonix_new(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
policy = """
Proxy * @type:TemplateVM @default allow target=sys-firewall
"""
- test_policy_manager.policy_client.policy_replace('proxy-file', policy)
-
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ test_policy_manager.policy_client.policy_replace("proxy-file", policy)
+
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
- assert handler.whonix_updatevm_model.get_selected() == 'sys-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
+ assert handler.whonix_updatevm_model.get_selected() == "sys-whonix"
-def test_update_proxy_init_policy_whonix(real_builder, test_qapp_whonix,
- test_policy_manager):
+def test_update_proxy_init_policy_whonix(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
policy = """
Proxy * @type:TemplateVM @default allow target=sys-firewall
Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
"""
- test_policy_manager.policy_client.policy_replace('proxy-file', policy)
-
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ test_policy_manager.policy_client.policy_replace("proxy-file", policy)
+
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
- assert handler.whonix_updatevm_model.get_selected() == 'anon-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
+ assert handler.whonix_updatevm_model.get_selected() == "anon-whonix"
-def test_update_proxy_init_policy_exc(real_builder, test_qapp_whonix,
- test_policy_manager):
+def test_update_proxy_init_policy_exc(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
policy = """
Proxy * fedora-36 @default allow target=sys-net
Proxy * @type:TemplateVM @default allow target=sys-firewall
Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
"""
- test_policy_manager.policy_client.policy_replace('proxy-file', policy)
-
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ test_policy_manager.policy_client.policy_replace("proxy-file", policy)
+
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
- assert handler.whonix_updatevm_model.get_selected() == 'anon-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
+ assert handler.whonix_updatevm_model.get_selected() == "anon-whonix"
assert len(handler.current_exception_rules) == 1
for rule in handler.current_exception_rules:
- assert rule.source == 'fedora-36'
- assert rule.target == 'sys-net'
+ assert rule.source == "fedora-36"
+ assert rule.target == "sys-net"
assert len(handler.exception_list_handler.rule_list.get_children()) == 1
-def test_update_proxy_add_exception(real_builder, test_qapp_whonix,
- test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+
+def test_update_proxy_add_exception(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert not handler.current_exception_rules
handler.exception_list_handler.add_button.clicked()
@@ -651,10 +813,10 @@ def test_update_proxy_add_exception(real_builder, test_qapp_whonix,
for child in handler.exception_list_handler.rule_list.get_children():
assert isinstance(child, NoActionListBoxRow)
if not child.editing:
- assert False # wait where is a non-edited row from??
+ assert False # wait where is a non-edited row from??
# source should have not-networked vms
- fedora36 = test_qapp_whonix.domains['fedora-36']
- sysnet = test_qapp_whonix.domains['sys-net']
+ fedora36 = test_qapp_whonix.domains["fedora-36"]
+ sysnet = test_qapp_whonix.domains["sys-net"]
assert child.source_widget.model.is_vm_available(fedora36)
assert not child.source_widget.model.is_vm_available(sysnet)
@@ -663,15 +825,17 @@ def test_update_proxy_add_exception(real_builder, test_qapp_whonix,
assert not child.target_widget.model.is_vm_available(fedora36)
# select stuff
- child.source_widget.model.select_value('fedora-36')
- child.target_widget.model.select_value('sys-net')
+ child.source_widget.model.select_value("fedora-36")
+ child.target_widget.model.select_value("sys-net")
child.validate_and_save()
desired_rules = test_policy_manager.text_to_rules(
- "Proxy * fedora-36 @default allow target=sys-net")
- assert [str(rule.raw_rule) for rule in handler.current_exception_rules] == \
- [str(rule) for rule in desired_rules]
+ "Proxy * fedora-36 @default allow target=sys-net"
+ )
+ assert [str(rule.raw_rule) for rule in handler.current_exception_rules] == [
+ str(rule) for rule in desired_rules
+ ]
# if I keep clicking add, I won't get random useless rules
handler.exception_list_handler.add_button.clicked()
@@ -683,200 +847,297 @@ def test_update_proxy_add_exception(real_builder, test_qapp_whonix,
handler.close_all_edits()
- assert [str(rule.raw_rule) for rule in handler.current_exception_rules] == \
- [str(rule) for rule in desired_rules]
-
-def test_update_proxy_add_exception_err(real_builder, test_qapp_whonix,
- test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ assert [str(rule.raw_rule) for rule in handler.current_exception_rules] == [
+ str(rule) for rule in desired_rules
+ ]
+
+
+def test_update_proxy_add_exception_err(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
handler.exception_list_handler.add_button.clicked()
# can't add update proxy without anon-gateway for whonix vm
for child in handler.exception_list_handler.rule_list.get_children():
assert isinstance(child, NoActionListBoxRow)
if not child.editing:
- assert False # wait where is a non-edited row from??
+ assert False # wait where is a non-edited row from??
- assert child.source_widget.model.is_vm_available('whonix-gw-15')
- assert child.target_widget.model.is_vm_available('sys-net')
- child.source_widget.model.select_value('whonix-gw-15')
- child.target_widget.model.select_value('sys-net')
+ assert child.source_widget.model.is_vm_available("whonix-gw-15")
+ assert child.target_widget.model.is_vm_available("sys-net")
+ child.source_widget.model.select_value("whonix-gw-15")
+ child.target_widget.model.select_value("sys-net")
- with patch('qubes_config.global_config.rule_list_widgets.show_error') \
- as mock_error:
+ with patch(
+ "qubes_config.global_config.rule_list_widgets.show_error"
+ ) as mock_error:
child.validate_and_save()
assert mock_error.mock_calls
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_update_proxy_save_updatevm(mock_feature, real_builder,
- test_qapp_whonix, test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_update_proxy_save_updatevm(
+ mock_feature, real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-net'
- assert handler.whonix_updatevm_model.get_selected() == 'sys-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-net"
+ assert handler.whonix_updatevm_model.get_selected() == "sys-whonix"
- assert handler.updatevm_model.is_vm_available('sys-firewall')
- assert handler.whonix_updatevm_model.is_vm_available('anon-whonix')
+ assert handler.updatevm_model.is_vm_available("sys-firewall")
+ assert handler.whonix_updatevm_model.is_vm_available("anon-whonix")
- handler.updatevm_model.select_value('sys-firewall')
- handler.whonix_updatevm_model.select_value('anon-whonix')
+ handler.updatevm_model.select_value("sys-firewall")
+ handler.whonix_updatevm_model.select_value("anon-whonix")
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
-"""Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
+ """Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
Proxy * @type:TemplateVM @default allow target=sys-firewall
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
file, rules, arg = mock_save.mock_calls[0].args
assert arg is None
- assert file == 'proxy-file'
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert file == "proxy-file"
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
assert len(mock_feature.mock_calls) == 4
- assert call(test_qapp_whonix.domains['sys-firewall'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['anon-whonix'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-whonix'],
- 'service.qubes-updates-proxy', None) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-net'],
- 'service.qubes-updates-proxy', None) in \
- mock_feature.mock_calls
-
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_update_proxy_apply(mock_feature, real_builder,
- test_qapp_whonix, test_policy_manager):
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-firewall"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["anon-whonix"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-whonix"],
+ "service.qubes-updates-proxy",
+ None,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-net"],
+ "service.qubes-updates-proxy",
+ None,
+ )
+ in mock_feature.mock_calls
+ )
+
+
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_update_proxy_apply(
+ mock_feature, real_builder, test_qapp_whonix, test_policy_manager
+):
# pylint: disable=unused-argument
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-net'
- assert handler.whonix_updatevm_model.get_selected() == 'sys-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-net"
+ assert handler.whonix_updatevm_model.get_selected() == "sys-whonix"
- assert handler.updatevm_model.is_vm_available('sys-firewall')
- assert handler.whonix_updatevm_model.is_vm_available('anon-whonix')
+ assert handler.updatevm_model.is_vm_available("sys-firewall")
+ assert handler.whonix_updatevm_model.is_vm_available("anon-whonix")
- handler.updatevm_model.select_value('sys-firewall')
- handler.whonix_updatevm_model.select_value('anon-whonix')
+ handler.updatevm_model.select_value("sys-firewall")
+ handler.whonix_updatevm_model.select_value("anon-whonix")
handler.save()
handler.reset()
- assert str(handler.updatevm_model.get_selected()) == 'sys-firewall'
- assert str(handler.whonix_updatevm_model.get_selected()) == 'anon-whonix'
+ assert str(handler.updatevm_model.get_selected()) == "sys-firewall"
+ assert str(handler.whonix_updatevm_model.get_selected()) == "anon-whonix"
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_update_proxy_save_justwhonix(mock_feature, real_builder,
- test_qapp_whonix, test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_update_proxy_save_justwhonix(
+ mock_feature, real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-net'
- assert handler.whonix_updatevm_model.get_selected() == 'sys-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-net"
+ assert handler.whonix_updatevm_model.get_selected() == "sys-whonix"
- assert handler.whonix_updatevm_model.is_vm_available('anon-whonix')
- handler.whonix_updatevm_model.select_value('anon-whonix')
+ assert handler.whonix_updatevm_model.is_vm_available("anon-whonix")
+ handler.whonix_updatevm_model.select_value("anon-whonix")
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
-"""Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
+ """Proxy * @tag:whonix-updatevm @default allow target=anon-whonix
Proxy * @type:TemplateVM @default allow target=sys-net
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
file, rules, arg = mock_save.mock_calls[0].args
assert arg is None
- assert file == 'proxy-file'
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert file == "proxy-file"
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
assert len(mock_feature.mock_calls) == 3
- assert call(test_qapp_whonix.domains['anon-whonix'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-net'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-whonix'],
- 'service.qubes-updates-proxy', None) in \
- mock_feature.mock_calls
-
-
-@patch('qubes_config.global_config.updates_handler.apply_feature_change')
-def test_update_proxy_save_add_rule(mock_feature, real_builder,
- test_qapp_whonix, test_policy_manager):
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ assert (
+ call(
+ test_qapp_whonix.domains["anon-whonix"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-net"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-whonix"],
+ "service.qubes-updates-proxy",
+ None,
+ )
+ in mock_feature.mock_calls
+ )
+
+
+@patch("qubes_config.global_config.updates_handler.apply_feature_change")
+def test_update_proxy_save_add_rule(
+ mock_feature, real_builder, test_qapp_whonix, test_policy_manager
+):
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
handler.exception_list_handler.add_button.clicked()
for child in handler.exception_list_handler.rule_list.get_children():
assert isinstance(child, NoActionListBoxRow)
if not child.editing:
- assert False # wait where is a non-edited row from??
+ assert False # wait where is a non-edited row from??
# select stuff
- child.source_widget.model.select_value('fedora-36')
- child.target_widget.model.select_value('sys-firewall')
+ child.source_widget.model.select_value("fedora-36")
+ child.target_widget.model.select_value("sys-firewall")
child.validate_and_save()
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
"""Proxy * fedora-36 @default allow target=sys-firewall
Proxy * @tag:whonix-updatevm @default allow target=sys-whonix
Proxy * @type:TemplateVM @default allow target=sys-net
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
file, rules, arg = mock_save.mock_calls[0].args
assert arg is None
- assert file == 'proxy-file'
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert file == "proxy-file"
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
assert len(mock_feature.mock_calls) == 3
- assert call(test_qapp_whonix.domains['sys-whonix'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-firewall'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
- assert call(test_qapp_whonix.domains['sys-net'],
- 'service.qubes-updates-proxy', True) in \
- mock_feature.mock_calls
-
-
-def test_update_proxy_reset(real_builder, test_qapp_whonix,
- test_policy_manager):
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-whonix"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-firewall"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+ assert (
+ call(
+ test_qapp_whonix.domains["sys-net"],
+ "service.qubes-updates-proxy",
+ True,
+ )
+ in mock_feature.mock_calls
+ )
+
+
+def test_update_proxy_reset(
+ real_builder, test_qapp_whonix, test_policy_manager
+):
policy = """
Proxy * fedora-36 @default allow target=sys-net
Proxy * @type:TemplateVM @default allow target=sys-firewall
Proxy * @tag:whonix-updatevm @default allow target=anon-whonix"""
- test_policy_manager.policy_client.policy_replace('proxy-file', policy)
-
- handler = UpdateProxy(real_builder, test_qapp_whonix, test_policy_manager,
- 'proxy-file', 'Proxy')
+ test_policy_manager.policy_client.policy_replace("proxy-file", policy)
+
+ handler = UpdateProxy(
+ real_builder,
+ test_qapp_whonix,
+ test_policy_manager,
+ "proxy-file",
+ "Proxy",
+ )
assert handler.has_whonix
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
- assert handler.whonix_updatevm_model.get_selected() == 'anon-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
+ assert handler.whonix_updatevm_model.get_selected() == "anon-whonix"
assert len(handler.current_exception_rules) == 1
for rule in handler.current_exception_rules:
- assert rule.source == 'fedora-36'
- assert rule.target == 'sys-net'
+ assert rule.source == "fedora-36"
+ assert rule.target == "sys-net"
assert len(handler.exception_list_handler.rule_list.get_children()) == 1
assert not handler.is_changed()
@@ -884,8 +1145,9 @@ def test_update_proxy_reset(real_builder, test_qapp_whonix,
# change things
assert handler.updatevm_model.is_vm_available(
- test_qapp_whonix.domains['sys-net'])
- handler.updatevm_model.select_value('sys-net')
+ test_qapp_whonix.domains["sys-net"]
+ )
+ handler.updatevm_model.select_value("sys-net")
# add exception
handler.exception_list_handler.add_button.clicked()
@@ -894,8 +1156,8 @@ def test_update_proxy_reset(real_builder, test_qapp_whonix,
if not child.editing:
continue
# select stuff
- child.source_widget.model.select_value('fedora-35')
- child.target_widget.model.select_value('sys-firewall')
+ child.source_widget.model.select_value("fedora-35")
+ child.target_widget.model.select_value("sys-firewall")
child.validate_and_save()
break
else:
@@ -904,64 +1166,74 @@ def test_update_proxy_reset(real_builder, test_qapp_whonix,
assert handler.is_changed()
handler.reset()
- assert handler.updatevm_model.get_selected() == 'sys-firewall'
- assert handler.whonix_updatevm_model.get_selected() == 'anon-whonix'
+ assert handler.updatevm_model.get_selected() == "sys-firewall"
+ assert handler.whonix_updatevm_model.get_selected() == "anon-whonix"
assert len(handler.current_exception_rules) == 1
for rule in handler.current_exception_rules:
- assert rule.source == 'fedora-36'
- assert rule.target == 'sys-net'
+ assert rule.source == "fedora-36"
+ assert rule.target == "sys-net"
assert len(handler.exception_list_handler.rule_list.get_children()) == 1
assert not handler.is_changed()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
-def test_complete_handler(mock_output, real_builder, test_qapp,
- test_policy_manager):
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
+def test_complete_handler(
+ mock_output, real_builder, test_qapp, test_policy_manager
+):
mock_output.return_value = ALL_ENABLED
handler = UpdatesHandler(test_qapp, test_policy_manager, real_builder)
# check if dom0 updatevm worked
- assert handler.dom0_updatevm_model.get_selected() == \
- test_qapp.domains['sys-net']
+ assert (
+ handler.dom0_updatevm_model.get_selected()
+ == test_qapp.domains["sys-net"]
+ )
# change selection
assert handler.dom0_updatevm_model.is_vm_available(
- test_qapp.domains['sys-firewall'])
- handler.dom0_updatevm_model.select_value('sys-firewall')
+ test_qapp.domains["sys-firewall"]
+ )
+ handler.dom0_updatevm_model.select_value("sys-firewall")
# check that we have some unsaved
- assert 'dom0 Update Proxy' in handler.get_unsaved()
+ assert "dom0 Update Proxy" in handler.get_unsaved()
# and change some more stuff
handler.update_checker.dom0_update_check.set_active(False)
- assert 'dom0 Update Proxy' in handler.get_unsaved()
+ assert "dom0 Update Proxy" in handler.get_unsaved()
assert 'dom0 "check for updates"' in handler.get_unsaved()
# and reset
handler.reset()
- assert handler.get_unsaved() == ''
- assert handler.dom0_updatevm_model.get_selected() == \
- test_qapp.domains['sys-net']
+ assert handler.get_unsaved() == ""
+ assert (
+ handler.dom0_updatevm_model.get_selected()
+ == test_qapp.domains["sys-net"]
+ )
assert handler.update_checker.dom0_update_check.get_active()
-@patch('qubes_config.global_config.updates_handler.qrexec_call')
-def test_complete_handle_dom0updatevm(mock_output, real_builder,
- test_qapp, test_policy_manager):
- mock_output.return_value = ''
+@patch("qubes_config.global_config.updates_handler.qrexec_call")
+def test_complete_handle_dom0updatevm(
+ mock_output, real_builder, test_qapp, test_policy_manager
+):
+ mock_output.return_value = ""
handler = UpdatesHandler(test_qapp, test_policy_manager, real_builder)
# check if dom0 updatevm worked
- assert handler.dom0_updatevm_model.get_selected() == \
- test_qapp.domains['sys-net']
+ assert (
+ handler.dom0_updatevm_model.get_selected()
+ == test_qapp.domains["sys-net"]
+ )
# change selection
assert handler.dom0_updatevm_model.is_vm_available(
- test_qapp.domains['sys-firewall'])
- handler.dom0_updatevm_model.select_value('sys-firewall')
+ test_qapp.domains["sys-firewall"]
+ )
+ handler.dom0_updatevm_model.select_value("sys-firewall")
with pytest.raises(AssertionError):
# should fail, no qapp call provided
@@ -970,6 +1242,7 @@ def test_complete_handle_dom0updatevm(mock_output, real_builder,
# and now we provide the call
test_qapp.expected_calls[
- ('dom0', 'admin.property.Set', 'updatevm', b'sys-firewall')] = b'0\x00'
+ ("dom0", "admin.property.Set", "updatevm", b"sys-firewall")
+ ] = b"0\x00"
handler.save()
diff --git a/qubes_config/tests/test_usb_devices.py b/qubes_config/tests/test_usb_devices.py
index 40d73eb6..f5c231a9 100644
--- a/qubes_config/tests/test_usb_devices.py
+++ b/qubes_config/tests/test_usb_devices.py
@@ -25,61 +25,81 @@
from unittest.mock import patch, call
-from ..global_config.usb_devices import InputDeviceHandler, U2FPolicyHandler, \
- DevicesHandler
+from ..global_config.usb_devices import (
+ InputDeviceHandler,
+ U2FPolicyHandler,
+ DevicesHandler,
+)
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
-def test_input_devices_simple_policy(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- test_policy_manager.policy_client.files['50-config-input'] = """
+def test_input_devices_simple_policy(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb})
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
for widget in handler.widgets.values():
assert widget.get_parent()
- assert handler.widgets[
- ('qubes.InputMouse', 'sys-usb')].model.get_selected() == 'ask'
- assert handler.widgets[('qubes.InputKeyboard',
- 'sys-usb')].model.get_selected() == 'deny'
- assert handler.widgets[
- ('qubes.InputTablet', 'sys-usb')].model.get_selected() == 'allow'
+ assert (
+ handler.widgets[("qubes.InputMouse", "sys-usb")].model.get_selected()
+ == "ask"
+ )
+ assert (
+ handler.widgets[("qubes.InputKeyboard", "sys-usb")].model.get_selected()
+ == "deny"
+ )
+ assert (
+ handler.widgets[("qubes.InputTablet", "sys-usb")].model.get_selected()
+ == "allow"
+ )
# check that things are in correct place: change something from fourth row,
# second column (should be tablet widget for sys-usb)
- handler.policy_grid.get_child_at(1, 3).model.select_value('deny')
+ handler.policy_grid.get_child_at(1, 3).model.select_value("deny")
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
- expected_rules = handler.policy_manager.text_to_rules("""
+ expected_rules = handler.policy_manager.text_to_rules(
+ """
qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm deny
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
-
-
-def test_input_devices_complex_policy(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- sys_net = test_qapp.domains['sys-net']
- test_policy_manager.policy_client.files['50-config-input'] = """
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
+
+
+def test_input_devices_complex_policy(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ sys_net = test_qapp.domains["sys-net"]
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm allow
@@ -87,116 +107,140 @@ def test_input_devices_complex_policy(test_qapp,
qubes.InputKeyboard * sys-net @adminvm deny
qubes.InputTablet * sys-net @adminvm allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb, sys_net})
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb, sys_net}
+ )
for widget in handler.widgets.values():
assert widget.get_parent()
- assert handler.widgets[
- ('qubes.InputMouse', 'sys-usb')].model.get_selected() == 'ask'
- assert handler.widgets[('qubes.InputKeyboard',
- 'sys-usb')].model.get_selected() == 'deny'
- assert handler.widgets[
- ('qubes.InputTablet', 'sys-usb')].model.get_selected() == 'allow'
- assert handler.widgets[
- ('qubes.InputMouse', 'sys-net')].model.get_selected() == 'deny'
- assert handler.widgets[('qubes.InputKeyboard',
- 'sys-net')].model.get_selected() == 'deny'
- assert handler.widgets[
- ('qubes.InputTablet', 'sys-net')].model.get_selected() == 'allow'
+ assert (
+ handler.widgets[("qubes.InputMouse", "sys-usb")].model.get_selected()
+ == "ask"
+ )
+ assert (
+ handler.widgets[("qubes.InputKeyboard", "sys-usb")].model.get_selected()
+ == "deny"
+ )
+ assert (
+ handler.widgets[("qubes.InputTablet", "sys-usb")].model.get_selected()
+ == "allow"
+ )
+ assert (
+ handler.widgets[("qubes.InputMouse", "sys-net")].model.get_selected()
+ == "deny"
+ )
+ assert (
+ handler.widgets[("qubes.InputKeyboard", "sys-net")].model.get_selected()
+ == "deny"
+ )
+ assert (
+ handler.widgets[("qubes.InputTablet", "sys-net")].model.get_selected()
+ == "allow"
+ )
# which row is sys_net in? change KB for it
i = 1
while i <= 2:
- if handler.policy_grid.get_child_at(i, 0).token_name == 'sys-net':
- handler.policy_grid.get_child_at(i, 1).model.select_value('allow')
+ if handler.policy_grid.get_child_at(i, 0).token_name == "sys-net":
+ handler.policy_grid.get_child_at(i, 1).model.select_value("allow")
break
i += 1
else:
raise AssertionError("no sys-net in grid")
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
- expected_rules = handler.policy_manager.text_to_rules("""
+ expected_rules = handler.policy_manager.text_to_rules(
+ """
qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm allow
qubes.InputMouse * sys-net @adminvm deny
qubes.InputKeyboard * sys-net @adminvm allow
qubes.InputTablet * sys-net @adminvm allow
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert sorted([str(rule) for rule in expected_rules]) == \
- sorted([str(rule) for rule in rules])
+ assert sorted([str(rule) for rule in expected_rules]) == sorted(
+ [str(rule) for rule in rules]
+ )
-def test_input_devices_no_policy_one_usb(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb})
+def test_input_devices_no_policy_one_usb(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
# check if defaults worked
for widget in handler.widgets.values():
- assert widget.model.get_selected() == 'deny'
+ assert widget.model.get_selected() == "deny"
assert widget.get_parent()
# no warning is needed
assert not handler.warn_box.get_visible()
# change things up
- mouse_widget = handler.widgets[('qubes.InputMouse', 'sys-usb')]
- mouse_widget.model.select_value('ask')
+ mouse_widget = handler.widgets[("qubes.InputMouse", "sys-usb")]
+ mouse_widget.model.select_value("ask")
- assert mouse_widget.model.get_selected() == 'ask'
- assert handler.get_unsaved() == 'Mouse input settings'
+ assert mouse_widget.model.get_selected() == "ask"
+ assert handler.get_unsaved() == "Mouse input settings"
# revert
handler.reset()
- assert mouse_widget.model.get_selected() == 'deny'
- assert handler.get_unsaved() == ''
+ assert mouse_widget.model.get_selected() == "deny"
+ assert handler.get_unsaved() == ""
# change and save
- mouse_widget = handler.widgets[('qubes.InputMouse', 'sys-usb')]
- mouse_widget.model.select_value('ask')
+ mouse_widget = handler.widgets[("qubes.InputMouse", "sys-usb")]
+ mouse_widget.model.select_value("ask")
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
-"""qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
+ """qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm deny
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
-def test_input_devices_faulty_policy_lines(test_qapp,
- test_policy_manager, real_builder):
- test_policy_manager.policy_client.files['50-config-input'] = """
+def test_input_devices_faulty_policy_lines(
+ test_qapp, test_policy_manager, real_builder
+):
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-usb @adminvm deny
qubes.InputKeyboard * sys-usb @adminvm ask default_target=@adminvm
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- sys_usb = test_qapp.domains['sys-usb']
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
# check if defaults worked
for designation, widget in handler.widgets.items():
- if designation[0] == 'qubes.InputKeyboard':
- assert widget.model.get_selected() == 'ask'
+ if designation[0] == "qubes.InputKeyboard":
+ assert widget.model.get_selected() == "ask"
else:
- assert widget.model.get_selected() == 'deny'
+ assert widget.model.get_selected() == "deny"
# exactly 3 widgets
assert len(handler.widgets) == 3
@@ -205,120 +249,135 @@ def test_input_devices_faulty_policy_lines(test_qapp,
assert not handler.warn_box.get_visible()
# if user changes nothing, there should be no changes
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
-def test_input_devices_faulty_policy_lines_2(test_qapp,
- test_policy_manager, real_builder):
- test_policy_manager.policy_client.files['50-config-input'] = """
+def test_input_devices_faulty_policy_lines_2(
+ test_qapp, test_policy_manager, real_builder
+):
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-net @adminvm deny
qubes.InputKeyboard * sys-usb @adminvm ask default_target=@adminvm
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- sys_usb = test_qapp.domains['sys-usb']
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
# check if defaults worked
for designation, widget in handler.widgets.items():
- if designation[0] == 'qubes.InputKeyboard':
- assert widget.model.get_selected() == 'ask'
+ if designation[0] == "qubes.InputKeyboard":
+ assert widget.model.get_selected() == "ask"
else:
- assert widget.model.get_selected() == 'deny'
+ assert widget.model.get_selected() == "deny"
# check if there's warning visible
assert handler.warn_box.get_visible()
# change something
- mouse_widget = handler.widgets[('qubes.InputTablet', 'sys-usb')]
- mouse_widget.model.select_value('allow')
+ mouse_widget = handler.widgets[("qubes.InputTablet", "sys-usb")]
+ mouse_widget.model.select_value("allow")
# the weird rule should not be discarded, I think
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
-"""
+ """
qubes.InputMouse * sys-net @adminvm deny
qubes.InputKeyboard * sys-usb @adminvm ask default_target=@adminvm
qubes.InputMouse * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm allow
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
-def test_input_devices_no_usbvm(test_qapp,
- test_policy_manager, real_builder):
- test_policy_manager.policy_client.files['50-config-input'] = """
+def test_input_devices_no_usbvm(test_qapp, test_policy_manager, real_builder):
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-usb @adminvm ask default_target=@adminvm
qubes.InputKeyboard * sys-usb @adminvm deny
qubes.InputTablet * sys-usb @adminvm deny
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, set())
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, set()
+ )
for widget in handler.widgets.values():
assert not widget.get_parent()
assert handler.warn_box.get_visible()
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
# no policy should be changed
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
assert len(mock_save.mock_calls) == 0
-def test_input_devices_faulty_policy_err(test_qapp,
- test_policy_manager, real_builder):
- test_policy_manager.policy_client.files['50-config-input'] = """
+def test_input_devices_faulty_policy_err(
+ test_qapp, test_policy_manager, real_builder
+):
+ test_policy_manager.policy_client.files[
+ "50-config-input"
+ ] = """
qubes.InputMouse * sys-usb @adminvm allow target=test-red
qubes.InputTablet * sys-usb test-red deny
qubes.InputKeyboard * sys-usb @adminvm ask default_target=sys-net
"""
- test_policy_manager.policy_client.file_tokens['50-config-input'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-input"] = "55"
- sys_usb = test_qapp.domains['sys-usb']
- handler = InputDeviceHandler(test_qapp, test_policy_manager,
- real_builder, {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = InputDeviceHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
# check if defaults worked
for _, widget in handler.widgets.items():
- assert widget.model.get_selected() == 'deny'
+ assert widget.model.get_selected() == "deny"
# check if there's warning visible
assert handler.warn_box.get_visible()
def test_u2f_handler_init(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
# settings from conftest: only vms that have this available are 'test-vm'
# and 'fedora-35', only test-vm can use the service, policy is default
- testvm = test_qapp.domains['test-vm']
- testred = test_qapp.domains['test-red']
- fedora35 = test_qapp.domains['fedora-35']
- sysusb = test_qapp.domains['sys-usb']
+ testvm = test_qapp.domains["test-vm"]
+ testred = test_qapp.domains["test-red"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ sysusb = test_qapp.domains["sys-usb"]
assert handler.enable_check.get_active()
assert handler.enable_some_handler.selected_vms == [testvm]
assert handler.enable_some_handler.add_qube_model.is_vm_available(testvm)
assert handler.enable_some_handler.add_qube_model.is_vm_available(fedora35)
assert not handler.enable_some_handler.add_qube_model.is_vm_available(
- testred)
+ testred
+ )
assert not handler.enable_some_handler.add_qube_model.is_vm_available(
- sysusb)
+ sysusb
+ )
assert not handler.register_check.get_active()
assert not handler.register_some_handler.selected_vms
@@ -328,40 +387,58 @@ def test_u2f_handler_init(test_qapp, test_policy_manager, real_builder):
def test_u2f_handler_init_disable(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_qapp.expected_calls[
+ (
+ "test-vm",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert not handler.enable_check.get_active()
assert not handler.problem_fatal_box.get_visible()
def test_u2f_handler_init_no_u2f_in_sysub(
- test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
test_qapp.expected_calls[
- ('sys-usb', 'admin.vm.feature.CheckWithTemplate',
- U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ (
+ "sys-usb",
+ "admin.vm.feature.CheckWithTemplate",
+ U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert not handler.enable_check.get_sensitive()
assert not handler.enable_check.get_active()
assert handler.problem_fatal_box.get_visible()
-def test_u2f_handler_no_usb_vm(
- test_qapp, test_policy_manager, real_builder):
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- set())
+def test_u2f_handler_no_usb_vm(test_qapp, test_policy_manager, real_builder):
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, set()
+ )
assert not handler.enable_check.get_sensitive()
assert not handler.enable_check.get_active()
@@ -369,23 +446,31 @@ def test_u2f_handler_no_usb_vm(
def test_u2f_handler_init_policy(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- fedora35 = test_qapp.domains['fedora-35']
- testvm = test_qapp.domains['test-vm']
- test_qapp.expected_calls[('fedora-35', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'0\x001'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+ sys_usb = test_qapp.domains["sys-usb"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ testvm = test_qapp.domains["test-vm"]
+ test_qapp.expected_calls[
+ (
+ "fedora-35",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register sys-usb @anyvm allow target=dom0
u2f.Register * fedora-35 sys-usb allow
u2f.Register * test-vm sys-usb allow
u2f.Authenticate * test-vm sys-usb allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert handler.enable_check.get_active()
assert handler.enable_some_handler.selected_vms == [fedora35, testvm]
@@ -398,17 +483,27 @@ def test_u2f_handler_init_policy(test_qapp, test_policy_manager, real_builder):
assert handler.blanket_handler.selected_vms == [testvm]
-def test_u2f_handler_init_no_policy(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
+def test_u2f_handler_init_no_policy(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
# disable service
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ test_qapp.expected_calls[
+ (
+ "test-vm",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert handler.enable_check.get_sensitive()
assert not handler.enable_check.get_active()
@@ -420,23 +515,32 @@ def test_u2f_handler_init_no_policy(test_qapp,
assert not handler.blanket_check.get_active()
-def test_u2f_handler_init_policy_2(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- fedora35 = test_qapp.domains['fedora-35']
- testvm = test_qapp.domains['test-vm']
- test_qapp.expected_calls[('fedora-35', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'0\x001'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+def test_u2f_handler_init_policy_2(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ testvm = test_qapp.domains["test-vm"]
+ test_qapp.expected_calls[
+ (
+ "fedora-35",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register sys-usb @anyvm allow target=dom0
u2f.Register * @anyvm sys-usb allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert handler.enable_check.get_active()
assert handler.enable_some_handler.selected_vms == [fedora35, testvm]
@@ -448,28 +552,44 @@ def test_u2f_handler_init_policy_2(test_qapp,
assert not handler.problem_fatal_box.get_visible()
-def test_u2f_handler_init_policy_mismatch(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- fedora35 = test_qapp.domains['fedora-35']
- testvm = test_qapp.domains['test-vm']
- test_qapp.expected_calls[('fedora-35', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'0\x001'
+def test_u2f_handler_init_policy_mismatch(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ testvm = test_qapp.domains["test-vm"]
test_qapp.expected_calls[
- ('test-standalone', 'admin.vm.feature.CheckWithTemplate',
- U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+ (
+ "fedora-35",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ (
+ "test-standalone",
+ "admin.vm.feature.CheckWithTemplate",
+ U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register test-standalone @anyvm allow target=dom0
u2f.Register * @anyvm test-standalone allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert handler.usb_qube_model.get_selected() == sys_usb
assert handler.enable_check.get_active()
@@ -483,24 +603,37 @@ def test_u2f_handler_init_policy_mismatch(test_qapp,
def test_u2f_handler_2_usbvms(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- test_standalone = test_qapp.domains['test-standalone']
- testvm = test_qapp.domains['test-vm']
- test_qapp.expected_calls[('test-standalone', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'0\x001'
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_standalone = test_qapp.domains["test-standalone"]
+ testvm = test_qapp.domains["test-vm"]
test_qapp.expected_calls[
- ('test-standalone', 'admin.vm.feature.CheckWithTemplate',
- U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE, None)] = b'0\x001'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+ (
+ "test-standalone",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ (
+ "test-standalone",
+ "admin.vm.feature.CheckWithTemplate",
+ U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register test-standalone @anyvm allow target=dom0
u2f.Register * @anyvm test-standalone allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb, test_standalone})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb, test_standalone}
+ )
assert handler.usb_qube_model.get_selected() == test_standalone
assert handler.usb_qube_model.is_vm_available(sys_usb)
@@ -514,26 +647,40 @@ def test_u2f_handler_2_usbvms(test_qapp, test_policy_manager, real_builder):
assert not handler.error_handler.error_box.get_visible()
-def test_u2f_handler_2_usbvms_switch(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- test_standalone = test_qapp.domains['test-standalone']
- testvm = test_qapp.domains['test-vm']
- test_qapp.expected_calls[('test-standalone', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'0\x001'
+def test_u2f_handler_2_usbvms_switch(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_standalone = test_qapp.domains["test-standalone"]
+ testvm = test_qapp.domains["test-vm"]
test_qapp.expected_calls[
- ('test-standalone', 'admin.vm.feature.CheckWithTemplate',
- U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE, None)] = b'0\x001'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+ (
+ "test-standalone",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+ test_qapp.expected_calls[
+ (
+ "test-standalone",
+ "admin.vm.feature.CheckWithTemplate",
+ U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE,
+ None,
+ )
+ ] = b"0\x001"
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register test-standalone @anyvm allow target=dom0
u2f.Register * @anyvm test-standalone allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb, test_standalone})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb, test_standalone}
+ )
assert handler.usb_qube_model.get_selected() == test_standalone
assert handler.usb_qube_model.is_vm_available(sys_usb)
@@ -569,25 +716,36 @@ def test_u2f_handler_2_usbvms_switch(test_qapp,
assert not handler.error_handler.error_box.get_visible()
-def test_u2f_handler_2_usbvms_broken(test_qapp, test_policy_manager,
- real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- test_standalone = test_qapp.domains['test-standalone']
- testvm = test_qapp.domains['test-vm']
+def test_u2f_handler_2_usbvms_broken(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_standalone = test_qapp.domains["test-standalone"]
+ testvm = test_qapp.domains["test-vm"]
test_qapp.expected_calls[
- ('test-standalone', 'admin.vm.feature.CheckWithTemplate',
- U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- test_policy_manager.policy_client.files['50-config-u2f'] = """
+ (
+ "test-standalone",
+ "admin.vm.feature.CheckWithTemplate",
+ U2FPolicyHandler.SUPPORTED_SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ test_policy_manager.policy_client.files[
+ "50-config-u2f"
+ ] = """
policy.RegisterArgument +u2f.Register test-standalone @anyvm allow target=dom0
u2f.Register * @anyvm test-standalone allow
"""
- test_policy_manager.policy_client.file_tokens['50-config-u2f'] = '55'
+ test_policy_manager.policy_client.file_tokens["50-config-u2f"] = "55"
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb, test_standalone})
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb, test_standalone}
+ )
assert handler.usb_qube_model.get_selected() == sys_usb
assert not handler.usb_qube_model.is_vm_available(test_standalone)
@@ -602,11 +760,12 @@ def test_u2f_handler_2_usbvms_broken(test_qapp, test_policy_manager,
def test_u2f_unsaved_reset(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
- testvm = test_qapp.domains['test-vm']
- fedora35 = test_qapp.domains['fedora-35']
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
+ testvm = test_qapp.domains["test-vm"]
+ fedora35 = test_qapp.domains["fedora-35"]
assert handler.enable_check.get_active()
assert not handler.register_check.get_active()
@@ -614,10 +773,10 @@ def test_u2f_unsaved_reset(test_qapp, test_policy_manager, real_builder):
assert handler.enable_some_handler.selected_vms == [testvm]
handler.enable_check.set_active(False)
- assert handler.get_unsaved() == 'U2F disabled'
+ assert handler.get_unsaved() == "U2F disabled"
handler.enable_check.set_active(True)
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
assert handler.enable_check.get_active()
assert not handler.register_check.get_active()
@@ -625,14 +784,14 @@ def test_u2f_unsaved_reset(test_qapp, test_policy_manager, real_builder):
handler.enable_some_handler.add_selected_vm(fedora35)
assert handler.enable_some_handler.selected_vms == [fedora35, testvm]
- assert handler.get_unsaved() == 'List of qubes with U2F enabled changed'
+ assert handler.get_unsaved() == "List of qubes with U2F enabled changed"
handler.reset()
assert handler.enable_check.get_active()
assert not handler.register_check.get_active()
assert not handler.blanket_check.get_active()
assert handler.enable_some_handler.selected_vms == [testvm]
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
handler.blanket_check.set_active(True)
handler.register_check.set_active(True)
@@ -642,28 +801,31 @@ def test_u2f_unsaved_reset(test_qapp, test_policy_manager, real_builder):
assert handler.blanket_handler.selected_vms == [fedora35]
assert handler.register_some_handler.selected_vms == [fedora35]
- assert 'U2F key registration' in handler.get_unsaved()
- assert 'unrestricted U2F key' in handler.get_unsaved()
+ assert "U2F key registration" in handler.get_unsaved()
+ assert "unrestricted U2F key" in handler.get_unsaved()
handler.reset()
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
assert not handler.blanket_handler.selected_vms
assert not handler.register_some_handler.selected_vms
+
def test_u2f_save_disable(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
handler.enable_check.set_active(False)
- with patch.object(handler.policy_manager, 'save_rules') as mock_save, \
- patch('qubes_config.global_config.usb_devices.'
- 'apply_feature_change') as mock_apply:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save, patch(
+ "qubes_config.global_config.usb_devices.apply_feature_change"
+ ) as mock_apply:
handler.save()
mock_apply.assert_called_with(
- test_qapp.domains['test-vm'], handler.SERVICE_FEATURE, None)
+ test_qapp.domains["test-vm"], handler.SERVICE_FEATURE, None
+ )
assert len(mock_apply.mock_calls) == 1
expected_rules = handler.policy_manager.text_to_rules(
@@ -671,52 +833,68 @@ def test_u2f_save_disable(test_qapp, test_policy_manager, real_builder):
u2f.Authenticate * @anyvm @anyvm deny
u2f.Register * @anyvm @anyvm deny
policy.RegisterArgument +u2f.Register @anyvm @anyvm deny
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
def test_u2f_save_service(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
- fedora35 = test_qapp.domains['fedora-35']
+ sys_usb = test_qapp.domains["sys-usb"]
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
+ fedora35 = test_qapp.domains["fedora-35"]
assert handler.enable_check.get_active()
handler.enable_some_handler.add_selected_vm(fedora35)
- test_qapp.expected_calls[('fedora-35', 'admin.vm.feature.Set',
- 'service.qubes-u2f-proxy', b'1')] = b'0\x00'
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Set',
- 'service.qubes-u2f-proxy', b'1')] = b'0\x00'
+ test_qapp.expected_calls[
+ ("fedora-35", "admin.vm.feature.Set", "service.qubes-u2f-proxy", b"1")
+ ] = b"0\x00"
+ test_qapp.expected_calls[
+ ("test-vm", "admin.vm.feature.Set", "service.qubes-u2f-proxy", b"1")
+ ] = b"0\x00"
- with patch.object(handler.policy_manager, 'save_rules') as mock_save:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save:
handler.save()
expected_rules = handler.policy_manager.text_to_rules(
"""
u2f.Register * @anyvm @anyvm deny
policy.RegisterArgument +u2f.Authenticate @anyvm @anyvm deny
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
def test_u2f_handler_save_complex(test_qapp, test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- testvm = test_qapp.domains['test-vm']
- fedora35 = test_qapp.domains['fedora-35']
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
-
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+ sys_usb = test_qapp.domains["sys-usb"]
+ testvm = test_qapp.domains["test-vm"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ test_qapp.expected_calls[
+ (
+ "test-vm",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert not handler.enable_check.get_active()
@@ -730,15 +908,19 @@ def test_u2f_handler_save_complex(test_qapp, test_policy_manager, real_builder):
handler.blanket_check.set_active(True)
handler.blanket_handler.add_selected_vm(testvm)
- with patch.object(handler.policy_manager, 'save_rules') as mock_save, \
- patch('qubes_config.global_config.usb_devices.'
- 'apply_feature_change') as mock_apply:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save, patch(
+ "qubes_config.global_config.usb_devices.apply_feature_change"
+ ) as mock_apply:
handler.save()
- assert call(test_qapp.domains['test-vm'],
- handler.SERVICE_FEATURE, True) in mock_apply.mock_calls
- assert call(test_qapp.domains['fedora-35'],
- handler.SERVICE_FEATURE, True) in mock_apply.mock_calls
+ assert (
+ call(test_qapp.domains["test-vm"], handler.SERVICE_FEATURE, True)
+ in mock_apply.mock_calls
+ )
+ assert (
+ call(test_qapp.domains["fedora-35"], handler.SERVICE_FEATURE, True)
+ in mock_apply.mock_calls
+ )
assert len(mock_apply.mock_calls) == 2
expected_rules = handler.policy_manager.text_to_rules(
@@ -746,25 +928,37 @@ def test_u2f_handler_save_complex(test_qapp, test_policy_manager, real_builder):
policy.RegisterArgument +u2f.Authenticate sys-usb @anyvm allow target=dom0
u2f.Register * @anyvm sys-usb allow
u2f.Authenticate * test-vm sys-usb allow
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
-
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
-def test_u2f_handler_save_complex_2(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- testvm = test_qapp.domains['test-vm']
- fedora35 = test_qapp.domains['fedora-35']
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Get',
- U2FPolicyHandler.SERVICE_FEATURE, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00' + \
- str(U2FPolicyHandler.SERVICE_FEATURE).encode() + b'\x00'
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+def test_u2f_handler_save_complex_2(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ testvm = test_qapp.domains["test-vm"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ test_qapp.expected_calls[
+ (
+ "test-vm",
+ "admin.vm.feature.Get",
+ U2FPolicyHandler.SERVICE_FEATURE,
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + str(U2FPolicyHandler.SERVICE_FEATURE).encode()
+ + b"\x00"
+ )
+
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
assert not handler.enable_check.get_active()
@@ -779,15 +973,19 @@ def test_u2f_handler_save_complex_2(test_qapp,
handler.blanket_check.set_active(False)
- with patch.object(handler.policy_manager, 'save_rules') as mock_save, \
- patch('qubes_config.global_config.usb_devices.'
- 'apply_feature_change') as mock_apply:
+ with patch.object(handler.policy_manager, "save_rules") as mock_save, patch(
+ "qubes_config.global_config.usb_devices.apply_feature_change"
+ ) as mock_apply:
handler.save()
- assert call(test_qapp.domains['test-vm'],
- handler.SERVICE_FEATURE, True) in mock_apply.mock_calls
- assert call(test_qapp.domains['fedora-35'],
- handler.SERVICE_FEATURE, True) in mock_apply.mock_calls
+ assert (
+ call(test_qapp.domains["test-vm"], handler.SERVICE_FEATURE, True)
+ in mock_apply.mock_calls
+ )
+ assert (
+ call(test_qapp.domains["fedora-35"], handler.SERVICE_FEATURE, True)
+ in mock_apply.mock_calls
+ )
assert len(mock_apply.mock_calls) == 2
expected_rules = handler.policy_manager.text_to_rules(
@@ -795,21 +993,26 @@ def test_u2f_handler_save_complex_2(test_qapp,
u2f.Register * fedora-35 sys-usb allow
u2f.Register * test-vm sys-usb allow
policy.RegisterArgument +u2f.Authenticate sys-usb @anyvm allow target=dom0
-""")
+"""
+ )
assert len(mock_save.mock_calls) == 1
_, rules, _ = mock_save.mock_calls[0].args
- assert [str(rule) for rule in expected_rules] == \
- [str(rule) for rule in rules]
+ assert [str(rule) for rule in expected_rules] == [
+ str(rule) for rule in rules
+ ]
+
-def test_u2f_handler_add_without_service(test_qapp,
- test_policy_manager, real_builder):
- sys_usb = test_qapp.domains['sys-usb']
- fedora35 = test_qapp.domains['fedora-35']
- testvm = test_qapp.domains['test-vm']
- handler = U2FPolicyHandler(test_qapp, test_policy_manager, real_builder,
- {sys_usb})
+def test_u2f_handler_add_without_service(
+ test_qapp, test_policy_manager, real_builder
+):
+ sys_usb = test_qapp.domains["sys-usb"]
+ fedora35 = test_qapp.domains["fedora-35"]
+ testvm = test_qapp.domains["test-vm"]
+ handler = U2FPolicyHandler(
+ test_qapp, test_policy_manager, real_builder, {sys_usb}
+ )
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
# settings from conftest: only vms that have this available are 'test-vm'
# and 'fedora-35', only test-vm can use the service, policy is default
@@ -821,10 +1024,11 @@ def test_u2f_handler_add_without_service(test_qapp,
assert handler.enable_some_handler.selected_vms == [testvm]
handler.register_some_handler.add_button.clicked()
- handler.register_some_handler.add_qube_model.select_value('fedora-35')
+ handler.register_some_handler.add_qube_model.select_value("fedora-35")
# refuse
- with patch('qubes_config.global_config.usb_devices.'
- 'ask_question') as mock_question:
+ with patch(
+ "qubes_config.global_config.usb_devices.ask_question"
+ ) as mock_question:
mock_question.return_value = Gtk.ResponseType.NO
handler.register_some_handler.add_confirm.clicked()
assert mock_question.mock_calls
@@ -832,8 +1036,9 @@ def test_u2f_handler_add_without_service(test_qapp,
assert handler.enable_some_handler.selected_vms == [testvm]
# accept
- with patch('qubes_config.global_config.usb_devices.'
- 'ask_question') as mock_question:
+ with patch(
+ "qubes_config.global_config.usb_devices.ask_question"
+ ) as mock_question:
mock_question.return_value = Gtk.ResponseType.YES
handler.register_some_handler.add_confirm.clicked()
assert mock_question.mock_calls
@@ -843,88 +1048,105 @@ def test_u2f_handler_add_without_service(test_qapp,
def test_devices_handler_unsaved(test_qapp, test_policy_manager, real_builder):
- test_qapp.expected_calls[('sys-usb', "admin.vm.device.pci.Attached",
- None, None)] = \
- b"0\x00dom0+00_0d.0 device_id='*' port_id='00_0d.0' devclass='pci' " \
- b"backend_domain='dom0' mode='required' " \
+ test_qapp.expected_calls[
+ ("sys-usb", "admin.vm.device.pci.Attached", None, None)
+ ] = (
+ b"0\x00dom0+00_0d.0 device_id='*' port_id='00_0d.0' devclass='pci' "
+ b"backend_domain='dom0' mode='required' "
b"_no-strict-reset='yes'\n"
- test_qapp.expected_calls[('dom0', "admin.vm.device.pci.Available",
- None, None)] = \
- b"0\x0000_0d.0 device_id='0000:0000::p0c0300' port_id='00_0d.0' " \
- b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' " \
- b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0d_0' " \
+ )
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.device.pci.Available", None, None)
+ ] = (
+ b"0\x0000_0d.0 device_id='0000:0000::p0c0300' port_id='00_0d.0' "
+ b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' "
+ b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0d_0' "
b"_device='0d'\n"
+ )
handler = DevicesHandler(test_qapp, test_policy_manager, real_builder)
- assert handler.get_unsaved() == ''
+ assert handler.get_unsaved() == ""
# some changes
kb_widget = handler.input_handler.widgets[
- ('qubes.InputKeyboard', 'sys-usb')]
- assert kb_widget.model.get_selected() == 'deny'
- kb_widget.model.select_value('ask')
+ ("qubes.InputKeyboard", "sys-usb")
+ ]
+ assert kb_widget.model.get_selected() == "deny"
+ kb_widget.model.select_value("ask")
assert handler.u2f_handler.enable_check.get_active()
handler.u2f_handler.enable_check.set_active(False)
- assert 'Keyboard input' in handler.get_unsaved()
- assert 'U2F disabled' in handler.get_unsaved()
+ assert "Keyboard input" in handler.get_unsaved()
+ assert "U2F disabled" in handler.get_unsaved()
-def test_devices_handler_detect_usbvms(test_qapp,
- test_policy_manager, real_builder):
- test_qapp.expected_calls[('sys-usb', "admin.vm.device.pci.Attached",
- None, None)] = \
- b"0\x00dom0+00_0d.0 device_id='*' port_id='00_0d.0' devclass='pci' " \
- b"backend_domain='dom0' mode='required' " \
+def test_devices_handler_detect_usbvms(
+ test_qapp, test_policy_manager, real_builder
+):
+ test_qapp.expected_calls[
+ ("sys-usb", "admin.vm.device.pci.Attached", None, None)
+ ] = (
+ b"0\x00dom0+00_0d.0 device_id='*' port_id='00_0d.0' devclass='pci' "
+ b"backend_domain='dom0' mode='required' "
b"_no-strict-reset='yes'\n"
- test_qapp.expected_calls[('test-standalone', "admin.vm.device.pci.Attached",
- None, None)] = \
- b"0\x00dom0+00_0f.0 device_id='*' port_id='00_0f.0' devclass='pci' " \
- b"backend_domain='dom0' mode='required' " \
+ )
+ test_qapp.expected_calls[
+ ("test-standalone", "admin.vm.device.pci.Attached", None, None)
+ ] = (
+ b"0\x00dom0+00_0f.0 device_id='*' port_id='00_0f.0' devclass='pci' "
+ b"backend_domain='dom0' mode='required' "
b"_no-strict-reset='yes'\n"
- test_qapp.expected_calls[('dom0', "admin.vm.device.pci.Available",
- None, None)] = \
- b"0\x0000_0f.0 device_id='0000:0000::p0c0300' port_id='00_0f.0' " \
- b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' " \
- b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0f_0' " \
- b"_device='0f'\n" \
- b"00_0d.0 device_id='0000:0000::p0c0300' port_id='00_0d.0' " \
- b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' " \
- b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0d_0' " \
+ )
+ test_qapp.expected_calls[
+ ("dom0", "admin.vm.device.pci.Available", None, None)
+ ] = (
+ b"0\x0000_0f.0 device_id='0000:0000::p0c0300' port_id='00_0f.0' "
+ b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' "
+ b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0f_0' "
+ b"_device='0f'\n"
+ b"00_0d.0 device_id='0000:0000::p0c0300' port_id='00_0d.0' "
+ b"devclass='pci' backend_domain='dom0' interfaces='p0c0300' "
+ b"_function='0' _bus='00' _libvirt_name='pci_0000_00_0d_0' "
b"_device='0d'\n"
+ )
handler = DevicesHandler(test_qapp, test_policy_manager, real_builder)
- sys_usb = test_qapp.domains['sys-usb']
- test_standalone = test_qapp.domains['test-standalone']
+ sys_usb = test_qapp.domains["sys-usb"]
+ test_standalone = test_qapp.domains["test-standalone"]
assert handler.input_handler.usb_qubes == {sys_usb, test_standalone}
-def test_devices_handler_save_reset(test_qapp,
- test_policy_manager, real_builder):
+def test_devices_handler_save_reset(
+ test_qapp, test_policy_manager, real_builder
+):
handler = DevicesHandler(test_qapp, test_policy_manager, real_builder)
# check all handlers have their save/reset called
- with patch.object(handler.u2f_handler, 'save') as mock_u2f, \
- patch.object(handler.input_handler, 'save') as mock_input:
+ with patch.object(handler.u2f_handler, "save") as mock_u2f, patch.object(
+ handler.input_handler, "save"
+ ) as mock_input:
handler.save()
mock_input.assert_called()
mock_u2f.assert_called()
- with patch.object(handler.u2f_handler, 'reset') as mock_u2f, \
- patch.object(handler.input_handler, 'reset') as mock_input:
+ with patch.object(handler.u2f_handler, "reset") as mock_u2f, patch.object(
+ handler.input_handler, "reset"
+ ) as mock_input:
handler.reset()
mock_input.assert_called()
mock_u2f.assert_called()
-def test_devices_handler_no_sys_usb(test_qapp_simple,
- test_policy_manager, real_builder):
- handler = DevicesHandler(test_qapp_simple,
- test_policy_manager, real_builder)
+def test_devices_handler_no_sys_usb(
+ test_qapp_simple, test_policy_manager, real_builder
+):
+ handler = DevicesHandler(
+ test_qapp_simple, test_policy_manager, real_builder
+ )
assert not handler.input_handler.usb_qubes
assert handler.u2f_handler.problem_fatal_box.get_visible()
diff --git a/qubes_config/tests/test_utils.py b/qubes_config/tests/test_utils.py
index 74428df1..037f687a 100644
--- a/qubes_config/tests/test_utils.py
+++ b/qubes_config/tests/test_utils.py
@@ -23,84 +23,91 @@
import pytest
import qubesadmin.exc
-from ..widgets.utils import apply_feature_change, get_boolean_feature, \
- get_feature, apply_feature_change_from_widget, BiDictionary
+from ..widgets.utils import (
+ apply_feature_change,
+ get_boolean_feature,
+ get_feature,
+ apply_feature_change_from_widget,
+ BiDictionary,
+)
+
def test_get_feature(test_qapp):
"""Test if get feature methods behave correctly, in
particular when setting features to None and handling boolean features."""
- feature_name = 'test_feature'
- default_value = 'test'
- vm = test_qapp.domains['test-vm']
+ feature_name = "test_feature"
+ default_value = "test"
+ vm = test_qapp.domains["test-vm"]
# missing feature
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Get',
- feature_name, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
+ ("test-vm", "admin.vm.feature.Get", feature_name, None)
+ ] = b"2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00"
assert get_feature(vm, feature_name, default_value) == default_value
# correct feature
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Get', feature_name, None)] = \
- b'0\0value1'
- assert get_feature(vm, feature_name, default_value) == 'value1'
+ ("test-vm", "admin.vm.feature.Get", feature_name, None)
+ ] = b"0\0value1"
+ assert get_feature(vm, feature_name, default_value) == "value1"
# boolean feature
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Get', feature_name, None)] = \
- b'0\x001'
+ ("test-vm", "admin.vm.feature.Get", feature_name, None)
+ ] = b"0\x001"
assert get_boolean_feature(vm, feature_name, False) is True
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Get',
- feature_name, None)] = \
- b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
+ ("test-vm", "admin.vm.feature.Get", feature_name, None)
+ ] = b"2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00"
assert get_boolean_feature(vm, feature_name, True) is True
# set feature
- call = ('test-vm', 'admin.vm.feature.Set', feature_name, b'1')
- test_qapp.expected_calls[call] = b'0\0'
+ call = ("test-vm", "admin.vm.feature.Set", feature_name, b"1")
+ test_qapp.expected_calls[call] = b"0\0"
apply_feature_change(vm, feature_name, True)
assert call in test_qapp.actual_calls
- call = ('test-vm', 'admin.vm.feature.Set', feature_name, b'text')
- test_qapp.expected_calls[call] = b'0\0'
- apply_feature_change(vm, feature_name, 'text')
+ call = ("test-vm", "admin.vm.feature.Set", feature_name, b"text")
+ test_qapp.expected_calls[call] = b"0\0"
+ apply_feature_change(vm, feature_name, "text")
assert call in test_qapp.actual_calls
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.List', None, None)] = \
- b'0\x00test_feature'
+ ("test-vm", "admin.vm.feature.List", None, None)
+ ] = b"0\x00test_feature"
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Remove', feature_name, None)] = \
- b'0\x001'
+ ("test-vm", "admin.vm.feature.Remove", feature_name, None)
+ ] = b"0\x001"
apply_feature_change(vm, feature_name, None)
- assert ('test-vm', 'admin.vm.feature.Remove', feature_name, None) \
- in test_qapp.actual_calls
+ assert (
+ "test-vm",
+ "admin.vm.feature.Remove",
+ feature_name,
+ None,
+ ) in test_qapp.actual_calls
def test_feature_unavailable(test_qapp):
- feature_name = 'test_feature'
- default_value = 'test'
- vm = test_qapp.domains['test-vm']
+ feature_name = "test_feature"
+ default_value = "test"
+ vm = test_qapp.domains["test-vm"]
# missing feature
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Get',
- feature_name, None)] = \
- b'2\x00QubesDaemonAccessError\x00\x00Feature not available\x00'
+ ("test-vm", "admin.vm.feature.Get", feature_name, None)
+ ] = b"2\x00QubesDaemonAccessError\x00\x00Feature not available\x00"
assert get_feature(vm, feature_name, default_value) == default_value
with pytest.raises(qubesadmin.exc.QubesException):
- test_qapp.expected_calls[('test-vm', 'admin.vm.feature.Set',
- feature_name, b'1')] = \
- b'2\x00QubesDaemonAccessError\x00\x00Feature not available\x00'
+ test_qapp.expected_calls[
+ ("test-vm", "admin.vm.feature.Set", feature_name, b"1")
+ ] = b"2\x00QubesDaemonAccessError\x00\x00Feature not available\x00"
apply_feature_change(vm, feature_name, True)
def test_apply_change_from_widget(test_qapp):
- vm = test_qapp.domains['test-vm']
- feature_name = 'test-feature'
+ vm = test_qapp.domains["test-vm"]
+ feature_name = "test-feature"
class MockWidget:
def __init__(self, changed, value):
@@ -118,36 +125,36 @@ def get_selected(self):
# set correctly
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Set', feature_name,
- b'1')] = b'0\0'
+ ("test-vm", "admin.vm.feature.Set", feature_name, b"1")
+ ] = b"0\0"
apply_feature_change_from_widget(MockWidget(True, True), vm, feature_name)
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Set', feature_name,
- b'text')] = b'0\0'
- apply_feature_change_from_widget(MockWidget(True, 'text'), vm, feature_name)
+ ("test-vm", "admin.vm.feature.Set", feature_name, b"text")
+ ] = b"0\0"
+ apply_feature_change_from_widget(MockWidget(True, "text"), vm, feature_name)
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.List', None, None)] = \
- b'0\x00other-feature'
+ ("test-vm", "admin.vm.feature.List", None, None)
+ ] = b"0\x00other-feature"
test_qapp.expected_calls[
- ('test-vm', 'admin.vm.feature.Remove', feature_name, None)] = \
- b'0\x001'
+ ("test-vm", "admin.vm.feature.Remove", feature_name, None)
+ ] = b"0\x001"
apply_feature_change_from_widget(MockWidget(True, None), vm, feature_name)
def test_bidict():
- d = {'a': 1, 'b': 2}
+ d = {"a": 1, "b": 2}
bidict = BiDictionary(d)
- assert bidict.inverted == {1: 'a', 2: 'b'}
- bidict['c'] = 3
- assert bidict.inverted == {1: 'a', 2: 'b', 3: 'c'}
- del bidict['a']
- assert bidict.inverted == {2: 'b', 3: 'c'}
+ assert bidict.inverted == {1: "a", 2: "b"}
+ bidict["c"] = 3
+ assert bidict.inverted == {1: "a", 2: "b", 3: "c"}
+ del bidict["a"]
+ assert bidict.inverted == {2: "b", 3: "c"}
with pytest.raises(ValueError):
- bidict['b'] = 3
+ bidict["b"] = 3
with pytest.raises(ValueError):
- d = {'a': 1, 'b': 1}
+ d = {"a": 1, "b": 1}
BiDictionary(d)
diff --git a/qubes_config/tests/test_vm_flowbox.py b/qubes_config/tests/test_vm_flowbox.py
index e51f9077..fca58883 100644
--- a/qubes_config/tests/test_vm_flowbox.py
+++ b/qubes_config/tests/test_vm_flowbox.py
@@ -21,12 +21,16 @@
from unittest.mock import patch
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
-from ..global_config.vm_flowbox import VMFlowboxHandler, \
- VMFlowBoxButton, PlaceholderText
+from ..global_config.vm_flowbox import (
+ VMFlowboxHandler,
+ VMFlowBoxButton,
+ PlaceholderText,
+)
def get_visible_vms(flowbox_handler: VMFlowboxHandler):
@@ -45,23 +49,23 @@ def get_visible_vms(flowbox_handler: VMFlowboxHandler):
def test_simple_flowbox_init_empty(test_qapp, test_builder):
- flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', [])
+ flowbox_handler = VMFlowboxHandler(test_builder, test_qapp, "flowtest", [])
assert not flowbox_handler.is_changed()
- assert len(flowbox_handler.flowbox.get_children()) == 1 # only placeholder
- assert isinstance(flowbox_handler.flowbox.get_children()[0],
- PlaceholderText)
+ assert len(flowbox_handler.flowbox.get_children()) == 1 # only placeholder
+ assert isinstance(
+ flowbox_handler.flowbox.get_children()[0], PlaceholderText
+ )
assert flowbox_handler.flowbox.get_children()[0].get_visible()
assert not flowbox_handler.add_box.get_visible()
def test_simple_flowbox_init_not_empty(test_qapp, test_builder):
- initial_vms = [test_qapp.domains['test-vm'],
- test_qapp.domains['test-blue']]
+ initial_vms = [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
assert not flowbox_handler.is_changed()
@@ -73,28 +77,30 @@ def test_simple_flowbox_init_not_empty(test_qapp, test_builder):
assert sorted(initial_vms) == flowbox_handler.selected_vms
-@patch('qubes_config.global_config.vm_flowbox.ask_question',
- return_value=Gtk.ResponseType.YES)
+@patch(
+ "qubes_config.global_config.vm_flowbox.ask_question",
+ return_value=Gtk.ResponseType.YES,
+)
def test_flowbox_remove_button(mock_question, test_qapp, test_builder):
- initial_vms = [test_qapp.domains['test-vm'],
- test_qapp.domains['test-blue']]
+ initial_vms = [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
# remove test-vm
assert not mock_question.mock_calls
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child, VMFlowBoxButton) and child.vm.name == 'test-vm':
+ if isinstance(child, VMFlowBoxButton) and child.vm.name == "test-vm":
child.get_child().clicked()
assert len(mock_question.mock_calls) == 1
- assert get_visible_vms(flowbox_handler) == [test_qapp.domains['test-blue']]
- assert flowbox_handler.selected_vms == [test_qapp.domains['test-blue']]
+ assert get_visible_vms(flowbox_handler) == [test_qapp.domains["test-blue"]]
+ assert flowbox_handler.selected_vms == [test_qapp.domains["test-blue"]]
# remove test-blue
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child, VMFlowBoxButton) and child.vm.name == 'test-blue':
+ if isinstance(child, VMFlowBoxButton) and child.vm.name == "test-blue":
child.get_child().clicked()
assert len(mock_question.mock_calls) == 2
@@ -102,20 +108,21 @@ def test_flowbox_remove_button(mock_question, test_qapp, test_builder):
assert not flowbox_handler.selected_vms
-@patch('qubes_config.global_config.vm_flowbox.ask_question',
- return_value=Gtk.ResponseType.NO)
+@patch(
+ "qubes_config.global_config.vm_flowbox.ask_question",
+ return_value=Gtk.ResponseType.NO,
+)
def test_flowbox_remove_button_no(mock_question, test_qapp, test_builder):
- initial_vms = [test_qapp.domains['test-vm'],
- test_qapp.domains['test-blue']]
+ initial_vms = [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
# remove test-vm
assert not mock_question.mock_calls
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child,
- VMFlowBoxButton) and child.vm.name == 'test-vm':
+ if isinstance(child, VMFlowBoxButton) and child.vm.name == "test-vm":
child.get_child().clicked()
assert len(mock_question.mock_calls) == 1
@@ -124,16 +131,17 @@ def test_flowbox_remove_button_no(mock_question, test_qapp, test_builder):
def test_flowbox_add_vm(test_qapp, test_builder):
- initial_vms = [test_qapp.domains['test-vm']]
+ initial_vms = [test_qapp.domains["test-vm"]]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
# try to add a VM and abort
assert not flowbox_handler.add_box.get_visible()
flowbox_handler.add_button.clicked()
assert flowbox_handler.add_box.get_visible()
- flowbox_handler.add_qube_model.select_value('test-blue')
+ flowbox_handler.add_qube_model.select_value("test-blue")
flowbox_handler.add_cancel.clicked()
assert not flowbox_handler.add_box.get_visible()
@@ -143,42 +151,48 @@ def test_flowbox_add_vm(test_qapp, test_builder):
# now try to add and do not abort
flowbox_handler.add_button.clicked()
assert flowbox_handler.add_box.get_visible()
- flowbox_handler.add_qube_model.select_value('test-blue')
+ flowbox_handler.add_qube_model.select_value("test-blue")
flowbox_handler.add_confirm.clicked()
assert not flowbox_handler.add_box.get_visible()
- expected_vms = sorted([test_qapp.domains['test-vm'],
- test_qapp.domains['test-blue']])
+ expected_vms = sorted(
+ [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
+ )
assert sorted(flowbox_handler.selected_vms) == expected_vms
assert sorted(get_visible_vms(flowbox_handler)) == expected_vms
# now try to add something that's already selected
flowbox_handler.add_button.clicked()
assert flowbox_handler.add_box.get_visible()
- flowbox_handler.add_qube_model.select_value('test-blue')
- with patch('qubes_config.global_config.vm_flowbox.show_error') as \
- mock_error:
+ flowbox_handler.add_qube_model.select_value("test-blue")
+ with patch(
+ "qubes_config.global_config.vm_flowbox.show_error"
+ ) as mock_error:
assert not mock_error.mock_calls
flowbox_handler.add_confirm.clicked()
assert mock_error.mock_calls
# the box should not have hidden, maybe user wants to change selection
assert flowbox_handler.add_box.get_visible()
- expected_vms = sorted([test_qapp.domains['test-vm'],
- test_qapp.domains['test-blue']])
+ expected_vms = sorted(
+ [test_qapp.domains["test-vm"], test_qapp.domains["test-blue"]]
+ )
assert sorted(flowbox_handler.selected_vms) == expected_vms
assert sorted(get_visible_vms(flowbox_handler)) == expected_vms
-@patch('qubes_config.global_config.vm_flowbox.ask_question',
- return_value=Gtk.ResponseType.YES)
+@patch(
+ "qubes_config.global_config.vm_flowbox.ask_question",
+ return_value=Gtk.ResponseType.YES,
+)
def test_save_reset(_mock_question, test_qapp, test_builder):
- test_vm = test_qapp.domains['test-vm']
- blue_vm = test_qapp.domains['test-blue']
+ test_vm = test_qapp.domains["test-vm"]
+ blue_vm = test_qapp.domains["test-blue"]
initial_vms = [test_vm]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
assert not flowbox_handler.is_changed()
@@ -193,8 +207,7 @@ def test_save_reset(_mock_question, test_qapp, test_builder):
# remove added qube
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child,
- VMFlowBoxButton) and child.vm == blue_vm:
+ if isinstance(child, VMFlowBoxButton) and child.vm == blue_vm:
child.get_child().clicked()
assert flowbox_handler.selected_vms == [test_vm]
assert get_visible_vms(flowbox_handler) == [test_vm]
@@ -204,8 +217,7 @@ def test_save_reset(_mock_question, test_qapp, test_builder):
# remove more
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child,
- VMFlowBoxButton) and child.vm == test_vm:
+ if isinstance(child, VMFlowBoxButton) and child.vm == test_vm:
child.get_child().clicked()
assert not flowbox_handler.selected_vms
assert not get_visible_vms(flowbox_handler)
@@ -232,8 +244,10 @@ def test_save_reset(_mock_question, test_qapp, test_builder):
# remove all and save
for child in flowbox_handler.flowbox.get_children():
- if isinstance(child,
- VMFlowBoxButton) and child.vm in [blue_vm, test_vm]:
+ if isinstance(child, VMFlowBoxButton) and child.vm in [
+ blue_vm,
+ test_vm,
+ ]:
child.get_child().clicked()
flowbox_handler.save()
assert not flowbox_handler.selected_vms
@@ -253,19 +267,23 @@ def test_save_reset(_mock_question, test_qapp, test_builder):
def test_flowbox_verify(test_qapp, test_builder):
- test_vm = test_qapp.domains['test-vm']
- red_vm = test_qapp.domains['test-red']
+ test_vm = test_qapp.domains["test-vm"]
+ red_vm = test_qapp.domains["test-red"]
initial_vms = [test_vm]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms,
- verification_callback=lambda vm: vm.name != 'test-blue')
+ test_builder,
+ test_qapp,
+ "flowtest",
+ initial_vms=initial_vms,
+ verification_callback=lambda vm: vm.name != "test-blue",
+ )
# attempt to add and see an erorr
flowbox_handler.add_button.clicked()
assert flowbox_handler.add_box.get_visible()
- flowbox_handler.add_qube_model.select_value('test-blue')
+ flowbox_handler.add_qube_model.select_value("test-blue")
# vm will not be added, but the verification callback is responsible
# for messaging (it can propose additional actions)
flowbox_handler.add_confirm.clicked()
@@ -276,7 +294,7 @@ def test_flowbox_verify(test_qapp, test_builder):
# but adding correct stuff still works
flowbox_handler.add_button.clicked()
assert flowbox_handler.add_box.get_visible()
- flowbox_handler.add_qube_model.select_value('test-red')
+ flowbox_handler.add_qube_model.select_value("test-red")
flowbox_handler.add_confirm.clicked()
assert flowbox_handler.selected_vms == [red_vm, test_vm]
assert get_visible_vms(flowbox_handler) == [red_vm, test_vm]
@@ -284,11 +302,12 @@ def test_flowbox_verify(test_qapp, test_builder):
def test_flowbox_visibility(test_qapp, test_builder):
- test_vm = test_qapp.domains['test-vm']
+ test_vm = test_qapp.domains["test-vm"]
initial_vms = [test_vm]
flowbox_handler = VMFlowboxHandler(
- test_builder, test_qapp, 'flowtest', initial_vms=initial_vms)
+ test_builder, test_qapp, "flowtest", initial_vms=initial_vms
+ )
flowbox_handler.set_visible(True)
assert flowbox_handler.selected_vms == [test_vm]
diff --git a/qubes_config/widgets/gtk_utils.py b/qubes_config/widgets/gtk_utils.py
index 0a333eed..93175218 100644
--- a/qubes_config/widgets/gtk_utils.py
+++ b/qubes_config/widgets/gtk_utils.py
@@ -27,21 +27,21 @@
from typing import Dict, Union, Optional
import gi
-gi.require_version('Gtk', '3.0')
+
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf, GLib, Gdk
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-RESPONSES_OK = {
- _('_OK'): Gtk.ResponseType.OK
-}
+RESPONSES_OK = {_("_OK"): Gtk.ResponseType.OK}
RESPONSES_YES_NO_CANCEL = {
_("_Yes"): Gtk.ResponseType.YES,
_("_No"): Gtk.ResponseType.NO,
- _("_Cancel"): Gtk.ResponseType.CANCEL
+ _("_Cancel"): Gtk.ResponseType.CANCEL,
}
APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock"
@@ -50,8 +50,9 @@
XEVENT = "/var/run/qubes/qubes-clipboard.bin.xevent"
-def load_icon_at_gtk_size(icon_name,
- icon_size: Gtk.IconSize = Gtk.IconSize.LARGE_TOOLBAR):
+def load_icon_at_gtk_size(
+ icon_name, icon_size: Gtk.IconSize = Gtk.IconSize.LARGE_TOOLBAR
+):
"""Load icon from provided name, if available. If not, attempt to treat
provided name as a path. If icon not found in any of the above ways,
load a blank icon of specified size, provided as Gtk.IconSize.
@@ -75,12 +76,14 @@ def load_icon(icon_name: str, width: int = 24, height: int = 24):
try:
# icon_name is a name
image: GdkPixbuf.Pixbuf = Gtk.IconTheme.get_default().load_icon(
- icon_name, width, 0)
+ icon_name, width, 0
+ )
return image
except (TypeError, GLib.Error):
# icon not found in any way
pixbuf: GdkPixbuf.Pixbuf = GdkPixbuf.Pixbuf.new(
- GdkPixbuf.Colorspace.RGB, True, 8, width, height)
+ GdkPixbuf.Colorspace.RGB, True, 8, width, height
+ )
pixbuf.fill(0x000)
return pixbuf
@@ -89,8 +92,13 @@ def show_error(parent, title, text):
"""
Helper function to display error messages.
"""
- return show_dialog_with_icon(parent=parent, title=title, text=text,
- buttons=RESPONSES_OK, icon_name="qubes-info")
+ return show_dialog_with_icon(
+ parent=parent,
+ title=title,
+ text=text,
+ buttons=RESPONSES_OK,
+ icon_name="qubes-info",
+ )
def ask_question(parent, title: str, text: str):
@@ -102,16 +110,16 @@ def ask_question(parent, title: str, text: str):
title=title,
text=text,
buttons=RESPONSES_YES_NO_CANCEL,
- icon_name="qubes-ask"
+ icon_name="qubes-ask",
)
def show_dialog_with_icon(
- parent: Optional[Gtk.Widget],
- title: str,
- text: Union[str, Gtk.Widget],
- buttons: Dict[str, Gtk.ResponseType],
- icon_name: str
+ parent: Optional[Gtk.Widget],
+ title: str,
+ text: Union[str, Gtk.Widget],
+ buttons: Dict[str, Gtk.ResponseType],
+ icon_name: str,
) -> Gtk.ResponseType:
"""
Helper function to show a dialog with icon given by name.
@@ -122,8 +130,9 @@ def show_dialog_with_icon(
dialog.destroy()
if response == Gtk.ResponseType.DELETE_EVENT:
if Gtk.ResponseType.CANCEL in buttons.values():
- # treat exiting from the window as cancel if it's one of the
- # available responses, then no if it's one of the available responses
+ # treat exiting from the window as cancel if it's one of the
+ # available responses, then no if it's one of the available
+ # responses
return Gtk.ResponseType.CANCEL
if Gtk.ResponseType.NO in buttons.values():
return Gtk.ResponseType.NO
@@ -131,11 +140,11 @@ def show_dialog_with_icon(
def show_dialog(
- parent: Gtk.Widget,
- title: str,
- text: Union[str, Gtk.Widget],
- buttons: Dict[str, Gtk.ResponseType],
- widget: Gtk.Widget
+ parent: Gtk.Widget,
+ title: str,
+ text: Union[str, Gtk.Widget],
+ buttons: Dict[str, Gtk.ResponseType],
+ widget: Gtk.Widget,
) -> Gtk.ResponseType:
"""
Show a dialog.
@@ -156,19 +165,19 @@ def show_dialog(
for key, value in buttons.items():
button: Gtk.Button = dialog.add_button(key, value)
button.set_use_underline(True)
- button.get_style_context().add_class('flat_button')
+ button.get_style_context().add_class("flat_button")
if value in [Gtk.ResponseType.YES, Gtk.ResponseType.OK]:
- button.get_style_context().add_class('button_save')
+ button.get_style_context().add_class("button_save")
else:
- button.get_style_context().add_class('button_cancel')
+ button.get_style_context().add_class("button_cancel")
dialog.set_title(title)
content_area: Gtk.Box = dialog.get_content_area()
- content_area.get_style_context().add_class('modal_dialog')
+ content_area.get_style_context().add_class("modal_dialog")
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
- box.get_style_context().add_class('modal_contents')
+ box.get_style_context().add_class("modal_contents")
content_area.pack_start(box, False, False, 0)
box.pack_start(widget, False, False, 0)
@@ -188,11 +197,14 @@ def show_dialog(
return dialog
-def load_theme(widget: Gtk.Widget, light_theme_path: Optional[str] = None,
- dark_theme_path: Optional[str] = None,
- package_name: Optional[str] = None,
- light_file_name: Optional[str] = None,
- dark_file_name: Optional[str] = None) -> Gtk.CssProvider:
+def load_theme(
+ widget: Gtk.Widget,
+ light_theme_path: Optional[str] = None,
+ dark_theme_path: Optional[str] = None,
+ package_name: Optional[str] = None,
+ light_file_name: Optional[str] = None,
+ dark_file_name: Optional[str] = None,
+) -> Gtk.CssProvider:
"""
Load a dark or light theme to current screen, based on widget's
current (system) defaults.
@@ -222,7 +234,8 @@ def load_theme(widget: Gtk.Widget, light_theme_path: Optional[str] = None,
provider = Gtk.CssProvider()
provider.load_from_path(path)
Gtk.StyleContext.add_provider_for_screen(
- screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
return provider
@@ -230,11 +243,12 @@ def is_theme_light(widget):
"""Check if current theme is light or dark"""
style_context: Gtk.StyleContext = widget.get_style_context()
background_color: Gdk.RGBA = style_context.get_background_color(
- Gtk.StateType.NORMAL)
- text_color: Gdk.RGBA = style_context.get_color(
- Gtk.StateType.NORMAL)
- background_intensity = background_color.red + \
- background_color.blue + background_color.green
+ Gtk.StateType.NORMAL
+ )
+ text_color: Gdk.RGBA = style_context.get_color(Gtk.StateType.NORMAL)
+ background_intensity = (
+ background_color.red + background_color.blue + background_color.green
+ )
text_intensity = text_color.red + text_color.blue + text_color.green
return text_intensity < background_intensity
@@ -255,9 +269,9 @@ def appviewer_lock():
def copy_to_global_clipboard(text: str):
"""Copy provided text to global clipboard"""
with appviewer_lock():
- with open(DATA, "w", encoding='utf-8') as contents:
+ with open(DATA, "w", encoding="utf-8") as contents:
contents.write(text)
- with open(FROM, "w", encoding='ascii') as source:
+ with open(FROM, "w", encoding="ascii") as source:
source.write("dom0")
- with open(XEVENT, "w", encoding='ascii') as timestamp:
+ with open(XEVENT, "w", encoding="ascii") as timestamp:
timestamp.write(str(Gtk.get_current_event_time()))
diff --git a/qubes_config/widgets/gtk_widgets.py b/qubes_config/widgets/gtk_widgets.py
index 005bda60..869f257d 100644
--- a/qubes_config/widgets/gtk_widgets.py
+++ b/qubes_config/widgets/gtk_widgets.py
@@ -26,7 +26,7 @@
import qubesadmin.vm
import itertools
-gi.require_version('Gtk', '3.0')
+gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf
from typing import Optional, Callable, Dict, Any, Union, List
@@ -34,20 +34,24 @@
from .gtk_utils import load_icon, is_theme_light
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-NONE_CATEGORY = {
- "None": _("(none)")
-}
+NONE_CATEGORY = {"None": _("(none)")}
class TokenName(Gtk.Box):
"""
A Gtk.Box containing a (optionally changing) nicely formatted token/vm name.
"""
- def __init__(self, token_name: str, qapp: qubesadmin.Qubes,
- categories: Optional[Dict[str, str]] = None):
+
+ def __init__(
+ self,
+ token_name: str,
+ qapp: qubesadmin.Qubes,
+ categories: Optional[Dict[str, str]] = None,
+ ):
"""
:param token_name: string for of the token
:param qapp: Qubes object
@@ -73,7 +77,7 @@ def set_token(self, token_name):
nice_name = self.categories.get(token_name, token_name)
label = Gtk.Label()
label.set_text(nice_name)
- label.get_style_context().add_class('qube-type')
+ label.get_style_context().add_class("qube-type")
label.show_all()
self.pack_start(label, False, False, 0)
@@ -83,6 +87,7 @@ class QubeName(Gtk.Box):
A Gtk.Box containing qube icon plus name, colored in the label color and
bolded.
"""
+
def __init__(self, vm: Optional[qubesadmin.vm.QubesVM]):
"""
:param vm: Qubes VM to be represented.
@@ -90,7 +95,7 @@ def __init__(self, vm: Optional[qubesadmin.vm.QubesVM]):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.vm = vm
self.label = Gtk.Label()
- self.label.set_label(vm.name if vm else _('None'))
+ self.label.set_label(vm.name if vm else _("None"))
self.set_spacing(5)
@@ -102,17 +107,20 @@ def __init__(self, vm: Optional[qubesadmin.vm.QubesVM]):
self.pack_start(self.label, False, False, 0)
- self.get_style_context().add_class('qube-box-base')
+ self.get_style_context().add_class("qube-box-base")
if vm:
- self.get_style_context().add_class(f'qube-box-{vm.label}')
+ self.get_style_context().add_class(f"qube-box-{vm.label}")
else:
- self.get_style_context().add_class('qube-box-black')
+ self.get_style_context().add_class("qube-box-black")
self.show_all()
class TraitSelector(abc.ABC):
- """abstract class representing various widgets for selecting trait value."""
+ """
+ Abstract class representing various widgets for selecting trait value.
+ """
+
@abc.abstractmethod
def get_selected(self):
"""
@@ -139,10 +147,14 @@ class TextModeler(TraitSelector):
"""
Class to handle modeling a text combo box.
"""
- def __init__(self, combobox: Gtk.ComboBoxText,
- values: Dict[str, Any],
- selected_value: Optional[Any] = None,
- style_changes: bool = False):
+
+ def __init__(
+ self,
+ combobox: Gtk.ComboBoxText,
+ values: Dict[str, Any],
+ selected_value: Optional[Any] = None,
+ style_changes: bool = False,
+ ):
"""
:param combobox: target ComboBoxText object
:param values: dictionary of displayed strings and corresponding values.
@@ -176,7 +188,7 @@ def __init__(self, combobox: Gtk.ComboBoxText,
self._initial_text = self._combo.get_active_text()
if style_changes:
- self._combo.connect('changed', self._on_changed)
+ self._combo.connect("changed", self._on_changed)
def get_selected(self):
"""Get currently selected value."""
@@ -197,9 +209,9 @@ def reset(self):
self._combo.set_active_id(self._initial_text)
def _on_changed(self, _widget):
- self._combo.get_style_context().remove_class('combo-changed')
+ self._combo.get_style_context().remove_class("combo-changed")
if self.is_changed():
- self._combo.get_style_context().add_class('combo-changed')
+ self._combo.get_style_context().add_class("combo-changed")
def update_initial(self):
self._initial_text = self._combo.get_active_text()
@@ -211,16 +223,20 @@ class VMListModeler(TraitSelector):
Modeler for Gtk.ComboBox contain a list of qubes VMs.
Based on boring-stuff's code in core-qrexec qrexec_policy_agent.py.
"""
- def __init__(self, combobox: Gtk.ComboBox, qapp: qubesadmin.Qubes,
- filter_function: Optional[Callable[[qubesadmin.vm.QubesVM],
- bool]] = None,
- event_callback: Optional[Callable[[], None]] = None,
- default_value: Optional[Union[qubesadmin.vm.QubesVM, str]] =
- None,
- current_value: Optional[Union[qubesadmin.vm.QubesVM, str]] =
- None,
- style_changes: bool = False,
- additional_options: Optional[Dict[str, str]] = None):
+
+ def __init__(
+ self,
+ combobox: Gtk.ComboBox,
+ qapp: qubesadmin.Qubes,
+ filter_function: Optional[
+ Callable[[qubesadmin.vm.QubesVM], bool]
+ ] = None,
+ event_callback: Optional[Callable[[], None]] = None,
+ default_value: Optional[Union[qubesadmin.vm.QubesVM, str]] = None,
+ current_value: Optional[Union[qubesadmin.vm.QubesVM, str]] = None,
+ style_changes: bool = False,
+ additional_options: Optional[Dict[str, str]] = None,
+ ):
"""
:param combobox: target ComboBox object
:param qapp: Qubes object, necessary to retrieve VM info
@@ -252,8 +268,9 @@ def __init__(self, combobox: Gtk.ComboBox, qapp: qubesadmin.Qubes,
self._icons: Dict[str, Gtk.Image] = {}
self._icon_size = 20
- self._create_entries(filter_function, default_value, additional_options,
- current_value)
+ self._create_entries(
+ filter_function, default_value, additional_options, current_value
+ )
self._apply_model()
@@ -281,11 +298,11 @@ def is_changed(self) -> bool:
def update_initial(self):
"""Inform the widget that information on 'initial' value should
- be updated to whatever the current value is. Useful if saving changes
- happened."""
+ be updated to whatever the current value is. Useful if saving changes
+ happened."""
self._initial_id = self.combo.get_active_id()
if self.style_changes:
- self.entry_box.get_style_context().remove_class('combo-changed')
+ self.entry_box.get_style_context().remove_class("combo-changed")
def reset(self):
"""Reset changes."""
@@ -293,25 +310,26 @@ def reset(self):
def _get_icon(self, name):
if name not in self._icons:
- icon = load_icon(name, self._icon_size, self._icon_size)
+ icon = load_icon(name, self._icon_size, self._icon_size)
self._icons[name] = icon
return self._icons[name]
def _create_entries(
- self,
- filter_function: Optional[Callable[[qubesadmin.vm.QubesVM], bool]],
- default_value: Optional[Union[qubesadmin.vm.QubesVM, str]],
- additional_options: Optional[Dict[str, str]] = None,
- current_value: Optional[str] = None):
+ self,
+ filter_function: Optional[Callable[[qubesadmin.vm.QubesVM], bool]],
+ default_value: Optional[Union[qubesadmin.vm.QubesVM, str]],
+ additional_options: Optional[Dict[str, str]] = None,
+ current_value: Optional[str] = None,
+ ):
if additional_options:
for api_name, display_name in additional_options.items():
if api_name == default_value:
- display_name += _(' (default)')
+ display_name += _(" (default)")
self._entries[display_name] = {
"api_name": api_name,
"icon": None,
- "vm": None
+ "vm": None,
}
for domain in self.qapp.domains:
@@ -322,7 +340,7 @@ def _create_entries(
display_name = vm_name
if domain == default_value:
- display_name = display_name + _(' (default)')
+ display_name = display_name + _(" (default)")
self._entries[display_name] = {
"api_name": vm_name,
@@ -340,7 +358,7 @@ def _create_entries(
self._entries[str(current_value)] = {
"api_name": str(current_value),
"icon": None,
- "vm": None
+ "vm": None,
}
def _get_valid_qube_name(self):
@@ -371,16 +389,17 @@ def _combo_change(self, _widget):
self.change_function()
if self.style_changes:
- self.entry_box.get_style_context().remove_class('combo-changed')
+ self.entry_box.get_style_context().remove_class("combo-changed")
if self.is_changed():
- self.entry_box.get_style_context().add_class('combo-changed')
+ self.entry_box.get_style_context().add_class("combo-changed")
def _apply_model(self):
assert isinstance(self.combo, Gtk.ComboBox)
list_store = Gtk.ListStore(int, str, GdkPixbuf.Pixbuf, str, str, str)
- for entry_no, display_name in zip(itertools.count(),
- sorted(self._entries)):
+ for entry_no, display_name in zip(
+ itertools.count(), sorted(self._entries)
+ ):
entry = self._entries[display_name]
list_store.append(
[
@@ -388,9 +407,10 @@ def _apply_model(self):
display_name,
entry["icon"],
entry["api_name"],
- '#f2f2f2' if entry['vm'] is None else None, # background
- '#000000' if entry['vm'] is None else None, # foreground
- ])
+ "#f2f2f2" if entry["vm"] is None else None, # background
+ "#000000" if entry["vm"] is None else None, # foreground
+ ]
+ )
self.combo.set_model(list_store)
self.combo.set_id_column(1)
@@ -422,8 +442,8 @@ def _apply_model(self):
# use list_store's 4th and 5th columns as source for background and
# foreground color
- self.combo.add_attribute(text_column, 'background', 4)
- self.combo.add_attribute(text_column, 'foreground', 5)
+ self.combo.add_attribute(text_column, "background", 4)
+ self.combo.add_attribute(text_column, "foreground", 5)
self.combo.connect("changed", self._combo_change)
self.entry_box.connect("changed", self._event_callback)
@@ -444,10 +464,12 @@ def get_selected(self) -> Optional[qubesadmin.vm.QubesVM]:
if selected in self._entries:
# special treatment for None:
- if self._entries[selected]['api_name'] == "None":
+ if self._entries[selected]["api_name"] == "None":
return None
- return self._entries[selected]["vm"] or \
- self._entries[selected]["api_name"]
+ return (
+ self._entries[selected]["vm"]
+ or self._entries[selected]["api_name"]
+ )
return None
def select_value(self, vm_name):
@@ -463,7 +485,7 @@ def select_value(self, vm_name):
def is_vm_available(self, vm: qubesadmin.vm.QubesVM) -> bool:
"""Check if given VM is available in the list."""
for entry in self._entries.values():
- if entry['vm'] == vm:
+ if entry["vm"] == vm:
return True
return False
@@ -472,11 +494,15 @@ class ImageListModeler(TraitSelector):
"""
Modeler for Gtk.ComboBox contain a list of icons accompanied by names.
"""
- def __init__(self, combobox: Gtk.ComboBox,
- value_list: Dict[str, Dict[str, Any]],
- event_callback: Optional[Callable[[], None]] = None,
- selected_value: Optional[str] = None,
- style_changes: bool = False):
+
+ def __init__(
+ self,
+ combobox: Gtk.ComboBox,
+ value_list: Dict[str, Dict[str, Any]],
+ event_callback: Optional[Callable[[], None]] = None,
+ selected_value: Optional[str] = None,
+ style_changes: bool = False,
+ ):
"""
:param combobox: target ComboBox object
:param value_list: entries to be stored, in the form of a dict
@@ -499,8 +525,9 @@ def __init__(self, combobox: Gtk.ComboBox,
self._entries: Dict[str, Dict[str, Any]] = value_list
for entry in self._entries.values():
- entry['loaded_icon'] = load_icon(entry['icon'],
- self.icon_size, self.icon_size)
+ entry["loaded_icon"] = load_icon(
+ entry["icon"], self.icon_size, self.icon_size
+ )
self._apply_model()
@@ -526,11 +553,11 @@ def is_changed(self) -> bool:
def update_initial(self):
"""Inform the widget that information on 'initial' value should
- be updated to whatever the current value is. Useful if saving changes
- happened."""
+ be updated to whatever the current value is. Useful if saving changes
+ happened."""
self._initial_id = self.combo.get_active_id()
if self.style_changes:
- self.entry_box.get_style_context().remove_class('combo-changed')
+ self.entry_box.get_style_context().remove_class("combo-changed")
def reset(self):
"""Reset changes."""
@@ -541,9 +568,9 @@ def _combo_change(self, _widget):
self.change_function()
if self.style_changes:
- self.entry_box.get_style_context().remove_class('combo-changed')
+ self.entry_box.get_style_context().remove_class("combo-changed")
if self.is_changed():
- self.entry_box.get_style_context().add_class('combo-changed')
+ self.entry_box.get_style_context().add_class("combo-changed")
def _apply_model(self):
assert isinstance(self.combo, Gtk.ComboBox)
@@ -553,7 +580,7 @@ def _apply_model(self):
list_store.append(
[
entry_name, # 0: displayed name
- entry['loaded_icon'], # 1: icon
+ entry["loaded_icon"], # 1: icon
]
)
@@ -589,7 +616,7 @@ def get_selected(self) -> Optional[Any]:
"""
selected = self.combo.get_active_id()
if selected in self._entries:
- return self._entries[selected]['object']
+ return self._entries[selected]["object"]
return None
def select_name(self, name):
@@ -602,10 +629,14 @@ def select_name(self, name):
class ImageTextButton(Gtk.Button):
"""Button with image and callback function. A simple helper
to avoid boilerplate."""
- def __init__(self, icon_name: str,
- label: Optional[str],
- click_function: Optional[Callable[[Any], Any]]=None,
- style_classes: Optional[List[str]]=None):
+
+ def __init__(
+ self,
+ icon_name: str,
+ label: Optional[str],
+ click_function: Optional[Callable[[Any], Any]] = None,
+ style_classes: Optional[List[str]] = None,
+ ):
super().__init__()
self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.image = Gtk.Image()
@@ -630,6 +661,7 @@ def __init__(self, icon_name: str,
class ProgressBarDialog(Gtk.Window):
"""Simple window showing a progress bar."""
+
def __init__(self, parent_application: Gtk.Application, loading_text: str):
super().__init__()
self.parent_application = parent_application
@@ -640,10 +672,10 @@ def __init__(self, parent_application: Gtk.Application, loading_text: str):
label = Gtk.Label()
label.set_text(loading_text)
self.box.pack_start(label, False, False, 10)
- self.box.get_style_context().add_class('modal_dialog')
+ self.box.get_style_context().add_class("modal_dialog")
self.progress_bar = Gtk.ProgressBar()
- self.progress_bar.get_style_context().add_class('loading')
+ self.progress_bar.get_style_context().add_class("loading")
self.progress_bar.set_fraction(0)
self.current_progress = 0
@@ -651,7 +683,7 @@ def __init__(self, parent_application: Gtk.Application, loading_text: str):
self.update_progress(0)
- self.connect('delete-event', self._quit)
+ self.connect("delete-event", self._quit)
def update_progress(self, value):
"""Update current progressbar progress"""
@@ -669,15 +701,17 @@ def _quit(self, *_args):
class ExpanderHandler:
"""A class to handle showing/hiding something on click."""
- def __init__(self,
- event_button: Gtk.Button,
- data_container: Gtk.Container,
- icon: Gtk.Image,
- label: Optional[Gtk.Label] = None,
- text_shown: Optional[str] = None,
- text_hidden: Optional[str] = None,
- event_callback: Optional[Callable[[bool], None]] = None
- ):
+
+ def __init__(
+ self,
+ event_button: Gtk.Button,
+ data_container: Gtk.Container,
+ icon: Gtk.Image,
+ label: Optional[Gtk.Label] = None,
+ text_shown: Optional[str] = None,
+ text_hidden: Optional[str] = None,
+ event_callback: Optional[Callable[[bool], None]] = None,
+ ):
"""
:param event_button: Gtk.Button that collects the click event
:param data_container: the container with things to hide/show
@@ -697,16 +731,15 @@ def __init__(self,
self.icon = icon
self.event_callback = event_callback
- self.event_button.connect(
- 'clicked', self._show_hide)
+ self.event_button.connect("clicked", self._show_hide)
self.text_shown = text_shown
self.text_hidden = text_hidden
# get variant
- suffix = 'black' if is_theme_light(Gtk.Window()) else 'white'
- self.icon_hidden = load_icon(f'qubes-expander-hidden-{suffix}', 18, 18)
- self.icon_shown = load_icon(f'qubes-expander-shown-{suffix}', 20, 20)
+ suffix = "black" if is_theme_light(Gtk.Window()) else "white"
+ self.icon_hidden = load_icon(f"qubes-expander-hidden-{suffix}", 18, 18)
+ self.icon_shown = load_icon(f"qubes-expander-shown-{suffix}", 20, 20)
self.set_state(False)
@@ -736,8 +769,12 @@ def set_state(self, state: bool):
class ViewportHandler:
"""A class that enables auto-scrolling to the focused widget."""
- def __init__(self, main_window: Gtk.Window,
- scrolled_windows: List[Gtk.ScrolledWindow]):
+
+ def __init__(
+ self,
+ main_window: Gtk.Window,
+ scrolled_windows: List[Gtk.ScrolledWindow],
+ ):
self.scrolled_windows = scrolled_windows
self.main_window = main_window
diff --git a/qubes_config/widgets/utils.py b/qubes_config/widgets/utils.py
index 5b569f9c..be9d74b4 100644
--- a/qubes_config/widgets/utils.py
+++ b/qubes_config/widgets/utils.py
@@ -28,6 +28,7 @@
from typing import Optional, Any, Dict, List
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -39,6 +40,7 @@ def get_feature(vm, feature_name, default_value=None):
except qubesadmin.exc.QubesDaemonAccessError:
return default_value
+
def get_boolean_feature(vm, feature_name, default=False):
"""helper function to get a feature converted to a Bool if it does exist.
Necessary because of the true/false in features being coded as 1/empty
@@ -50,16 +52,20 @@ def get_boolean_feature(vm, feature_name, default=False):
result = default
return result
-def apply_feature_change_from_widget(widget, vm: qubesadmin.vm.QubesVM,
- feature_name:str):
+
+def apply_feature_change_from_widget(
+ widget, vm: qubesadmin.vm.QubesVM, feature_name: str
+):
"""Change a feature value, taking into account weirdness with None.
Widget must support is_changed and get_selected methods."""
if widget.is_changed():
value = widget.get_selected()
apply_feature_change(vm, feature_name, value)
-def apply_feature_change(vm: qubesadmin.vm.QubesVM,
- feature_name: str, new_value: Optional[Any]):
+
+def apply_feature_change(
+ vm: qubesadmin.vm.QubesVM, feature_name: str, new_value: Optional[Any]
+):
"""Change a feature value, taking into account weirdness with None."""
try:
if new_value is None:
@@ -70,13 +76,17 @@ def apply_feature_change(vm: qubesadmin.vm.QubesVM,
except qubesadmin.exc.QubesDaemonAccessError:
# pylint: disable=raise-missing-from
raise qubesadmin.exc.QubesException(
- _("Failed to set {feature_name} due to insufficient "
- "permissions").format(feature_name=feature_name))
+ _(
+ "Failed to set {feature_name} due to insufficient "
+ "permissions"
+ ).format(feature_name=feature_name)
+ )
class BiDictionary(dict):
"""Helper bi-directional dictionary. By design, duplicate values
cause errors."""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.inverted: Dict[Any, Any] = {}
@@ -98,8 +108,9 @@ def __delitem__(self, key):
super().__delitem__(key)
-def compare_rule_lists(rule_list_1: List[Rule],
- rule_list_2: List[Rule]) -> bool:
+def compare_rule_lists(
+ rule_list_1: List[Rule], rule_list_2: List[Rule]
+) -> bool:
"""Check if two provided rule lists are the same. Return True if yes."""
if len(rule_list_1) != len(rule_list_2):
return False
@@ -108,17 +119,28 @@ def compare_rule_lists(rule_list_1: List[Rule],
return False
return True
+
def _open_url_in_dvm(url, default_dvm: qubesadmin.vm.QubesVM):
subprocess.run(
- ['qvm-run', '-p', '--service', f'--dispvm={default_dvm}',
- 'qubes.OpenURL'], input=url.encode(), check=False,
- stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
+ [
+ "qvm-run",
+ "-p",
+ "--service",
+ f"--dispvm={default_dvm}",
+ "qubes.OpenURL",
+ ],
+ input=url.encode(),
+ check=False,
+ stderr=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ )
+
def open_url_in_disposable(url: str, qapp: qubesadmin.Qubes):
"""Open provided url in disposable qube based on default disposable
template"""
default_dvm = qapp.default_dispvm
- open_thread = threading.Thread(group=None,
- target=_open_url_in_dvm,
- args=[url, default_dvm])
+ open_thread = threading.Thread(
+ group=None, target=_open_url_in_dvm, args=[url, default_dvm]
+ )
open_thread.start()
diff --git a/qui/decorators.py b/qui/decorators.py
index 8a76fb6a..39faf613 100644
--- a/qui/decorators.py
+++ b/qui/decorators.py
@@ -1,22 +1,24 @@
#!/usr/bin/env python3
-''' Decorators wrap a `qui.models.PropertiesModel` in a class
+""" Decorators wrap a `qui.models.PropertiesModel` in a class
containing helpful representation methods.
-'''
+"""
# pylint: disable=wrong-import-position,import-error
import gi # isort:skip
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Pango, GLib, GdkPixbuf # isort:skip
from qubesadmin import exc
from qubesadmin.utils import size_to_human
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
-class PropertiesDecorator():
- ''' Base class for all decorators '''
+class PropertiesDecorator:
+ """Base class for all decorators"""
# pylint: disable=too-few-public-methods
@@ -27,13 +29,13 @@ def __init__(self, obj, margins=(5, 5)) -> None:
super().__init__()
def set_margins(self, widget):
- ''' Helper for setting the default margins on a widget '''
+ """Helper for setting the default margins on a widget"""
widget.set_margin_left(self.margin_left)
widget.set_margin_right(self.margin_right)
class DomainDecorator(PropertiesDecorator):
- ''' Useful methods for domain data representation '''
+ """Useful methods for domain data representation"""
def __init__(self, vm, margins=(5, 5)) -> None:
super().__init__(vm, margins)
@@ -56,18 +58,19 @@ def __init__(self, vm):
if self.vm:
self.label.set_label(self.vm.name)
else:
- self.label.set_markup(_('Qube'))
+ self.label.set_markup(_("Qube"))
self.pack_start(self.label, False, False, 0)
- self.outdated_icon = create_icon('outdated')
- self.updateable_icon = create_icon('software-update-available')
+ self.outdated_icon = create_icon("outdated")
+ self.updateable_icon = create_icon("software-update-available")
self.outdated_icon.set_no_show_all(True)
self.updateable_icon.set_no_show_all(True)
self.updateable_icon.set_tooltip_text(_("Updates available"))
self.outdated_icon.set_tooltip_text(
- _("Qube must be restarted to reflect changes in template"))
+ _("Qube must be restarted to reflect changes in template")
+ )
self.update_outdated(False)
self.update_updateable()
@@ -81,10 +84,10 @@ def update_outdated(self, state):
self.update_tooltip()
def update_updateable(self):
- if self.vm is None or not getattr(self.vm, 'updateable', False):
+ if self.vm is None or not getattr(self.vm, "updateable", False):
return
try:
- updates_state = self.vm.features.get('updates-available', False)
+ updates_state = self.vm.features.get("updates-available", False)
except exc.QubesException:
# no access to VM features
updates_state = False
@@ -92,42 +95,49 @@ def update_updateable(self):
self.updates_available = updates_state
self.update_tooltip()
- def update_tooltip(self,
- netvm_changed=False,
- storage_changed=False):
+ def update_tooltip(self, netvm_changed=False, storage_changed=False):
if self.vm is None:
return
tooltip = f"{self.vm.name}"
- if self.vm.klass == 'AdminVM':
+ if self.vm.klass == "AdminVM":
tooltip += _("\nAdministrative domain")
else:
if not self.template_name:
- self.template_name = getattr(self.vm, 'template', None)
- self.template_name = _("None") if not self.template_name \
+ self.template_name = getattr(self.vm, "template", None)
+ self.template_name = (
+ _("None")
+ if not self.template_name
else str(self.template_name)
+ )
if not self.netvm_name or netvm_changed:
- self.netvm_name = getattr(self.vm, 'netvm',
- _("permission denied"))
- self.netvm_name = _("None") if not self.netvm_name \
+ self.netvm_name = getattr(
+ self.vm, "netvm", _("permission denied")
+ )
+ self.netvm_name = (
+ _("None")
+ if not self.netvm_name
else str(self.netvm_name)
+ )
if not self.cur_storage or storage_changed:
try:
- self.cur_storage = \
- self.vm.get_disk_utilization() / 1024 ** 3
+ self.cur_storage = (
+ self.vm.get_disk_utilization() / 1024**3
+ )
except (exc.QubesDaemonNoResponseError, KeyError):
self.cur_storage = 0
if not self.max_storage or storage_changed:
try:
- self.max_storage = \
- self.vm.volumes['private'].size / 1024 ** 3
+ self.max_storage = (
+ self.vm.volumes["private"].size / 1024**3
+ )
except (exc.QubesDaemonNoResponseError, KeyError):
self.max_storage = 0
@@ -136,20 +146,23 @@ def update_tooltip(self,
else:
perc_storage = self.cur_storage / self.max_storage
- tooltip += \
- _("\nTemplate: {template}"
- "\nNetworking: {netvm}"
- "\nPrivate storage: {current_storage:.2f}GB/"
- "{max_storage:.2f}GB ({perc_storage:.1%})").format(
- template=self.template_name,
- netvm=self.netvm_name,
- current_storage=self.cur_storage,
- max_storage=self.max_storage,
- perc_storage=perc_storage)
+ tooltip += _(
+ "\nTemplate: {template}"
+ "\nNetworking: {netvm}"
+ "\nPrivate storage: {current_storage:.2f}GB/"
+ "{max_storage:.2f}GB ({perc_storage:.1%})"
+ ).format(
+ template=self.template_name,
+ netvm=self.netvm_name,
+ current_storage=self.cur_storage,
+ max_storage=self.max_storage,
+ perc_storage=perc_storage,
+ )
if self.outdated:
- tooltip += _("\n\nRestart qube to "
- "apply changes in template.")
+ tooltip += _(
+ "\n\nRestart qube to apply changes in template."
+ )
if self.updates_available:
tooltip += _("\n\nUpdates available.")
@@ -170,13 +183,16 @@ def __init__(self):
def update_state(self, cpu=0, header=False):
if header:
- markup = _('CPU')
+ markup = _("CPU")
elif cpu > 0:
# pylint: disable=consider-using-f-string
- markup = '{:3d}%'.format(cpu)
+ markup = "{:3d}%".format(cpu)
else:
- color = self.cpu_label.get_style_context() \
- .get_color(Gtk.StateFlags.INSENSITIVE).to_color()
+ color = (
+ self.cpu_label.get_style_context()
+ .get_color(Gtk.StateFlags.INSENSITIVE)
+ .to_color()
+ )
markup = f'0%'
self.cpu_label.set_markup(markup)
@@ -189,9 +205,9 @@ def __init__(self):
def update_state(self, memory=0, header=False):
if header:
- markup = _('RAM')
+ markup = _("RAM")
else:
- markup = f'{str(int(memory/1024))} MiB'
+ markup = f"{str(int(memory/1024))} MiB"
self.mem_label.set_markup(markup)
@@ -208,25 +224,24 @@ def cpu(self):
return cpu_widget
def icon(self) -> Gtk.Image:
- ''' Returns a `Gtk.Image` containing the colored lock icon '''
- if self.vm is None: # should not be called
+ """Returns a `Gtk.Image` containing the colored lock icon"""
+ if self.vm is None: # should not be called
return None
try:
# this is a temporary, emergency fix for unexecpected conflict with
# qui-devices rewrite
- icon = getattr(self.vm, 'icon', self.vm.label.icon)
+ icon = getattr(self.vm, "icon", self.vm.label.icon)
except exc.QubesDaemonCommunicationError:
# no permission to access icon
- icon = 'appvm-black'
- icon_vm = Gtk.IconTheme.get_default().load_icon(
- icon, 16, 0)
+ icon = "appvm-black"
+ icon_vm = Gtk.IconTheme.get_default().load_icon(icon, 16, 0)
icon_img = Gtk.Image.new_from_pixbuf(icon_vm)
return icon_img
def netvm(self) -> Gtk.Label:
- netvm = getattr(self.vm, 'netvm', _("permission denied"))
+ netvm = getattr(self.vm, "netvm", _("permission denied"))
if netvm is None:
- label = Gtk.Label(_('No'), xalign=0)
+ label = Gtk.Label(_("No"), xalign=0)
else:
label = Gtk.Label(str(netvm), xalign=0)
@@ -235,30 +250,30 @@ def netvm(self) -> Gtk.Label:
def device_hbox(device) -> Gtk.Box:
- ''' Returns a :class:`Gtk.Box` containing the device name & icon.. '''
- if device.devclass == 'block':
- icon = 'drive-removable-media'
- elif device.devclass == 'mic':
- icon = 'audio-input-microphone'
- elif device.devclass == 'usb':
- icon = 'generic-usb'
+ """Returns a :class:`Gtk.Box` containing the device name & icon.."""
+ if device.devclass == "block":
+ icon = "drive-removable-media"
+ elif device.devclass == "mic":
+ icon = "audio-input-microphone"
+ elif device.devclass == "usb":
+ icon = "generic-usb"
else:
- icon = 'emblem-important'
+ icon = "emblem-important"
dev_icon = create_icon(icon)
name_label = Gtk.Label(xalign=0)
name = f"{device.backend_domain}:{device.port_id} - {device.description}"
if device.attachments:
dev_list = ", ".join(list(device.attachments))
- name_label.set_markup(f'{name} ({dev_list})')
+ name_label.set_markup(f"{name} ({dev_list})")
else:
name_label.set_text(name)
name_label.set_max_width_chars(64)
name_label.set_ellipsize(Pango.EllipsizeMode.END)
size_label = Gtk.Label(xalign=1)
- if device.devclass == 'block' and 'size' in device.data:
- size_label.set_text(size_to_human(int(device.data['size'])))
+ if device.devclass == "block" and "size" in device.data:
+ size_label.set_text(size_to_human(int(device.data["size"])))
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
hbox.pack_start(name_label, True, True, 0)
@@ -273,15 +288,15 @@ def device_domain_hbox(vm, attached: bool) -> Gtk.Box:
# hbox.pack_start(label, True, True, 5)
if attached:
- eject_icon = create_icon('media-eject')
+ eject_icon = create_icon("media-eject")
hbox.pack_start(eject_icon, False, False, 5)
else:
- add_icon = create_icon('list-add')
+ add_icon = create_icon("list-add")
hbox.pack_start(add_icon, False, False, 5)
name = Gtk.Label(xalign=0)
if attached:
- name.set_markup(f'{vm.vm_name}')
+ name.set_markup(f"{vm.vm_name}")
else:
name.set_text(vm.vm_name)
@@ -290,11 +305,11 @@ def device_domain_hbox(vm, attached: bool) -> Gtk.Box:
def create_icon(name) -> Gtk.Image:
- """" Create an icon from string; tries for both the normal and -symbolic
- variants, because some themes only have the symbolic variant. If not
- found, outputs a blank icon."""
+ """ " Create an icon from string; tries for both the normal and -symbolic
+ variants, because some themes only have the symbolic variant. If not
+ found, outputs a blank icon."""
- names = [name, f'{name}-symbolic']
+ names = [name, f"{name}-symbolic"]
pixbuf = None
for icon_name in names:
try:
diff --git a/qui/devices/actionable_widgets.py b/qui/devices/actionable_widgets.py
index 266bd8a1..812337b8 100644
--- a/qui/devices/actionable_widgets.py
+++ b/qui/devices/actionable_widgets.py
@@ -30,7 +30,8 @@
import qubesadmin.vm
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, GdkPixbuf, GLib # isort:skip
from . import backend
@@ -49,24 +50,32 @@ def load_icon(icon_name: str, backup_name: str, size: int = 24):
"""
try:
image: GdkPixbuf.Pixbuf = Gtk.IconTheme.get_default().load_icon(
- icon_name, size, 0)
+ icon_name, size, 0
+ )
return image
except (TypeError, GLib.Error):
try:
image: GdkPixbuf.Pixbuf = Gtk.IconTheme.get_default().load_icon(
- backup_name, size, 0)
+ backup_name, size, 0
+ )
return image
except (TypeError, GLib.Error):
try:
# this is a workaround in case we are running this locally
- icon_path = str(pathlib.Path().resolve()) + \
- '/icons/scalable/' + icon_name + '.svg'
+ icon_path = (
+ str(pathlib.Path().resolve())
+ + "/icons/scalable/"
+ + icon_name
+ + ".svg"
+ )
return GdkPixbuf.Pixbuf.new_from_file_at_size(
- icon_path, size, size)
+ icon_path, size, size
+ )
except (GLib.Error, TypeError):
# we are giving up and just using a blank icon
pixbuf: GdkPixbuf.Pixbuf = GdkPixbuf.Pixbuf.new(
- GdkPixbuf.Colorspace.RGB, True, 8, size, size)
+ GdkPixbuf.Colorspace.RGB, True, 8, size, size
+ )
pixbuf.fill(0x000)
return pixbuf
@@ -74,6 +83,7 @@ def load_icon(icon_name: str, backup_name: str, size: int = 24):
class ActionableWidget:
"""abstract class to be used in various clickable items in menus and
list items"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# should this widget be sensitive?
@@ -94,10 +104,10 @@ def __init__(self, icon_name, initial_variant: str, size: int):
"""
super().__init__()
- self.light_icon = load_icon(icon_name + '-light', icon_name, size)
- self.dark_icon = load_icon(icon_name + '-dark', icon_name, size)
+ self.light_icon = load_icon(icon_name + "-light", icon_name, size)
+ self.dark_icon = load_icon(icon_name + "-dark", icon_name, size)
- if initial_variant == 'light':
+ if initial_variant == "light":
self.set_from_pixbuf(self.light_icon)
self.is_light = True
else:
@@ -114,8 +124,13 @@ def toggle_icon(self):
class VMWithIcon(Gtk.Box):
- def __init__(self, vm: backend.VM, size: int = 18, variant: str = 'dark',
- name_extension: Optional[str] = None):
+ def __init__(
+ self,
+ vm: backend.VM,
+ size: int = 18,
+ variant: str = "dark",
+ name_extension: Optional[str] = None,
+ ):
"""
Icon with VM name and optional text name extension in parentheses.
:param vm: VM object
@@ -136,7 +151,7 @@ def __init__(self, vm: backend.VM, size: int = 18, variant: str = 'dark',
self.pack_start(self.backend_icon, False, False, 4)
self.pack_start(self.backend_label, False, False, 0)
- self.get_style_context().add_class('vm_item')
+ self.get_style_context().add_class("vm_item")
class VMAttachmentDiagram(Gtk.Box):
@@ -144,60 +159,64 @@ class VMAttachmentDiagram(Gtk.Box):
Device attachment scheme, in the following form:
backend_vm (device name) [-> frontend_vm[, other_frontend+]]
"""
- def __init__(self, device: backend.Device,
- variant: str = 'dark'):
+
+ def __init__(self, device: backend.Device, variant: str = "dark"):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
backend_vm = device.backend_domain
frontend_vms = list(device.attachments)
# backend is always there
- backend_vm_icon = VMWithIcon(backend_vm,
- name_extension=device.id_string)
- backend_vm_icon.get_style_context().add_class('main_device_vm')
+ backend_vm_icon = VMWithIcon(
+ backend_vm, name_extension=device.id_string
+ )
+ backend_vm_icon.get_style_context().add_class("main_device_vm")
self.pack_start(backend_vm_icon, False, False, 4)
if frontend_vms:
# arrow
- self.arrow = VariantIcon('arrow', variant, 15)
+ self.arrow = VariantIcon("arrow", variant, 15)
self.pack_start(self.arrow, False, False, 4)
for vm in frontend_vms:
# vm
# potential topic to explore: commas
vm_name = VMWithIcon(vm)
- vm_name.get_style_context().add_class('main_device_vm')
+ vm_name.get_style_context().add_class("main_device_vm")
self.pack_start(vm_name, False, False, 4)
#### Non-interactive items
+
class InfoHeader(Gtk.Label, ActionableWidget):
"""
Simple header with a bolded name, left-aligned.
"""
+
def __init__(self, text):
super().__init__()
self.set_text(text)
- self.get_style_context().add_class('device_header')
- self.get_style_context().add_class('main_device_item')
+ self.get_style_context().add_class("device_header")
+ self.get_style_context().add_class("main_device_item")
self.set_halign(Gtk.Align.START)
self.actionable = False
class SeparatorItem(Gtk.Separator, ActionableWidget):
"""Separator item"""
+
def __init__(self):
super().__init__()
self.actionable = False
- self.get_style_context().add_class('separator_item')
+ self.get_style_context().add_class("separator_item")
#### Attach/detach action items
class SimpleActionWidget(Gtk.Box):
- def __init__(self, icon_name, text, variant: str = 'dark'):
+ def __init__(self, icon_name, text, variant: str = "dark"):
"""Widget with an action and an icon."""
super().__init__()
self.set_orientation(Gtk.Orientation.HORIZONTAL)
@@ -206,7 +225,7 @@ def __init__(self, icon_name, text, variant: str = 'dark'):
self.text_label.set_line_wrap_mode(Gtk.WrapMode.WORD)
self.text_label.set_markup(text)
self.text_label.set_xalign(0)
- self.get_style_context().add_class('vm_item')
+ self.get_style_context().add_class("vm_item")
self.pack_start(self.icon, False, False, 5)
self.pack_start(self.text_label, True, True, 0)
@@ -214,6 +233,7 @@ def __init__(self, icon_name, text, variant: str = 'dark'):
class AttachWidget(ActionableWidget, VMWithIcon):
"""Attach device to qube action"""
+
def __init__(self, vm: backend.VM, device: backend.Device):
super().__init__(vm)
self.vm = vm
@@ -225,10 +245,13 @@ def widget_action(self, *_args):
class DetachWidget(ActionableWidget, SimpleActionWidget):
"""Detach device from a VM"""
- def __init__(self, vm: backend.VM, device: backend.Device,
- variant: str = 'dark'):
- super().__init__('detach', 'Detach from ' + vm.name + '',
- variant)
+
+ def __init__(
+ self, vm: backend.VM, device: backend.Device, variant: str = "dark"
+ ):
+ super().__init__(
+ "detach", "Detach from " + vm.name + "", variant
+ )
self.vm = vm
self.device = device
@@ -238,10 +261,13 @@ def widget_action(self, *_args):
class DetachAndShutdownWidget(ActionableWidget, SimpleActionWidget):
"""Detach device from a disposable VM and shut it down."""
- def __init__(self, vm: backend.VM, device: backend.Device,
- variant: str = 'dark'):
- super().__init__('detach', 'Detach and shut down ' +
- vm.name + '', variant)
+
+ def __init__(
+ self, vm: backend.VM, device: backend.Device, variant: str = "dark"
+ ):
+ super().__init__(
+ "detach", "Detach and shut down " + vm.name + "", variant
+ )
self.vm = vm
self.device = device
@@ -252,8 +278,10 @@ def widget_action(self, *_args):
class DetachAndAttachWidget(ActionableWidget, VMWithIcon):
"""Detach device from current attachment(s) and attach to another"""
- def __init__(self, vm: backend.VM, device: backend.Device,
- variant: str = 'dark'):
+
+ def __init__(
+ self, vm: backend.VM, device: backend.Device, variant: str = "dark"
+ ):
super().__init__(vm, variant=variant)
self.vm = vm
self.device = device
@@ -266,15 +294,18 @@ def widget_action(self, *_args):
class AttachDisposableWidget(ActionableWidget, VMWithIcon):
"""Attach to a new disposable qube"""
- def __init__(self, vm: backend.VM, device: backend.Device,
- variant: str = 'dark'):
+
+ def __init__(
+ self, vm: backend.VM, device: backend.Device, variant: str = "dark"
+ ):
super().__init__(vm, variant=variant)
self.vm = vm
self.device = device
def widget_action(self, *_args):
- new_dispvm = qubesadmin.vm.DispVM.from_appvm(self.vm.vm_object.app,
- self.vm)
+ new_dispvm = qubesadmin.vm.DispVM.from_appvm(
+ self.vm.vm_object.app, self.vm
+ )
new_dispvm.start()
self.device.attach_to_vm(backend.VM(new_dispvm))
@@ -282,16 +313,19 @@ def widget_action(self, *_args):
class DetachAndAttachDisposableWidget(ActionableWidget, VMWithIcon):
"""Detach from all current attachments and attach to new disposable"""
- def __init__(self, vm: backend.VM, device: backend.Device,
- variant: str = 'dark'):
+
+ def __init__(
+ self, vm: backend.VM, device: backend.Device, variant: str = "dark"
+ ):
super().__init__(vm, variant=variant)
self.vm = vm
self.device = device
def widget_action(self, *_args):
self.device.detach_from_vm(self.vm)
- new_dispvm = qubesadmin.vm.DispVM.from_appvm(self.vm.vm_object.app,
- self.vm)
+ new_dispvm = qubesadmin.vm.DispVM.from_appvm(
+ self.vm.vm_object.app, self.vm
+ )
new_dispvm.start()
self.device.attach_to_vm(backend.VM(new_dispvm))
@@ -299,13 +333,14 @@ def widget_action(self, *_args):
#### Other actions
+
class DeviceSettingsWidget(ActionableWidget, SimpleActionWidget):
"""
Not yet implemented.
"""
- def __init__(self, device: backend.Device, variant: str = 'dark'):
- super().__init__('settings', 'Device settings',
- variant)
+
+ def __init__(self, device: backend.Device, variant: str = "dark"):
+ super().__init__("settings", "Device settings", variant)
self.device = device
def widget_action(self, *_args):
@@ -316,8 +351,9 @@ class GlobalSettingsWidget(ActionableWidget, SimpleActionWidget):
"""
Not yet implemented.
"""
- def __init__(self, device: backend.Device, variant: str = 'dark'):
- super().__init__('settings', 'Global device settings', variant)
+
+ def __init__(self, device: backend.Device, variant: str = "dark"):
+ super().__init__("settings", "Global device settings", variant)
self.device = device
def widget_action(self, *_args):
@@ -328,18 +364,20 @@ class HelpWidget(ActionableWidget, SimpleActionWidget):
"""
Not yet implemented.
"""
- def __init__(self, device: backend.Device, variant: str = 'dark'):
- super().__init__('question-icon', 'Help', variant)
+
+ def __init__(self, device: backend.Device, variant: str = "dark"):
+ super().__init__("question-icon", "Help", variant)
self.device = device
def widget_action(self, *_args):
pass
+
#### Device info widget
class DeviceHeaderWidget(Gtk.Box, ActionableWidget):
- def __init__(self, device: backend.Device, variant: str = 'dark'):
+ def __init__(self, device: backend.Device, variant: str = "dark"):
"""General information about the device - name, in the future also
a button to rename the device."""
super().__init__(orientation=Gtk.Orientation.VERTICAL)
@@ -368,7 +406,7 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
self.device_label = Gtk.Label()
self.device_label.set_markup(device.name)
- self.device_label.get_style_context().add_class('device_name')
+ self.device_label.get_style_context().add_class("device_name")
self.device_label.set_xalign(Gtk.Align.CENTER)
self.device_label.set_halign(Gtk.Align.CENTER)
@@ -389,7 +427,8 @@ class MainDeviceWidget(ActionableWidget, Gtk.Grid):
| dev_icon | device_name |
| | backend_vm | (arrow) | frontend_vm[s] |
"""
- def __init__(self, device: backend.Device, variant: str = 'dark'):
+
+ def __init__(self, device: backend.Device, variant: str = "dark"):
super().__init__()
self.device = device
self.variant = variant
@@ -399,7 +438,7 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
# reduce NEW! label timeout to 2 minutes after 1st view
self._new_device_label_afterview = 2 * 60
- self.get_style_context().add_class('main_device_item')
+ self.get_style_context().add_class("main_device_item")
# the part that is common to all devices
@@ -409,8 +448,10 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
self.device_label = Gtk.Label(xalign=0)
label_markup = device.name
- if (device.connection_timestamp and
- int(time.monotonic() - device.connection_timestamp) < 120):
+ if (
+ device.connection_timestamp
+ and int(time.monotonic() - device.connection_timestamp) < 120
+ ):
label_markup += ' NEW'
self.device_label.set_markup(label_markup)
@@ -425,8 +466,9 @@ def __init__(self, device: backend.Device, variant: str = 'dark'):
self.vm_diagram = VMAttachmentDiagram(device, self.variant)
self.attach(self.vm_diagram, 1, 1, 3, 1)
- def get_child_widgets(self, vms, disp_vm_templates) -> \
- Iterable[ActionableWidget]:
+ def get_child_widgets(
+ self, vms, disp_vm_templates
+ ) -> Iterable[ActionableWidget]:
"""
Get type-appropriate list of child widgets.
:return: iterable of ActionableWidgets, ready to be packed in somewhere
@@ -454,8 +496,9 @@ def get_child_widgets(self, vms, disp_vm_templates) -> \
yield InfoHeader("Detach and attach to new disposable qube:")
for vm in disp_vm_templates:
- yield DetachAndAttachDisposableWidget(vm, self.device,
- self.variant)
+ yield DetachAndAttachDisposableWidget(
+ vm, self.device, self.variant
+ )
else:
yield InfoHeader("Attach to qube:")
@@ -470,8 +513,9 @@ def get_child_widgets(self, vms, disp_vm_templates) -> \
yield AttachDisposableWidget(vm, self.device, self.variant)
-def generate_wrapper_widget(widget_class: Callable,
- signal: str, inside_widget: ActionableWidget):
+def generate_wrapper_widget(
+ widget_class: Callable, signal: str, inside_widget: ActionableWidget
+):
"""
Wraps a provided
:param widget_class: "outside" widget class, e.g. Gtk.MenuItem
diff --git a/qui/devices/backend.py b/qui/devices/backend.py
index b496a73e..00f45d43 100644
--- a/qui/devices/backend.py
+++ b/qui/devices/backend.py
@@ -27,10 +27,12 @@
from qubesadmin.device_protocol import DeviceAssignment
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Gio # isort:skip
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -39,6 +41,7 @@ class VM:
"""
Wrapper for various VMs that can serve as backend/frontend
"""
+
def __init__(self, vm: qubesadmin.vm.QubesVM):
self.__hash = hash(vm)
self._vm = vm
@@ -61,23 +64,23 @@ def __hash__(self):
def icon_name(self):
"""Name of the VM icon"""
try:
- return getattr(self._vm, 'icon', self._vm.label.icon)
+ return getattr(self._vm, "icon", self._vm.label.icon)
except qubesadmin.exc.QubesException:
- return 'appvm-black'
+ return "appvm-black"
@property
def is_dispvm_template(self) -> bool:
"""
Is this VM a dispvm template?
"""
- return getattr(self._vm, 'template_for_dispvms', False)
+ return getattr(self._vm, "template_for_dispvms", False)
@property
def is_attachable(self) -> bool:
"""
Should this VM be listed as possible attachment target in the GUI?
"""
- return self.vm_class != 'AdminVM' and self._vm.is_running()
+ return self.vm_class != "AdminVM" and self._vm.is_running()
@property
def vm_object(self):
@@ -95,36 +98,38 @@ def should_be_cleaned_up(self):
class Device:
- def __init__(self, dev: qubesadmin.devices.DeviceInfo,
- gtk_app: Gtk.Application):
+ def __init__(
+ self, dev: qubesadmin.devices.DeviceInfo, gtk_app: Gtk.Application
+ ):
self.gtk_app: Gtk.Application = gtk_app
self._dev: qubesadmin.devices.DeviceInfo = dev
self.__hash = hash(dev)
- self._port: str = ''
+ self._port: str = ""
# Monotonic connection timestamp only for new devices
self.connection_timestamp: float = None
- self._dev_name: str = getattr(dev, 'description', 'unknown')
- if dev.devclass == 'block' and 'size' in dev.data:
- self._dev_name += " (" + size_to_human(int(dev.data['size'])) + ")"
+ self._dev_name: str = getattr(dev, "description", "unknown")
+ if dev.devclass == "block" and "size" in dev.data:
+ self._dev_name += " (" + size_to_human(int(dev.data["size"])) + ")"
- self._ident: str = getattr(dev, 'port_id', 'unknown')
- self._description: str = getattr(dev, 'description', 'unknown')
- self._devclass: str = getattr(dev, 'devclass', 'unknown')
- self._data: Dict = getattr(dev, 'data', {})
- self._device_id = getattr(dev, 'device_id', '*')
+ self._ident: str = getattr(dev, "port_id", "unknown")
+ self._description: str = getattr(dev, "description", "unknown")
+ self._devclass: str = getattr(dev, "devclass", "unknown")
+ self._data: Dict = getattr(dev, "data", {})
+ self._device_id = getattr(dev, "device_id", "*")
self.attachments: Set[VM] = set()
- backend_domain = getattr(dev, 'backend_domain', None)
+ backend_domain = getattr(dev, "backend_domain", None)
if backend_domain:
self._backend_domain: Optional[VM] = VM(backend_domain)
else:
self._backend_domain: Optional[VM] = None
try:
- self.vm_icon: str = getattr(dev.backend_domain, 'icon',
- dev.backend_domain.label.icon)
+ self.vm_icon: str = getattr(
+ dev.backend_domain, "icon", dev.backend_domain.label.icon
+ )
except qubesadmin.exc.QubesException:
- self.vm_icon: str = 'appvm-black'
+ self.vm_icon: str = "appvm-black"
def __str__(self):
return self._dev_name
@@ -163,11 +168,11 @@ def device_class(self) -> str:
@property
def device_icon(self) -> str:
"""Device icon"""
- if self.device_class == 'block':
- return 'harddrive'
- if self.device_class == 'mic':
- return 'mic'
- return ''
+ if self.device_class == "block":
+ return "harddrive"
+ if self.device_class == "mic":
+ return "mic"
+ return ""
@property
def backend_domain(self) -> Optional[VM]:
@@ -187,41 +192,41 @@ def notification_id(self) -> str:
@property
def device_group(self) -> str:
"""Device group for purposes of menus."""
- if self._devclass == 'block':
- return 'Data (Block) Devices'
- if self._devclass == 'usb':
- return 'USB Devices'
- if self._devclass == 'mic':
- return 'Microphones'
+ if self._devclass == "block":
+ return "Data (Block) Devices"
+ if self._devclass == "usb":
+ return "USB Devices"
+ if self._devclass == "mic":
+ return "Microphones"
# TODO: those below come from new API, may need an update
- if self._devclass == 'Other':
- return 'Other Devices'
- if self._devclass == 'Communication':
- return 'Other Devices' # eg. modems
- if self._devclass in ('Input', 'Keyboard', 'Mouse'):
- return 'Input Devices'
- if self._devclass in ('Printer', 'Scanner'):
+ if self._devclass == "Other":
+ return "Other Devices"
+ if self._devclass == "Communication":
+ return "Other Devices" # eg. modems
+ if self._devclass in ("Input", "Keyboard", "Mouse"):
+ return "Input Devices"
+ if self._devclass in ("Printer", "Scanner"):
return "Printers and Scanners"
- if self._devclass == 'Multimedia':
- return 'Other Devices'
+ if self._devclass == "Multimedia":
+ return "Other Devices"
# Multimedia = Audio, Video, Displays etc.
- if self._devclass == 'Wireless':
- return 'Other Devices'
- if self._devclass == 'Bluetooth':
- return 'Bluetooth Devices'
- if self._devclass == 'Mass_Data':
- return 'Other Devices'
- if self._devclass == 'Network':
- return 'Other Devices'
- if self._devclass == 'Memory':
- return 'Other Devices'
- if self._devclass.startswith('PCI'):
- return 'PCI Devices'
- if self._devclass == 'Docking Station':
- return 'Docking Station'
- if self._devclass == 'Processor':
- return 'Other Devices'
- return 'Other Devices'
+ if self._devclass == "Wireless":
+ return "Other Devices"
+ if self._devclass == "Bluetooth":
+ return "Bluetooth Devices"
+ if self._devclass == "Mass_Data":
+ return "Other Devices"
+ if self._devclass == "Network":
+ return "Other Devices"
+ if self._devclass == "Memory":
+ return "Other Devices"
+ if self._devclass.startswith("PCI"):
+ return "PCI Devices"
+ if self._devclass == "Docking Station":
+ return "Docking Station"
+ if self._devclass == "Processor":
+ return "Other Devices"
+ return "Other Devices"
@property
def sorting_key(self) -> str:
@@ -233,27 +238,31 @@ def attach_to_vm(self, vm: VM):
Perform attachment to provided VM.
"""
try:
- assignment = DeviceAssignment.new(self.backend_domain,
- port_id=self.id_string, devclass=self.device_class,
- device_id=self._device_id)
+ assignment = DeviceAssignment.new(
+ self.backend_domain,
+ port_id=self.id_string,
+ devclass=self.device_class,
+ device_id=self._device_id,
+ )
vm.vm_object.devices[self.device_class].attach(assignment)
self.gtk_app.emit_notification(
_("Attaching device"),
_("Attaching {} to {}").format(self.description, vm),
Gio.NotificationPriority.NORMAL,
- notification_id=self.notification_id)
+ notification_id=self.notification_id,
+ )
- except Exception as ex: # pylint: disable=broad-except
+ except Exception as ex: # pylint: disable=broad-except
self.gtk_app.emit_notification(
_("Error"),
- _("Attaching device {0} to {1} failed. "
- "Error: {2} - {3}").format(
- self.description, vm, type(ex).__name__,
- ex),
+ _(
+ "Attaching device {0} to {1} failed. Error: {2} - {3}"
+ ).format(self.description, vm, type(ex).__name__, ex),
Gio.NotificationPriority.HIGH,
error=True,
- notification_id=self.notification_id)
+ notification_id=self.notification_id,
+ )
def detach_from_vm(self, vm: VM):
"""
@@ -263,19 +272,23 @@ def detach_from_vm(self, vm: VM):
_("Detaching device"),
_("Detaching {} from {}").format(self.description, vm),
Gio.NotificationPriority.NORMAL,
- notification_id=self.notification_id)
+ notification_id=self.notification_id,
+ )
try:
assignment = DeviceAssignment.new(
- self.backend_domain, self._ident, self.device_class)
+ self.backend_domain, self._ident, self.device_class
+ )
vm.vm_object.devices[self.device_class].detach(assignment)
except qubesadmin.exc.QubesException as ex:
self.gtk_app.emit_notification(
_("Error"),
- _("Detaching device {0} from {1} failed. "
- "Error: {2}").format(self.description, vm, ex),
+ _("Detaching device {0} from {1} failed. Error: {2}").format(
+ self.description, vm, ex
+ ),
Gio.NotificationPriority.HIGH,
error=True,
- notification_id=self.notification_id)
+ notification_id=self.notification_id,
+ )
def detach_from_all(self):
"""
diff --git a/qui/devices/device_widget.py b/qui/devices/device_widget.py
index 942ae298..6b44006f 100644
--- a/qui/devices/device_widget.py
+++ b/qui/devices/device_widget.py
@@ -34,7 +34,8 @@
import qui.utils
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Gdk, Gio # isort:skip
from qui.devices import backend
@@ -43,27 +44,34 @@
from qubes_config.widgets.gtk_utils import is_theme_light
import gbulb
+
gbulb.install()
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
# FUTURE: this should be moved to backend with new API changes
-DEV_TYPES = ['block', 'usb', 'mic']
+DEV_TYPES = ["block", "usb", "mic"]
class DeviceMenu(Gtk.Menu):
"""Menu for handling a single device"""
- def __init__(self, main_item: actionable_widgets.MainDeviceWidget,
- vms: List[backend.VM],
- dispvm_templates: List[backend.VM]):
+
+ def __init__(
+ self,
+ main_item: actionable_widgets.MainDeviceWidget,
+ vms: List[backend.VM],
+ dispvm_templates: List[backend.VM],
+ ):
super().__init__()
for child_widget in main_item.get_child_widgets(vms, dispvm_templates):
item = actionable_widgets.generate_wrapper_widget(
- Gtk.MenuItem, 'activate', child_widget)
+ Gtk.MenuItem, "activate", child_widget
+ )
self.add(item)
self.show_all()
@@ -71,6 +79,7 @@ def __init__(self, main_item: actionable_widgets.MainDeviceWidget,
class DevicesTray(Gtk.Application):
"""Tray application for handling devices."""
+
def __init__(self, app_name, qapp, dispatcher):
super().__init__()
self.name: str = app_name
@@ -90,32 +99,38 @@ def __init__(self, app_name, qapp, dispatcher):
self.initialize_dev_data()
for devclass in DEV_TYPES:
- self.dispatcher.add_handler('device-attach:' + devclass,
- self.device_attached)
- self.dispatcher.add_handler('device-detach:' + devclass,
- self.device_detached)
- self.dispatcher.add_handler('device-list-change:' + devclass,
- self.device_list_update)
-
- self.dispatcher.add_handler('domain-shutdown',
- self.vm_shutdown)
- self.dispatcher.add_handler('domain-start-failed',
- self.vm_shutdown)
- self.dispatcher.add_handler('domain-start', self.vm_start)
-
- self.dispatcher.add_handler('property-set:template_for_dispvms',
- self.vm_dispvm_template_change)
-
- self.dispatcher.add_handler('property-reset:template_for_dispvms',
- self.vm_dispvm_template_change)
- self.dispatcher.add_handler('property-del:template_for_dispvms',
- self.vm_dispvm_template_change)
+ self.dispatcher.add_handler(
+ "device-attach:" + devclass, self.device_attached
+ )
+ self.dispatcher.add_handler(
+ "device-detach:" + devclass, self.device_detached
+ )
+ self.dispatcher.add_handler(
+ "device-list-change:" + devclass, self.device_list_update
+ )
+
+ self.dispatcher.add_handler("domain-shutdown", self.vm_shutdown)
+ self.dispatcher.add_handler("domain-start-failed", self.vm_shutdown)
+ self.dispatcher.add_handler("domain-start", self.vm_start)
+
+ self.dispatcher.add_handler(
+ "property-set:template_for_dispvms", self.vm_dispvm_template_change
+ )
+
+ self.dispatcher.add_handler(
+ "property-reset:template_for_dispvms",
+ self.vm_dispvm_template_change,
+ )
+ self.dispatcher.add_handler(
+ "property-del:template_for_dispvms", self.vm_dispvm_template_change
+ )
self.widget_icon = Gtk.StatusIcon()
- self.widget_icon.set_from_icon_name('qubes-devices')
- self.widget_icon.connect('button-press-event', self.show_menu)
+ self.widget_icon.set_from_icon_name("qubes-devices")
+ self.widget_icon.connect("button-press-event", self.show_menu)
self.widget_icon.set_tooltip_markup(
- 'Qubes Devices\nView and manage devices.')
+ "Qubes Devices\nView and manage devices."
+ )
def device_list_update(self, vm, _event, **_kwargs):
@@ -126,7 +141,8 @@ def device_list_update(self, vm, _event, **_kwargs):
for devclass in DEV_TYPES:
for device in vm.devices[devclass]:
changed_devices[str(device.port)] = backend.Device(
- device, self)
+ device, self
+ )
except qubesadmin.exc.QubesException:
changed_devices = {} # VM was removed
@@ -139,7 +155,8 @@ def device_list_update(self, vm, _event, **_kwargs):
_("Device available"),
_("Device {} is available.").format(dev.description),
Gio.NotificationPriority.NORMAL,
- notification_id=dev.notification_id)
+ notification_id=dev.notification_id,
+ )
dev_to_remove = []
for dev_port, dev in self.devices.items():
@@ -153,7 +170,8 @@ def device_list_update(self, vm, _event, **_kwargs):
_("Device removed"),
_("Device {} has been removed.").format(dev.description),
Gio.NotificationPriority.NORMAL,
- notification_id=dev.notification_id)
+ notification_id=dev.notification_id,
+ )
del self.devices[dev_port]
def initialize_vm_data(self):
@@ -175,7 +193,8 @@ def initialize_dev_data(self):
try:
for device in domain.devices[devclass]:
self.devices[str(device.port)] = backend.Device(
- device, self)
+ device, self
+ )
except qubesadmin.exc.QubesException:
# we have no permission to access VM's devices
continue
@@ -184,15 +203,17 @@ def initialize_dev_data(self):
for domain in self.qapp.domains:
for devclass in DEV_TYPES:
try:
- for device in domain.devices[devclass
- ].get_attached_devices():
+ for device in domain.devices[
+ devclass
+ ].get_attached_devices():
dev = str(device.port)
if dev in self.devices:
# occassionally ghost UnknownDevices appear when a
# device was removed but not detached from a VM
# FUTURE: is this still true after api changes?
self.devices[dev].attachments.add(
- backend.VM(domain))
+ backend.VM(domain)
+ )
except qubesadmin.exc.QubesException:
# we have no permission to access VM's devices
continue
@@ -256,6 +277,7 @@ def vm_dispvm_template_change(self, vm, _event, **_kwargs):
self.dispvm_templates.add(wrapped_vm)
else:
self.dispvm_templates.discard(wrapped_vm)
+
#
# def on_label_changed(self, vm, _event, **_kwargs):
# if not vm: # global properties changed
@@ -284,16 +306,18 @@ def load_css(widget) -> str:
because it needs a realized widget.
Returns light/dark variant used currently as 'light' or 'dark' string.
"""
- theme = 'light' if is_theme_light(widget) else 'dark'
+ theme = "light" if is_theme_light(widget) else "dark"
screen = Gdk.Screen.get_default()
provider = Gtk.CssProvider()
- css_file_ref = (importlib.resources.files('qui') /
- f'qubes-devices-{theme}.css')
+ css_file_ref = (
+ importlib.resources.files("qui") / f"qubes-devices-{theme}.css"
+ )
with importlib.resources.as_file(css_file_ref) as css_file:
provider.load_from_path(str(css_file))
Gtk.StyleContext.add_provider_for_screen(
- screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
return theme
@@ -307,23 +331,24 @@ def show_menu(self, _unused, _event):
menu_items = []
sorted_vms = sorted(self.vms)
sorted_dispvms = sorted(self.dispvm_templates)
- sorted_devices = sorted(self.devices.values(),
- key=lambda x: x.sorting_key)
+ sorted_devices = sorted(
+ self.devices.values(), key=lambda x: x.sorting_key
+ )
for i, dev in enumerate(sorted_devices):
if i == 0 or dev.device_group != sorted_devices[i - 1].device_group:
# add a header
- menu_item = \
- actionable_widgets.generate_wrapper_widget(
- Gtk.MenuItem,
- 'activate',
- actionable_widgets.InfoHeader(dev.device_group))
+ menu_item = actionable_widgets.generate_wrapper_widget(
+ Gtk.MenuItem,
+ "activate",
+ actionable_widgets.InfoHeader(dev.device_group),
+ )
menu_items.append(menu_item)
device_widget = actionable_widgets.MainDeviceWidget(dev, theme)
- device_item = \
- actionable_widgets.generate_wrapper_widget(
- Gtk.MenuItem, 'activate', device_widget)
+ device_item = actionable_widgets.generate_wrapper_widget(
+ Gtk.MenuItem, "activate", device_widget
+ )
device_item.set_reserve_indicator(False)
device_menu = DeviceMenu(device_widget, sorted_vms, sorted_dispvms)
@@ -338,15 +363,16 @@ def show_menu(self, _unused, _event):
tray_menu.show_all()
tray_menu.popup_at_pointer(None) # use current event
- def emit_notification(self, title, message, priority, error=False,
- notification_id=None):
+ def emit_notification(
+ self, title, message, priority, error=False, notification_id=None
+ ):
notification = Gio.Notification.new(title)
notification.set_body(message)
notification.set_priority(priority)
if error:
- notification.set_icon(Gio.ThemedIcon.new('dialog-error'))
+ notification.set_icon(Gio.ThemedIcon.new("dialog-error"))
if notification_id:
- notification_id += 'ERROR'
+ notification_id += "ERROR"
self.send_notification(notification_id, notification)
@@ -355,16 +381,17 @@ def main():
# qapp = qubesadmin.tests.mock_app.MockQubesComplete()
dispatcher = qubesadmin.events.EventsDispatcher(qapp)
# dispatcher = qubesadmin.tests.mock_app.MockDispatcher(qapp)
- app = DevicesTray(
- 'org.qubes.qui.tray.Devices', qapp, dispatcher)
+ app = DevicesTray("org.qubes.qui.tray.Devices", qapp, dispatcher)
loop = asyncio.get_event_loop()
return_code = qui.utils.run_asyncio_and_show_errors(
- loop, [asyncio.ensure_future(dispatcher.listen_for_events())],
- "Qubes Devices Widget")
+ loop,
+ [asyncio.ensure_future(dispatcher.listen_for_events())],
+ "Qubes Devices Widget",
+ )
del app
return return_code
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qui/tests/tests_domains.py b/qui/tests/tests_domains.py
index 3e668033..414adad8 100644
--- a/qui/tests/tests_domains.py
+++ b/qui/tests/tests_domains.py
@@ -23,12 +23,13 @@
import qui.tray.domains as domains_widget
from qubesadmin import Qubes
+
class DomainsWidgetTest(unittest.TestCase):
def setUp(self):
super(DomainsWidgetTest, self).setUp()
- self.widget = domains_widget.DomainTray('org.qubes.ui.tray.Domains')
+ self.widget = domains_widget.DomainTray("org.qubes.ui.tray.Domains")
self.widget.initialize_menu()
self.qapp = Qubes()
@@ -45,23 +46,26 @@ def test_01_correct_vm_state(self):
# are all running VMs listed
domains_in_widget = []
for menu_item in self.widget.tray_menu:
- domain = self.qapp.domains[menu_item.vm['name']]
+ domain = self.qapp.domains[menu_item.vm["name"]]
domains_in_widget.append(domain)
- self.assertTrue(domain.is_running(),
- "halted domain listed incorrectly")
+ self.assertTrue(
+ domain.is_running(), "halted domain listed incorrectly"
+ )
for domain in self.qapp.domains:
- if domain.klass != 'AdminVM':
- self.assertEqual(domain in domains_in_widget,
- domain.is_running(),
- "domain missing from list")
+ if domain.klass != "AdminVM":
+ self.assertEqual(
+ domain in domains_in_widget,
+ domain.is_running(),
+ "domain missing from list",
+ )
def test_02_stop_vm(self):
- domain_to_stop = self.qapp.domains['test-running']
+ domain_to_stop = self.qapp.domains["test-running"]
if not domain_to_stop.is_running():
domain_to_stop.start()
- while domain_to_stop.get_power_state() != 'Running':
- time.sleep(1)
+ while domain_to_stop.get_power_state() != "Running":
+ time.sleep(1)
time.sleep(10)
menu_item = self.__find_menu_item(domain_to_stop)
@@ -71,7 +75,7 @@ def test_02_stop_vm(self):
countdown = 100
- while domain_to_stop.get_power_state() != 'Halted' and countdown > 0:
+ while domain_to_stop.get_power_state() != "Halted" and countdown > 0:
time.sleep(1)
countdown -= 1
@@ -81,11 +85,11 @@ def test_02_stop_vm(self):
self.assertIsNone(menu_item, "stopped item still incorrectly listed")
def test_03_start_vm(self):
- domain_to_start = self.qapp.domains['test-halted']
+ domain_to_start = self.qapp.domains["test-halted"]
if domain_to_start.is_running():
domain_to_start.shutdown()
- while domain_to_start.get_power_state() != 'Halted':
+ while domain_to_start.get_power_state() != "Halted":
time.sleep(1)
time.sleep(10)
@@ -99,15 +103,16 @@ def test_03_start_vm(self):
# should finish starting
countdown = 100
while countdown > 0:
- if domain_to_start.get_power_state() == 'Running':
+ if domain_to_start.get_power_state() == "Running":
self.__refresh_gui(45)
item = self.__find_menu_item(domain_to_start)
- self.assertIsNotNone(item,
- "domain not listed as started")
+ self.assertIsNotNone(item, "domain not listed as started")
self.assertIsNotNone(item, "item incorrectly not listed")
- self.assertIsInstance(item.get_submenu(),
- domains_widget.StartedMenu,
- "incorrect menu (debug not start)")
+ self.assertIsInstance(
+ item.get_submenu(),
+ domains_widget.StartedMenu,
+ "incorrect menu (debug not start)",
+ )
break
time.sleep(1)
countdown -= 1
@@ -116,7 +121,7 @@ def test_03_start_vm(self):
def __find_menu_item(self, vm):
for menu_item in self.widget.tray_menu:
- menu_domain = self.qapp.domains[menu_item.vm['name']]
+ menu_domain = self.qapp.domains[menu_item.vm["name"]]
if menu_domain == vm:
return menu_item
return None
@@ -125,7 +130,7 @@ def __find_menu_item(self, vm):
def __refresh_gui(delay=0):
time.sleep(delay)
while Gtk.events_pending():
- Gtk.main_iteration_do(blocking=True)
+ Gtk.main_iteration_do(blocking=True)
if __name__ == "__main__":
diff --git a/qui/tools/qubes_device_agent.py b/qui/tools/qubes_device_agent.py
index 0ef0c330..b2a7c5b8 100644
--- a/qui/tools/qubes_device_agent.py
+++ b/qui/tools/qubes_device_agent.py
@@ -43,7 +43,9 @@
from qrexec.server import SocketService
from qrexec.utils import sanitize_domain_name
from qrexec.tools.qrexec_policy_agent import (
- VMListModeler, RPCConfirmationWindow)
+ VMListModeler,
+ RPCConfirmationWindow,
+)
from qubesadmin.device_protocol import DeviceSerializer
@@ -59,7 +61,7 @@ def _override_entries(self, options):
self._entries = {}
for name, vm in self._domains_info.items():
if name.startswith("@dispvm:"):
- vm_name = name[len("@dispvm:"):]
+ vm_name = name[len("@dispvm:") :]
prefix = "Disposable VM: "
else:
vm_name = name
@@ -79,14 +81,16 @@ def _override_entries(self, options):
def apply_icon(self, entry, qube_name):
if isinstance(entry, Gtk.Entry):
for vm_info in self._entries.values():
- if qube_name == vm_info['api_name']:
+ if qube_name == vm_info["api_name"]:
entry.set_icon_from_pixbuf(
- Gtk.EntryIconPosition.PRIMARY, vm_info["icon"],
+ Gtk.EntryIconPosition.PRIMARY,
+ vm_info["icon"],
)
break
else:
raise ValueError(
- f"The following source qube does not exist: {qube_name}")
+ f"The following source qube does not exist: {qube_name}"
+ )
else:
raise TypeError(
"Only expecting Gtk.Entry objects to want our icon."
@@ -96,7 +100,8 @@ def apply_icon(self, entry, qube_name):
class AttachmentConfirmationWindow(RPCConfirmationWindow):
# pylint: disable=too-few-public-methods,too-many-instance-attributes
_source_file_ref = importlib.resources.files("qui").joinpath(
- os.path.join("devices", "AttachConfirmationWindow.glade"))
+ os.path.join("devices", "AttachConfirmationWindow.glade")
+ )
_source_id = {
"window": "AttachConfirmationWindow",
@@ -114,13 +119,20 @@ class AttachmentConfirmationWindow(RPCConfirmationWindow):
# pylint: disable=super-init-not-called
def __init__(
self,
- entries_info, source, device_name, argument, targets_list, target=None
+ entries_info,
+ source,
+ device_name,
+ argument,
+ targets_list,
+ target=None,
):
# pylint: disable=too-many-arguments
sanitize_domain_name(source, assert_sanitized=True)
DeviceSerializer.sanitize_str(
- device_name, DeviceSerializer.ALLOWED_CHARS_PARAM,
- error_message="Invalid device name")
+ device_name,
+ DeviceSerializer.ALLOWED_CHARS_PARAM,
+ error_message="Invalid device name",
+ )
self._gtk_builder = Gtk.Builder()
with importlib.resources.as_file(self._source_file_ref) as path:
@@ -157,8 +169,11 @@ def __init__(
self._entries_info = entries_info
- options = {name: " " + options for vm_data in targets_list
- for name, _, options in (vm_data.partition(" "),)}
+ options = {
+ name: " " + options
+ for vm_data in targets_list
+ for name, _, options in (vm_data.partition(" "),)
+ }
list_modeler = self._new_vm_list_modeler_overridden(options)
list_modeler.apply_model(
diff --git a/qui/tray/disk_space.py b/qui/tray/disk_space.py
index 8d8c8094..6e61e0f3 100644
--- a/qui/tray/disk_space.py
+++ b/qui/tray/disk_space.py
@@ -4,7 +4,8 @@
from typing import List
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, GObject, Gio, GLib # isort:skip
from qubesadmin import Qubes
from qubesadmin.utils import size_to_human
@@ -12,6 +13,7 @@
from qubesadmin.storage import Pool
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -29,9 +31,9 @@ def __init__(self, vm):
def check_usage(self):
self.problem_volumes = {}
- volumes_to_check = ['private']
- if not hasattr(self.vm, 'template'):
- volumes_to_check.append('root')
+ volumes_to_check = ["private"]
+ if not hasattr(self.vm, "template"):
+ volumes_to_check.append("root")
for volume_name in volumes_to_check:
try:
if volume_name in self.vm.volumes:
@@ -57,8 +59,7 @@ def __populate_vms(self):
usage_data = VMUsage(vm)
if usage_data.problem_volumes:
self.problematic_vms.append(usage_data)
- except (exc.QubesPropertyAccessError,
- exc.QubesVMNotFoundError) :
+ except (exc.QubesPropertyAccessError, exc.QubesVMNotFoundError):
continue
def get_vms_widgets(self):
@@ -71,9 +72,9 @@ def __create_widgets(vm_usage):
# icon widget
try:
- icon = getattr(vm, 'icon', vm.label.icon)
+ icon = getattr(vm, "icon", vm.label.icon)
except exc.QubesPropertyAccessError:
- icon = 'appvm-black'
+ icon = "appvm-black"
icon_vm = Gtk.IconTheme.get_default().load_icon(icon, 16, 0)
icon_img = Gtk.Image.new_from_pixbuf(icon_vm)
@@ -84,8 +85,9 @@ def __create_widgets(vm_usage):
for volume_name, usage in vm_usage.problem_volumes.items():
# pylint: disable=consider-using-f-string
- label_contents.append(_('volume {} is {:.1%} full').format(
- volume_name, usage))
+ label_contents.append(
+ _("volume {} is {:.1%} full").format(volume_name, usage)
+ )
label_text = f"{vm.name}: " + ", ".join(label_contents)
label_widget.set_markup(label_text)
@@ -98,15 +100,15 @@ def __init__(self, vm):
super().__init__()
self.vm = vm
- self.set_label(_('Open Qube Settings'))
+ self.set_label(_("Open Qube Settings"))
- self.connect('activate', launch_preferences_dialog, self.vm.name)
+ self.connect("activate", launch_preferences_dialog, self.vm.name)
def launch_preferences_dialog(_, vm):
- vm = str(vm).strip('\'')
+ vm = str(vm).strip("'")
# pylint: disable=consider-using-with
- subprocess.Popen(['qubes-vm-settings', vm])
+ subprocess.Popen(["qubes-vm-settings", vm])
class NeverNotifyItem(Gtk.CheckMenuItem):
@@ -114,23 +116,24 @@ def __init__(self, vm):
super().__init__()
self.vm = vm
- self.set_label(_('Do not show notifications about this qube'))
+ self.set_label(_("Do not show notifications about this qube"))
try:
self.set_active(
- self.vm.features.get('disk-space-not-notify', False))
+ self.vm.features.get("disk-space-not-notify", False)
+ )
except exc.QubesDaemonCommunicationError:
self.set_active(False)
self.set_sensitive(False)
- self.connect('toggled', self.toggle_state)
+ self.connect("toggled", self.toggle_state)
def toggle_state(self, _item):
try:
if self.get_active():
- self.vm.features['disk-space-not-notify'] = 1
+ self.vm.features["disk-space-not-notify"] = 1
else:
- del self.vm.features['disk-space-not-notify']
+ del self.vm.features["disk-space-not-notify"]
except exc.QubesDaemonAccessError:
self.set_sensitive(False)
@@ -148,17 +151,18 @@ def __init__(self, vm):
class PoolWrapper:
"""Wrapper for pools, to manage various exceptions thrown by properties."""
+
def __init__(self, pool: Pool):
self.pool = pool
self.has_error = False
- self.size = self._get_attribute('size', None)
- self.usage = self._get_attribute('usage', None)
- self.config = self._get_attribute('config', {})
- self.usage_details = self._get_attribute('usage_details', {})
- self.name = self._get_attribute('name', 'ERROR: pool name unknown')
+ self.size = self._get_attribute("size", None)
+ self.usage = self._get_attribute("usage", None)
+ self.config = self._get_attribute("config", {})
+ self.usage_details = self._get_attribute("usage_details", {})
+ self.name = self._get_attribute("name", "ERROR: pool name unknown")
- self.metadata_size = self.usage_details.get('metadata_size', None)
- self.metadata_usage = self.usage_details.get('metadata_usage', None)
+ self.metadata_size = self.usage_details.get("metadata_size", None)
+ self.metadata_usage = self.usage_details.get("metadata_usage", None)
if self.size and self.usage:
self.usage_perc = self.usage / self.size
@@ -201,8 +205,11 @@ def __populate_pools(self):
if wrapped_pool.has_error:
continue
- if not wrapped_pool.size or not wrapped_pool.usage or \
- 'included_in' in wrapped_pool.config:
+ if (
+ not wrapped_pool.size
+ or not wrapped_pool.usage
+ or "included_in" in wrapped_pool.config
+ ):
continue
self.total_size += wrapped_pool.size
self.used_size += wrapped_pool.usage
@@ -210,14 +217,18 @@ def __populate_pools(self):
if wrapped_pool.usage_perc >= URGENT_WARN_LEVEL:
self.warning_message.append(
_("\n{:.1%} space left in pool {}").format(
- 1-wrapped_pool.usage_perc, wrapped_pool.name))
+ 1 - wrapped_pool.usage_perc, wrapped_pool.name
+ )
+ )
if wrapped_pool.metadata_perc >= URGENT_WARN_LEVEL:
# pylint: disable=consider-using-f-string
- self.warning_message.append(_(
- "\nMetadata space for pool {} is running out. "
- "Current usage: {:.0%}").format(
- wrapped_pool.name, wrapped_pool.metadata_perc))
+ self.warning_message.append(
+ _(
+ "\nMetadata space for pool {} is running out. "
+ "Current usage: {:.0%}"
+ ).format(wrapped_pool.name, wrapped_pool.metadata_perc)
+ )
def get_pools_widgets(self):
for p in self.pools:
@@ -228,7 +239,7 @@ def get_warning(self):
def get_usage(self):
if self.total_size > 0:
- return self.used_size/self.total_size
+ return self.used_size / self.total_size
return 0
@staticmethod
@@ -241,21 +252,20 @@ def __create_box(pool: PoolWrapper):
if pool.has_error:
# Pool with errors
- formatted_name = \
- f'{pool.name}'
- elif pool.size and 'included_in' not in pool.config:
+ formatted_name = f"{pool.name}"
+ elif pool.size and "included_in" not in pool.config:
# normal pool
- formatted_name = f'{pool.name}'
+ formatted_name = f"{pool.name}"
else:
# pool without data or included in another pool
- formatted_name = f'{pool.name}'
+ formatted_name = f"{pool.name}"
pool_name.set_markup(formatted_name)
pool_name.set_margin_left(20)
name_box.pack_start(pool_name, True, True, 0)
- if not pool.size or 'included_in' in pool.config:
+ if not pool.size or "included_in" in pool.config:
return name_box, percentage_box, usage_box
if pool.has_error:
@@ -288,15 +298,15 @@ def __create_box(pool: PoolWrapper):
if pool.metadata_perc:
metadata_label = Gtk.Label()
- metadata_label.set_markup(colored_percentage(
- pool.metadata_perc))
+ metadata_label.set_markup(colored_percentage(pool.metadata_perc))
percentage_box.pack_start(metadata_label, True, True, 0)
numeric_label = Gtk.Label()
numeric_label.set_markup(
- ''
- f'{size_to_human(pool.usage or 0)}/'
- f'{size_to_human(pool.size or 0)}')
+ ""
+ f"{size_to_human(pool.usage or 0)}/"
+ f"{size_to_human(pool.size or 0)}"
+ )
numeric_label.set_justify(Gtk.Justification.RIGHT)
# pack with empty labels to guarantee proper alignment
@@ -309,14 +319,14 @@ def __create_box(pool: PoolWrapper):
def colored_percentage(value):
if value < WARN_LEVEL:
- color = 'green'
+ color = "green"
elif value < URGENT_WARN_LEVEL:
- color = 'orange'
+ color = "orange"
else:
- color = 'red'
+ color = "red"
# pylint: disable=consider-using-f-string
- result = '{:.1%}'.format(color, value)
+ result = "{:.1%}".format(color, value)
return result
@@ -325,11 +335,12 @@ def emit_notification(gtk_app, title, text, vm=None):
notification = Gio.Notification.new(title)
notification.set_priority(Gio.NotificationPriority.HIGH)
notification.set_body(text)
- notification.set_icon(Gio.ThemedIcon.new('dialog-warning'))
+ notification.set_icon(Gio.ThemedIcon.new("dialog-warning"))
if vm:
- notification.add_button(_('Open qube settings'),
- f"app.prefs::{vm.name}")
+ notification.add_button(
+ _("Open qube settings"), f"app.prefs::{vm.name}"
+ )
gtk_app.send_notification(None, notification)
@@ -353,7 +364,7 @@ def __init__(self, **properties):
self.add_action(prefs_action)
self.icon = Gtk.StatusIcon()
- self.icon.connect('button-press-event', self.make_menu)
+ self.icon.connect("button-press-event", self.make_menu)
self.refresh_icon()
GObject.timeout_add_seconds(120, self.refresh_icon)
@@ -367,8 +378,7 @@ def refresh_icon(self):
vm_warning = vm_data.problematic_vms
# set icon
- self.set_icon_state(pool_warning=pool_warning,
- vm_warning=vm_warning)
+ self.set_icon_state(pool_warning=pool_warning, vm_warning=vm_warning)
# emit notification
if pool_warning:
@@ -376,8 +386,9 @@ def refresh_icon(self):
emit_notification(
self,
_("Disk usage warning!"),
- _("You are running out of disk space.") + ''.join(
- pool_warning))
+ _("You are running out of disk space.")
+ + "".join(pool_warning),
+ )
self.pool_warned = True
else:
self.pool_warned = False
@@ -385,17 +396,22 @@ def refresh_icon(self):
if vm_warning:
currently_problematic_vms = [x.vm for x in vm_warning]
- self.vms_warned = {vm for vm in self.vms_warned if vm in
- currently_problematic_vms}
+ self.vms_warned = {
+ vm for vm in self.vms_warned if vm in currently_problematic_vms
+ }
for vm in currently_problematic_vms:
- if not vm.features.get('disk-space-not-notify', False) and vm\
- not in self.vms_warned:
+ if (
+ not vm.features.get("disk-space-not-notify", False)
+ and vm not in self.vms_warned
+ ):
emit_notification(
self,
_("Qube usage warning"),
_("Qube {} is running out of storage space.").format(
- vm.name),
- vm=vm)
+ vm.name
+ ),
+ vm=vm,
+ )
self.vms_warned.add(vm)
else:
self.vms_warned = set()
@@ -407,16 +423,19 @@ def set_icon_state(self, pool_warning=None, vm_warning=None):
self.icon.set_from_icon_name("dialog-warning")
text = _("Qubes Disk Space Monitor\n\nWARNING!")
if pool_warning:
- text += _('\nYou are running out of disk space.\n') + \
- ''.join(pool_warning)
+ text += _("\nYou are running out of disk space.\n") + "".join(
+ pool_warning
+ )
if vm_warning:
- text += _('\nThe following qubes are running out of space: ') \
- + ', '.join([x.vm.name for x in vm_warning])
+ text += _(
+ "\nThe following qubes are running out of space: "
+ ) + ", ".join([x.vm.name for x in vm_warning])
self.icon.set_tooltip_markup(text)
else:
self.icon.set_from_icon_name("drive-harddisk")
self.icon.set_tooltip_markup(
- _('Qubes Disk Space Monitor\nView free disk space.'))
+ _("Qubes Disk Space Monitor\nView free disk space.")
+ )
def make_menu(self, _unused, _event):
pool_data = PoolUsageData(self.qubes_app)
@@ -426,11 +445,11 @@ def make_menu(self, _unused, _event):
menu.append(self.make_top_box(pool_data))
- menu.append(self.make_title_item('Volumes'))
+ menu.append(self.make_title_item("Volumes"))
grid = Gtk.Grid()
col_no = 0
- for (label1, label2, label3) in pool_data.get_pools_widgets():
+ for label1, label2, label3 in pool_data.get_pools_widgets():
grid.attach(label1, 0, col_no, 1, 1)
grid.attach(label2, 1, col_no, 1, 1)
grid.attach(label3, 2, col_no, 1, 1)
@@ -443,9 +462,9 @@ def make_menu(self, _unused, _event):
menu.append(grid_menu_item)
if vm_data.problematic_vms:
- menu.append(self.make_title_item(_('Qubes warnings')))
+ menu.append(self.make_title_item(_("Qubes warnings")))
- for (vm, label1, label2) in vm_data.get_vms_widgets():
+ for vm, label1, label2 in vm_data.get_vms_widgets():
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
hbox.pack_start(label1, False, False, 0)
hbox.pack_start(label2, False, False, 5)
@@ -485,7 +504,7 @@ def make_top_box(pool_data):
progress_bar = Gtk.LevelBar()
progress_bar.set_min_value(0)
progress_bar.set_max_value(100)
- progress_bar.set_value(pool_data.get_usage()*100)
+ progress_bar.set_value(pool_data.get_usage() * 100)
progress_bar.set_vexpand(True)
progress_bar.set_hexpand(True)
progress_bar.set_margin_left(20)
@@ -509,5 +528,5 @@ def main():
app.run()
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qui/tray/domains.py b/qui/tray/domains.py
index 1eae1310..8e5fa3d3 100644
--- a/qui/tray/domains.py
+++ b/qui/tray/domains.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# pylint: disable=wrong-import-position,import-error,superfluous-parens
-''' A menu listing domains '''
+""" A menu listing domains """
import abc
import asyncio
import subprocess
@@ -18,39 +18,42 @@
from qubesadmin import exc
import gi # isort:skip
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gio, Gtk, GObject, GLib, GdkPixbuf # isort:skip
import gbulb
+
gbulb.install()
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
STATE_DICTIONARY = {
- 'domain-pre-start': 'Transient',
- 'domain-start': 'Running',
- 'domain-start-failed': 'Halted',
- 'domain-paused': 'Paused',
- 'domain-unpaused': 'Running',
- 'domain-shutdown': 'Halted',
- 'domain-pre-shutdown': 'Transient',
- 'domain-shutdown-failed': 'Running'
+ "domain-pre-start": "Transient",
+ "domain-start": "Running",
+ "domain-start-failed": "Halted",
+ "domain-paused": "Paused",
+ "domain-unpaused": "Running",
+ "domain-shutdown": "Halted",
+ "domain-pre-shutdown": "Transient",
+ "domain-shutdown-failed": "Running",
}
class IconCache:
def __init__(self):
self.icon_files = {
- 'pause': 'media-playback-pause',
- 'terminal': 'utilities-terminal',
- 'preferences': 'preferences-system',
- 'kill': 'media-record',
- 'shutdown': 'media-playback-stop',
- 'unpause': 'media-playback-start',
- 'files': 'system-file-manager',
- 'restart': 'edit-redo'
+ "pause": "media-playback-pause",
+ "terminal": "utilities-terminal",
+ "preferences": "preferences-system",
+ "kill": "media-record",
+ "shutdown": "media-playback-stop",
+ "unpause": "media-playback-start",
+ "files": "system-file-manager",
+ "restart": "edit-redo",
}
self.icons = {}
@@ -60,11 +63,13 @@ def get_icon(self, icon_name):
else:
try:
icon = Gtk.IconTheme.get_default().load_icon(
- self.icon_files[icon_name], 16, 0)
+ self.icon_files[icon_name], 16, 0
+ )
self.icons[icon_name] = icon
except (TypeError, GLib.Error):
- icon = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB,
- True, 8, 16, 16)
+ icon = GdkPixbuf.Pixbuf.new(
+ GdkPixbuf.Colorspace.RGB, True, 8, 16, 16
+ )
icon.fill(0)
self.icons[icon_name] = icon
return icon
@@ -72,7 +77,8 @@ def get_icon(self, icon_name):
def show_error(title, text):
dialog = Gtk.MessageDialog(
- None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK)
+ None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK
+ )
dialog.set_title(title)
dialog.set_markup(text)
dialog.connect("response", lambda *x: dialog.destroy())
@@ -89,8 +95,7 @@ def __init__(self, vm, icon_cache, icon_name, label):
self.set_image(img)
self.set_label(label)
- self.connect(
- 'activate', self.instantiate_thread_with_function)
+ self.connect("activate", self.instantiate_thread_with_function)
@abc.abstractmethod
def perform_action(self):
@@ -106,66 +111,77 @@ def instantiate_thread_with_function(self, *_args, **_kwargs):
class PauseItem(VMActionMenuItem):
- ''' Shutdown menu Item. When activated pauses the domain. '''
+ """Shutdown menu Item. When activated pauses the domain."""
def __init__(self, vm, icon_cache):
- super().__init__(vm, icon_cache, 'pause', _('Emergency pause'))
+ super().__init__(vm, icon_cache, "pause", _("Emergency pause"))
def perform_action(self):
try:
self.vm.pause()
except exc.QubesException as ex:
- show_error(_("Error pausing qube"),
- _("The following error occurred while "
- "attempting to pause qube {0}:\n{1}").format(
- self.vm.name, str(ex)))
+ show_error(
+ _("Error pausing qube"),
+ _(
+ "The following error occurred while "
+ "attempting to pause qube {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class UnpauseItem(VMActionMenuItem):
- ''' Unpause menu Item. When activated unpauses the domain. '''
+ """Unpause menu Item. When activated unpauses the domain."""
+
def __init__(self, vm, icon_cache):
- super().__init__(vm, icon_cache, 'unpause', _('Unpause'))
+ super().__init__(vm, icon_cache, "unpause", _("Unpause"))
def perform_action(self):
try:
self.vm.unpause()
except exc.QubesException as ex:
- show_error(_("Error unpausing qube"),
- _("The following error occurred while attempting "
- "to unpause qube {0}:\n{1}").format(
- self.vm.name, str(ex)))
+ show_error(
+ _("Error unpausing qube"),
+ _(
+ "The following error occurred while attempting "
+ "to unpause qube {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class ShutdownItem(VMActionMenuItem):
- ''' Shutdown menu Item. When activated shutdowns the domain. '''
+ """Shutdown menu Item. When activated shutdowns the domain."""
+
def __init__(self, vm, icon_cache):
- super().__init__(vm, icon_cache, 'shutdown', _('Shutdown'))
+ super().__init__(vm, icon_cache, "shutdown", _("Shutdown"))
def perform_action(self):
try:
self.vm.shutdown()
except exc.QubesException as ex:
- show_error(_("Error shutting down qube"),
- _("The following error occurred while attempting to "
- "shut down qube {0}:\n{1}").format(
- self.vm.name, str(ex)))
+ show_error(
+ _("Error shutting down qube"),
+ _(
+ "The following error occurred while attempting to "
+ "shut down qube {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class RestartItem(Gtk.ImageMenuItem):
- ''' Restart menu Item. When activated shutdowns the domain and
- then starts it again. '''
+ """Restart menu Item. When activated shutdowns the domain and
+ then starts it again."""
def __init__(self, vm, icon_cache):
super().__init__()
self.vm = vm
- img = Gtk.Image.new_from_pixbuf(icon_cache.get_icon('restart'))
+ img = Gtk.Image.new_from_pixbuf(icon_cache.get_icon("restart"))
self.set_image(img)
- self.set_label(_('Restart'))
+ self.set_label(_("Restart"))
self.restart_thread = None
- self.connect('activate', self.restart)
+ self.connect("activate", self.restart)
def restart(self, *_args, **_kwargs):
asyncio.ensure_future(self.perform_restart())
@@ -176,39 +192,49 @@ async def perform_restart(self):
while self.vm.is_running():
await asyncio.sleep(1)
proc = await asyncio.create_subprocess_exec(
- 'qvm-start', self.vm.name, stderr=subprocess.PIPE)
+ "qvm-start", self.vm.name, stderr=subprocess.PIPE
+ )
_stdout, stderr = await proc.communicate()
if proc.returncode != 0:
raise exc.QubesException(stderr)
except exc.QubesException as ex:
- show_error(_("Error restarting qube"),
- _("The following error occurred while attempting to "
- "restart qube {0}:\n{1}").format(
- self.vm.name, str(ex)))
+ show_error(
+ _("Error restarting qube"),
+ _(
+ "The following error occurred while attempting to "
+ "restart qube {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class KillItem(VMActionMenuItem):
- ''' Kill domain menu Item. When activated kills the domain. '''
+ """Kill domain menu Item. When activated kills the domain."""
+
def __init__(self, vm, icon_cache):
- super().__init__(vm, icon_cache, 'kill', _('Kill'))
+ super().__init__(vm, icon_cache, "kill", _("Kill"))
def perform_action(self, *_args, **_kwargs):
try:
self.vm.kill()
except exc.QubesException as ex:
- show_error(_("Error shutting down qube"),
- _("The following error occurred while attempting to shut"
- "down qube {0}:\n{1}").format(self.vm.name, str(ex)))
+ show_error(
+ _("Error shutting down qube"),
+ _(
+ "The following error occurred while attempting to shut"
+ "down qube {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class PreferencesItem(VMActionMenuItem):
- ''' Preferences menu Item. When activated shows preferences dialog '''
+ """Preferences menu Item. When activated shows preferences dialog"""
+
def __init__(self, vm, icon_cache):
- super().__init__(vm, icon_cache, 'preferences', _('Settings'))
+ super().__init__(vm, icon_cache, "preferences", _("Settings"))
def perform_action(self):
# pylint: disable=consider-using-with
- subprocess.Popen(['qubes-vm-settings', self.vm.name])
+ subprocess.Popen(["qubes-vm-settings", self.vm.name])
class LogItem(Gtk.ImageMenuItem):
@@ -217,38 +243,44 @@ def __init__(self, name, path):
self.path = path
img = Gtk.Image.new_from_file(
- "/usr/share/icons/HighContrast/16x16/apps/logviewer.png")
+ "/usr/share/icons/HighContrast/16x16/apps/logviewer.png"
+ )
self.set_image(img)
self.set_label(name)
- self.connect('activate', self.launch_log_viewer)
+ self.connect("activate", self.launch_log_viewer)
def launch_log_viewer(self, *_args, **_kwargs):
# pylint: disable=consider-using-with
- subprocess.Popen(['qubes-log-viewer', self.path])
+ subprocess.Popen(["qubes-log-viewer", self.path])
class RunTerminalItem(Gtk.ImageMenuItem):
- ''' Run Terminal menu Item. When activated runs a terminal emulator. '''
+ """Run Terminal menu Item. When activated runs a terminal emulator."""
+
def __init__(self, vm, icon_cache):
super().__init__()
self.vm = vm
- img = Gtk.Image.new_from_pixbuf(icon_cache.get_icon('terminal'))
+ img = Gtk.Image.new_from_pixbuf(icon_cache.get_icon("terminal"))
self.set_image(img)
- self.set_label(_('Run Terminal'))
+ self.set_label(_("Run Terminal"))
- self.connect('activate', self.run_terminal)
+ self.connect("activate", self.run_terminal)
def run_terminal(self, _item):
try:
- self.vm.run_service('qubes.StartApp+qubes-run-terminal')
+ self.vm.run_service("qubes.StartApp+qubes-run-terminal")
except exc.QubesException as ex:
- show_error(_("Error starting terminal"),
- _("The following error occurred while attempting to "
- "run terminal {0}:\n{1}").format(self.vm.name, str(ex)))
+ show_error(
+ _("Error starting terminal"),
+ _(
+ "The following error occurred while attempting to "
+ "run terminal {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class OpenFileManagerItem(Gtk.ImageMenuItem):
@@ -259,43 +291,45 @@ def __init__(self, vm, icon_cache):
super().__init__()
self.vm = vm
- img = Gtk.Image.new_from_pixbuf(
- icon_cache.get_icon('files'))
+ img = Gtk.Image.new_from_pixbuf(icon_cache.get_icon("files"))
self.set_image(img)
- self.set_label(_('Open File Manager'))
+ self.set_label(_("Open File Manager"))
- self.connect('activate', self.open_file_manager)
+ self.connect("activate", self.open_file_manager)
def open_file_manager(self, _item):
try:
- self.vm.run_service('qubes.StartApp+qubes-open-file-manager')
+ self.vm.run_service("qubes.StartApp+qubes-open-file-manager")
except exc.QubesException as ex:
- show_error(_("Error opening file manager"),
- _("The following error occurred while attempting to "
- "open file manager {0}:\n{1}").format(
- self.vm.name, str(ex)))
+ show_error(
+ _("Error opening file manager"),
+ _(
+ "The following error occurred while attempting to "
+ "open file manager {0}:\n{1}"
+ ).format(self.vm.name, str(ex)),
+ )
class InternalInfoItem(Gtk.MenuItem):
- ''' Restart menu Item. When activated shutdowns the domain and
- then starts it again. '''
+ """Restart menu Item. When activated shutdowns the domain and
+ then starts it again."""
def __init__(self):
super().__init__()
self.label = Gtk.Label(xalign=0)
- self.label.set_markup(_(
- 'Internal qube'))
+ self.label.set_markup(_("Internal qube"))
self.set_tooltip_text(
- 'Internal qubes are used by the operating system. Do not modify'
- ' them or run programs in them unless you really '
- 'know what you are doing.')
+ "Internal qubes are used by the operating system. Do not modify"
+ " them or run programs in them unless you really "
+ "know what you are doing."
+ )
self.add(self.label)
self.set_sensitive(False)
class StartedMenu(Gtk.Menu):
- ''' The sub-menu for a started domain'''
+ """The sub-menu for a started domain"""
def __init__(self, vm, app, icon_cache):
super().__init__()
@@ -307,14 +341,14 @@ def __init__(self, vm, app, icon_cache):
self.add(PreferencesItem(self.vm, icon_cache))
self.add(PauseItem(self.vm, icon_cache))
self.add(ShutdownItem(self.vm, icon_cache))
- if self.vm.klass != 'DispVM' or not self.vm.auto_cleanup:
+ if self.vm.klass != "DispVM" or not self.vm.auto_cleanup:
self.add(RestartItem(self.vm, icon_cache))
self.show_all()
class PausedMenu(Gtk.Menu):
- ''' The sub-menu for a paused domain'''
+ """The sub-menu for a paused domain"""
def __init__(self, vm, icon_cache):
super().__init__()
@@ -328,7 +362,7 @@ def __init__(self, vm, icon_cache):
class DebugMenu(Gtk.Menu):
- ''' Sub-menu providing multiple MenuItem for domain logs. '''
+ """Sub-menu providing multiple MenuItem for domain logs."""
def __init__(self, vm, icon_cache):
super().__init__()
@@ -337,11 +371,15 @@ def __init__(self, vm, icon_cache):
self.add(PreferencesItem(self.vm, icon_cache))
logs = [
- (_("Console Log"),
- "/var/log/xen/console/guest-" + vm.name + ".log"),
- (_("QEMU Console Log"),
- "/var/log/xen/console/guest-" + vm.name + "-dm.log"),
- ]
+ (
+ _("Console Log"),
+ "/var/log/xen/console/guest-" + vm.name + ".log",
+ ),
+ (
+ _("QEMU Console Log"),
+ "/var/log/xen/console/guest-" + vm.name + "-dm.log",
+ ),
+ ]
for name, path in logs:
if os.path.isfile(path):
@@ -354,6 +392,7 @@ def __init__(self, vm, icon_cache):
class InternalMenu(Gtk.Menu):
"""Sub-menu for Internal qubes"""
+
def __init__(self, vm, icon_cache, working_correctly=True):
"""
:param vm: relevant Internal qube
@@ -367,11 +406,15 @@ def __init__(self, vm, icon_cache, working_correctly=True):
self.add(InternalInfoItem())
logs = [
- (_("Console Log"),
- "/var/log/xen/console/guest-" + vm.name + ".log"),
- (_("QEMU Console Log"),
- "/var/log/xen/console/guest-" + vm.name + "-dm.log"),
- ]
+ (
+ _("Console Log"),
+ "/var/log/xen/console/guest-" + vm.name + ".log",
+ ),
+ (
+ _("QEMU Console Log"),
+ "/var/log/xen/console/guest-" + vm.name + "-dm.log",
+ ),
+ ]
for name, path in logs:
if os.path.isfile(path):
@@ -387,19 +430,20 @@ def __init__(self, vm, icon_cache, working_correctly=True):
def run_manager(_item):
# pylint: disable=consider-using-with
- subprocess.Popen(['qubes-qube-manager'])
+ subprocess.Popen(["qubes-qube-manager"])
class QubesManagerItem(Gtk.ImageMenuItem):
def __init__(self):
super().__init__()
- self.set_image(Gtk.Image.new_from_icon_name('qubes-logo-icon',
- Gtk.IconSize.MENU))
+ self.set_image(
+ Gtk.Image.new_from_icon_name("qubes-logo-icon", Gtk.IconSize.MENU)
+ )
- self.set_label(_('Open Qube Manager'))
+ self.set_label(_("Open Qube Manager"))
- self.connect('activate', run_manager)
+ self.connect("activate", run_manager)
self.show_all()
@@ -443,7 +487,7 @@ def __init__(self, vm, app, icon_cache, state=None):
self.cpu.update_state(header=True)
self.memory.update_state(header=True)
self.show_all() # header should always be visible
- elif self.vm.klass == 'AdminVM': # no submenu for AdminVM
+ elif self.vm.klass == "AdminVM": # no submenu for AdminVM
self.set_reserve_indicator(True) # align with submenu triangles
else:
if not state:
@@ -456,12 +500,13 @@ def set_label_icon(self):
self.set_image(self.decorator.icon())
def _set_submenu(self, state):
- if self.vm.features.get('internal', False):
- submenu = InternalMenu(self.vm, self.icon_cache,
- working_correctly=(state == 'Running'))
- elif state == 'Running':
+ if self.vm.features.get("internal", False):
+ submenu = InternalMenu(
+ self.vm, self.icon_cache, working_correctly=(state == "Running")
+ )
+ elif state == "Running":
submenu = StartedMenu(self.vm, self.app, self.icon_cache)
- elif state == 'Paused':
+ elif state == "Paused":
submenu = PausedMenu(self.vm, self.icon_cache)
else:
submenu = DebugMenu(self.vm, self.icon_cache)
@@ -485,9 +530,9 @@ def hide_spinner(self):
self.spinner.hide()
def update_state(self, state):
- vm_klass = getattr(self.vm, 'klass', None)
+ vm_klass = getattr(self.vm, "klass", None)
- if not self.vm or vm_klass == 'AdminVM':
+ if not self.vm or vm_klass == "AdminVM":
# it's a header or an AdminVM, no need to do anything
return
@@ -498,19 +543,20 @@ def update_state(self, state):
return
# if VM is not running, hide it
- if state == 'Halted':
+ if state == "Halted":
self.hide()
return
self.show_all()
- if state in ['Running', 'Paused']:
+ if state in ["Running", "Paused"]:
self.hide_spinner()
else:
self.show_spinner()
- colormap = {'Paused': 'grey', 'Crashed': 'red', 'Transient': 'red'}
+ colormap = {"Paused": "grey", "Crashed": "red", "Transient": "red"}
if state in colormap:
self.name.label.set_markup(
- f'{self.vm.name}')
+ f"{self.vm.name}"
+ )
else:
self.name.label.set_label(self.vm.name)
@@ -522,7 +568,7 @@ def update_stats(self, memory_kb, cpu_usage):
class DomainTray(Gtk.Application):
- ''' A tray icon application listing all but halted domains. ” '''
+ """A tray icon application listing all but halted domains. ”"""
def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
super().__init__()
@@ -531,10 +577,11 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
self.stats_dispatcher = stats_dispatcher
self.widget_icon: Gtk.StatusIcon = Gtk.StatusIcon()
- self.widget_icon.set_from_icon_name('qui-domains-scalable')
- self.widget_icon.connect('button-press-event', self.show_menu)
+ self.widget_icon.set_from_icon_name("qui-domains-scalable")
+ self.widget_icon.connect("button-press-event", self.show_menu)
self.widget_icon.set_tooltip_markup(
- _('Qubes Domains\nView and manage running domains.'))
+ _("Qubes Domains\nView and manage running domains.")
+ )
self.tray_menu = Gtk.Menu()
@@ -542,8 +589,8 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
self.menu_items = {}
- self.unpause_all_action = Gio.SimpleAction.new('do-unpause-all', None)
- self.unpause_all_action.connect('activate', self.do_unpause_all)
+ self.unpause_all_action = Gio.SimpleAction.new("do-unpause-all", None)
+ self.unpause_all_action.connect("activate", self.do_unpause_all)
self.add_action(self.unpause_all_action)
self.pause_notification_out = False
@@ -555,99 +602,115 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
self.register() # register Gtk Application
def register_events(self):
- self.dispatcher.add_handler('connection-established', self.refresh_all)
- self.dispatcher.add_handler('domain-pre-start', self.update_domain_item)
- self.dispatcher.add_handler('domain-start', self.update_domain_item)
- self.dispatcher.add_handler('domain-start-failed',
- self.update_domain_item)
- self.dispatcher.add_handler('domain-paused', self.update_domain_item)
- self.dispatcher.add_handler('domain-unpaused', self.update_domain_item)
- self.dispatcher.add_handler('domain-shutdown', self.update_domain_item)
- self.dispatcher.add_handler('domain-pre-shutdown',
- self.update_domain_item)
- self.dispatcher.add_handler('domain-shutdown-failed',
- self.update_domain_item)
-
- self.dispatcher.add_handler('domain-add', self.add_domain_item)
- self.dispatcher.add_handler('domain-delete', self.remove_domain_item)
-
- self.dispatcher.add_handler('domain-pre-start', self.emit_notification)
- self.dispatcher.add_handler('domain-start', self.emit_notification)
- self.dispatcher.add_handler('domain-start-failed',
- self.emit_notification)
- self.dispatcher.add_handler('domain-pre-shutdown',
- self.emit_notification)
- self.dispatcher.add_handler('domain-shutdown', self.emit_notification)
- self.dispatcher.add_handler('domain-shutdown-failed',
- self.emit_notification)
-
- self.dispatcher.add_handler('domain-start', self.check_pause_notify)
- self.dispatcher.add_handler('domain-paused', self.check_pause_notify)
- self.dispatcher.add_handler('domain-unpaused', self.check_pause_notify)
- self.dispatcher.add_handler('domain-shutdown', self.check_pause_notify)
-
- self.dispatcher.add_handler('domain-feature-set:updates-available',
- self.feature_change)
- self.dispatcher.add_handler('domain-feature-delete:updates-available',
- self.feature_change)
- self.dispatcher.add_handler('property-set:netvm', self.property_change)
- self.dispatcher.add_handler('property-set:label', self.property_change)
-
- self.stats_dispatcher.add_handler('vm-stats', self.update_stats)
+ self.dispatcher.add_handler("connection-established", self.refresh_all)
+ self.dispatcher.add_handler("domain-pre-start", self.update_domain_item)
+ self.dispatcher.add_handler("domain-start", self.update_domain_item)
+ self.dispatcher.add_handler(
+ "domain-start-failed", self.update_domain_item
+ )
+ self.dispatcher.add_handler("domain-paused", self.update_domain_item)
+ self.dispatcher.add_handler("domain-unpaused", self.update_domain_item)
+ self.dispatcher.add_handler("domain-shutdown", self.update_domain_item)
+ self.dispatcher.add_handler(
+ "domain-pre-shutdown", self.update_domain_item
+ )
+ self.dispatcher.add_handler(
+ "domain-shutdown-failed", self.update_domain_item
+ )
+
+ self.dispatcher.add_handler("domain-add", self.add_domain_item)
+ self.dispatcher.add_handler("domain-delete", self.remove_domain_item)
+
+ self.dispatcher.add_handler("domain-pre-start", self.emit_notification)
+ self.dispatcher.add_handler("domain-start", self.emit_notification)
+ self.dispatcher.add_handler(
+ "domain-start-failed", self.emit_notification
+ )
+ self.dispatcher.add_handler(
+ "domain-pre-shutdown", self.emit_notification
+ )
+ self.dispatcher.add_handler("domain-shutdown", self.emit_notification)
+ self.dispatcher.add_handler(
+ "domain-shutdown-failed", self.emit_notification
+ )
+
+ self.dispatcher.add_handler("domain-start", self.check_pause_notify)
+ self.dispatcher.add_handler("domain-paused", self.check_pause_notify)
+ self.dispatcher.add_handler("domain-unpaused", self.check_pause_notify)
+ self.dispatcher.add_handler("domain-shutdown", self.check_pause_notify)
+
+ self.dispatcher.add_handler(
+ "domain-feature-set:updates-available", self.feature_change
+ )
+ self.dispatcher.add_handler(
+ "domain-feature-delete:updates-available", self.feature_change
+ )
+ self.dispatcher.add_handler("property-set:netvm", self.property_change)
+ self.dispatcher.add_handler("property-set:label", self.property_change)
+
+ self.stats_dispatcher.add_handler("vm-stats", self.update_stats)
def show_menu(self, _unused, event):
self.tray_menu.popup_at_pointer(event) # None means current event
def emit_notification(self, vm, event, **kwargs):
- notification = Gio.Notification.new(_(
- "Qube Status: {}"). format(vm.name))
+ notification = Gio.Notification.new(
+ _("Qube Status: {}").format(vm.name)
+ )
notification.set_priority(Gio.NotificationPriority.NORMAL)
- if event == 'domain-start-failed':
- notification.set_body(_('Qube {} has failed to start: {}').format(
- vm.name, kwargs['reason']))
+ if event == "domain-start-failed":
+ notification.set_body(
+ _("Qube {} has failed to start: {}").format(
+ vm.name, kwargs["reason"]
+ )
+ )
notification.set_priority(Gio.NotificationPriority.HIGH)
- notification.set_icon(
- Gio.ThemedIcon.new('dialog-warning'))
- elif event == 'domain-pre-start':
- notification.set_body(_('Qube {} is starting.').format(vm.name))
- elif event == 'domain-start':
- notification.set_body(_('Qube {} has started.').format(vm.name))
- elif event == 'domain-pre-shutdown':
+ notification.set_icon(Gio.ThemedIcon.new("dialog-warning"))
+ elif event == "domain-pre-start":
+ notification.set_body(_("Qube {} is starting.").format(vm.name))
+ elif event == "domain-start":
+ notification.set_body(_("Qube {} has started.").format(vm.name))
+ elif event == "domain-pre-shutdown":
notification.set_body(
- _('Qube {} is attempting to shut down.').format(vm.name))
- elif event == 'domain-shutdown':
- notification.set_body(_('Qube {} has shut down.').format(vm.name))
- elif event == 'domain-shutdown-failed':
+ _("Qube {} is attempting to shut down.").format(vm.name)
+ )
+ elif event == "domain-shutdown":
+ notification.set_body(_("Qube {} has shut down.").format(vm.name))
+ elif event == "domain-shutdown-failed":
notification.set_body(
- _('Qube {} failed to shut down: {}').format(
- vm.name, kwargs['reason']))
+ _("Qube {} failed to shut down: {}").format(
+ vm.name, kwargs["reason"]
+ )
+ )
notification.set_priority(Gio.NotificationPriority.HIGH)
- notification.set_icon(
- Gio.ThemedIcon.new('dialog-warning'))
+ notification.set_icon(Gio.ThemedIcon.new("dialog-warning"))
else:
return
self.send_notification(None, notification)
def emit_paused_notification(self):
if not self.pause_notification_out:
- notification = Gio.Notification.new(_("Your qubes have been "
- "paused!"))
- notification.set_body(_(
- "All your qubes are currently paused. If this was an accident, "
- "simply click \"Unpause All\" to unpause them. Otherwise, "
- "you can unpause individual qubes via the Qubes Domains "
- "tray widget."))
- notification.set_icon(
- Gio.ThemedIcon.new('dialog-warning'))
- notification.add_button(_('Unpause All'), 'app.do-unpause-all')
+ notification = Gio.Notification.new(
+ _("Your qubes have been paused!")
+ )
+ notification.set_body(
+ _(
+ "All your qubes are currently paused. If this was an "
+ "accident, simply click 'Unpause All' to unpause them. "
+ "Otherwise, you can unpause individual qubes via the "
+ "Qubes Domains tray widget."
+ )
+ )
+ notification.set_icon(Gio.ThemedIcon.new("dialog-warning"))
+ notification.add_button(_("Unpause All"), "app.do-unpause-all")
notification.set_priority(Gio.NotificationPriority.HIGH)
- self.send_notification('vms-paused', notification)
+ self.send_notification("vms-paused", notification)
self.pause_notification_out = True
def withdraw_paused_notification(self):
if self.pause_notification_out:
- self.withdraw_notification('vms-paused')
+ self.withdraw_notification("vms-paused")
self.pause_notification_out = False
def do_unpause_all(self, _vm, *_args, **_kwargs):
@@ -667,7 +730,7 @@ def check_pause_notify(self, _vm, _event, **_kwargs):
def have_running_and_all_are_paused(self):
found_paused = False
for vm in self.qapp.domains:
- if vm.klass != 'AdminVM':
+ if vm.klass != "AdminVM":
if vm.is_running():
if vm.is_paused():
# a running that is paused
@@ -679,9 +742,9 @@ def have_running_and_all_are_paused(self):
def add_domain_item(self, _submitter, event, vm, **_kwargs):
"""Add a DomainMenuItem to menu; if event is None, this was fired
- manually (mot due to domain-add event, and it is assumed the menu items
- are created in alphabetical order. Otherwise, this method will
- attempt to sort menu items correctly."""
+ manually (mot due to domain-add event, and it is assumed the menu items
+ are created in alphabetical order. Otherwise, this method will
+ attempt to sort menu items correctly."""
# check if it already exists
try:
vm = self.qapp.domains[str(vm)]
@@ -701,7 +764,7 @@ def add_domain_item(self, _submitter, event, vm, **_kwargs):
if vm not in self.qapp.domains:
return
# or we might not have permission to access its power state
- state = 'Halted'
+ state = "Halted"
domain_item = DomainMenuItem(vm, self, self.icon_cache, state=state)
if not event: # menu item creation at widget start; we can assume
@@ -710,12 +773,12 @@ def add_domain_item(self, _submitter, event, vm, **_kwargs):
else:
position = 0
for i in self.tray_menu: # pylint: disable=not-an-iterable
- if not hasattr(i, 'vm'): # we reached the end
+ if not hasattr(i, "vm"): # we reached the end
break
if not i.vm: # header should be skipper
position += 1
continue
- if i.vm.klass == 'AdminVM':
+ if i.vm.klass == "AdminVM":
# AdminVM(s) should be skipped
position += 1
continue
@@ -729,9 +792,9 @@ def add_domain_item(self, _submitter, event, vm, **_kwargs):
def property_change(self, vm, event, *_args, **_kwargs):
if vm not in self.menu_items:
return
- if event == 'property-set:netvm':
+ if event == "property-set:netvm":
self.menu_items[vm].name.update_tooltip(netvm_changed=True)
- elif event == 'property-set:label':
+ elif event == "property-set:label":
self.menu_items[vm].set_label_icon()
def feature_change(self, vm, *_args, **_kwargs):
@@ -756,7 +819,7 @@ def remove_domain_item(self, _submitter, _event, vm, **_kwargs):
def handle_domain_shutdown(self, vm):
try:
- if getattr(vm, 'klass', None) == 'TemplateVM':
+ if getattr(vm, "klass", None) == "TemplateVM":
for menu_item in self.menu_items.values():
try:
if not menu_item.vm.is_running():
@@ -765,9 +828,10 @@ def handle_domain_shutdown(self, vm):
continue
except exc.QubesPropertyAccessError:
continue
- if getattr(menu_item.vm, 'template', None) == vm and \
- any(vol.is_outdated()
- for vol in menu_item.vm.volumes.values()):
+ if getattr(menu_item.vm, "template", None) == vm and any(
+ vol.is_outdated()
+ for vol in menu_item.vm.volumes.values()
+ ):
menu_item.name.update_outdated(True)
except exc.QubesVMNotFoundError:
# attribute not available anymore as VM was removed
@@ -775,8 +839,8 @@ def handle_domain_shutdown(self, vm):
pass
def update_domain_item(self, vm, event, **kwargs):
- ''' Update the menu item with the started menu for
- the specified vm in the tray'''
+ """Update the menu item with the started menu for
+ the specified vm in the tray"""
try:
item = self.menu_items[vm]
except exc.QubesPropertyAccessError:
@@ -796,41 +860,44 @@ def update_domain_item(self, vm, event, **kwargs):
else:
try:
state = vm.get_power_state()
- except Exception: # pylint: disable=broad-except
+ except Exception: # pylint: disable=broad-except
# it's a fragile DispVM
state = "Transient"
item.update_state(state)
- if event == 'domain-shutdown':
+ if event == "domain-shutdown":
self.handle_domain_shutdown(vm)
# if the VM was shut down, it is no longer outdated
item.name.update_outdated(False)
- if event in ('domain-start', 'domain-pre-start'):
+ if event in ("domain-start", "domain-pre-start"):
# A newly started VM should not be outdated.
item.name.update_outdated(False)
item.show_all()
- if event == 'domain-shutdown':
+ if event == "domain-shutdown":
item.hide()
def update_stats(self, vm, _event, **kwargs):
if vm not in self.menu_items:
return
self.menu_items[vm].update_stats(
- kwargs['memory_kb'], kwargs['cpu_usage'])
+ kwargs["memory_kb"], kwargs["cpu_usage"]
+ )
def initialize_menu(self):
self.tray_menu.add(DomainMenuItem(None, self, self.icon_cache))
# Add AdminVMS
- for vm in sorted([vm for vm in self.qapp.domains
- if vm.klass == "AdminVM"]):
+ for vm in sorted(
+ [vm for vm in self.qapp.domains if vm.klass == "AdminVM"]
+ ):
self.add_domain_item(None, None, vm)
# and the rest of them
- for vm in sorted([vm for vm in self.qapp.domains
- if vm.klass != 'AdminVM']):
+ for vm in sorted(
+ [vm for vm in self.qapp.domains if vm.klass != "AdminVM"]
+ ):
self.add_domain_item(None, None, vm)
for item in self.menu_items.values():
@@ -846,7 +913,7 @@ def initialize_menu(self):
self.tray_menu.add(Gtk.SeparatorMenuItem())
self.tray_menu.add(QubesManagerItem())
- self.connect('shutdown', self._disconnect_signals)
+ self.connect("shutdown", self._disconnect_signals)
def refresh_all(self, _subject, _event, **_kwargs):
items_to_delete = []
@@ -856,71 +923,91 @@ def refresh_all(self, _subject, _event, **_kwargs):
for vm in items_to_delete:
self.remove_domain_item(None, None, vm)
for vm in self.qapp.domains:
- self.update_domain_item(vm, '')
+ self.update_domain_item(vm, "")
def run(self): # pylint: disable=arguments-differ
self.initialize_menu()
def _disconnect_signals(self, _event):
- self.dispatcher.remove_handler('connection-established',
- self.refresh_all)
- self.dispatcher.remove_handler('domain-pre-start',
- self.update_domain_item)
- self.dispatcher.remove_handler('domain-start', self.update_domain_item)
- self.dispatcher.remove_handler('domain-start-failed',
- self.update_domain_item)
- self.dispatcher.remove_handler('domain-paused', self.update_domain_item)
- self.dispatcher.remove_handler('domain-unpaused',
- self.update_domain_item)
- self.dispatcher.remove_handler('domain-shutdown',
- self.update_domain_item)
- self.dispatcher.remove_handler('domain-pre-shutdown',
- self.update_domain_item)
- self.dispatcher.remove_handler('domain-shutdown-failed',
- self.update_domain_item)
-
- self.dispatcher.remove_handler('domain-add', self.add_domain_item)
- self.dispatcher.remove_handler('domain-delete', self.remove_domain_item)
-
- self.dispatcher.remove_handler('domain-pre-start',
- self.emit_notification)
- self.dispatcher.remove_handler('domain-start', self.emit_notification)
- self.dispatcher.remove_handler('domain-start-failed',
- self.emit_notification)
- self.dispatcher.remove_handler('domain-pre-shutdown',
- self.emit_notification)
- self.dispatcher.remove_handler('domain-shutdown',
- self.emit_notification)
- self.dispatcher.remove_handler('domain-shutdown-failed',
- self.emit_notification)
-
- self.dispatcher.remove_handler('domain-start', self.check_pause_notify)
- self.dispatcher.remove_handler('domain-paused', self.check_pause_notify)
- self.dispatcher.remove_handler('domain-unpaused',
- self.check_pause_notify)
- self.dispatcher.remove_handler('domain-shutdown',
- self.check_pause_notify)
-
- self.dispatcher.remove_handler('domain-feature-set:updates-available',
- self.feature_change)
self.dispatcher.remove_handler(
- 'domain-feature-delete:updates-available', self.feature_change)
- self.dispatcher.remove_handler('property-set:netvm',
- self.property_change)
- self.dispatcher.remove_handler('property-set:label',
- self.property_change)
+ "connection-established", self.refresh_all
+ )
+ self.dispatcher.remove_handler(
+ "domain-pre-start", self.update_domain_item
+ )
+ self.dispatcher.remove_handler("domain-start", self.update_domain_item)
+ self.dispatcher.remove_handler(
+ "domain-start-failed", self.update_domain_item
+ )
+ self.dispatcher.remove_handler("domain-paused", self.update_domain_item)
+ self.dispatcher.remove_handler(
+ "domain-unpaused", self.update_domain_item
+ )
+ self.dispatcher.remove_handler(
+ "domain-shutdown", self.update_domain_item
+ )
+ self.dispatcher.remove_handler(
+ "domain-pre-shutdown", self.update_domain_item
+ )
+ self.dispatcher.remove_handler(
+ "domain-shutdown-failed", self.update_domain_item
+ )
+
+ self.dispatcher.remove_handler("domain-add", self.add_domain_item)
+ self.dispatcher.remove_handler("domain-delete", self.remove_domain_item)
+
+ self.dispatcher.remove_handler(
+ "domain-pre-start", self.emit_notification
+ )
+ self.dispatcher.remove_handler("domain-start", self.emit_notification)
+ self.dispatcher.remove_handler(
+ "domain-start-failed", self.emit_notification
+ )
+ self.dispatcher.remove_handler(
+ "domain-pre-shutdown", self.emit_notification
+ )
+ self.dispatcher.remove_handler(
+ "domain-shutdown", self.emit_notification
+ )
+ self.dispatcher.remove_handler(
+ "domain-shutdown-failed", self.emit_notification
+ )
+
+ self.dispatcher.remove_handler("domain-start", self.check_pause_notify)
+ self.dispatcher.remove_handler("domain-paused", self.check_pause_notify)
+ self.dispatcher.remove_handler(
+ "domain-unpaused", self.check_pause_notify
+ )
+ self.dispatcher.remove_handler(
+ "domain-shutdown", self.check_pause_notify
+ )
+
+ self.dispatcher.remove_handler(
+ "domain-feature-set:updates-available", self.feature_change
+ )
+ self.dispatcher.remove_handler(
+ "domain-feature-delete:updates-available", self.feature_change
+ )
+ self.dispatcher.remove_handler(
+ "property-set:netvm", self.property_change
+ )
+ self.dispatcher.remove_handler(
+ "property-set:label", self.property_change
+ )
- self.stats_dispatcher.remove_handler('vm-stats', self.update_stats)
+ self.stats_dispatcher.remove_handler("vm-stats", self.update_stats)
def main():
- ''' main function '''
+ """main function"""
qapp = qubesadmin.Qubes()
dispatcher = qubesadmin.events.EventsDispatcher(qapp)
stats_dispatcher = qubesadmin.events.EventsDispatcher(
- qapp, api_method='admin.vm.Stats')
+ qapp, api_method="admin.vm.Stats"
+ )
app = DomainTray(
- 'org.qubes.qui.tray.Domains', qapp, dispatcher, stats_dispatcher)
+ "org.qubes.qui.tray.Domains", qapp, dispatcher, stats_dispatcher
+ )
app.run()
loop = asyncio.get_event_loop()
@@ -929,9 +1016,10 @@ def main():
asyncio.ensure_future(stats_dispatcher.listen_for_events()),
]
- return qui.utils.run_asyncio_and_show_errors(loop, tasks,
- "Qubes Domains Widget")
+ return qui.utils.run_asyncio_and_show_errors(
+ loop, tasks, "Qubes Domains Widget"
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qui/tray/updates.py b/qui/tray/updates.py
index 8533dea3..37ce643f 100644
--- a/qui/tray/updates.py
+++ b/qui/tray/updates.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# pylint: disable=wrong-import-position,import-error
-''' A widget that monitors update availability and notifies the user
- about new updates to templates and standalone VMs'''
+""" A widget that monitors update availability and notifies the user
+ about new updates to templates and standalone VMs"""
import asyncio
import sys
import subprocess
@@ -13,13 +13,16 @@
from qubesadmin import exc
import gi # isort:skip
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Gio # isort:skip
import gbulb
+
gbulb.install()
import gettext
+
t = gettext.translation("desktop-linux-manager", fallback=True)
_ = t.gettext
@@ -51,7 +54,7 @@ def __init__(self, text, command):
self.set_margin_bottom(5)
self.add(title_label)
self.show_all()
- self.connect('activate', command)
+ self.connect("activate", command)
class UpdatesTray(Gtk.Application):
@@ -66,11 +69,12 @@ def __init__(self, app_name, qapp, dispatcher):
self.register() # register Gtk Application
self.widget_icon = Gtk.StatusIcon()
- self.widget_icon.set_from_icon_name('software-update-available')
+ self.widget_icon.set_from_icon_name("software-update-available")
self.widget_icon.set_visible(False)
- self.widget_icon.connect('button-press-event', self.show_menu)
- self.widget_icon.set_tooltip_markup(_(
- 'Qubes Update\nUpdates are available.'))
+ self.widget_icon.connect("button-press-event", self.show_menu)
+ self.widget_icon.set_tooltip_markup(
+ _("Qubes Update\nUpdates are available.")
+ )
self.vms_needing_update = set()
self.obsolete_vms = set()
@@ -88,20 +92,31 @@ def setup_menu(self):
if self.vms_needing_update:
self.tray_menu.append(TextItem(_("Qube updates available!")))
- self.tray_menu.append(RunItem(
- _("Updates for {} qubes are available!\n"
- "Launch updater").format(
- len(self.vms_needing_update)), self.launch_updater))
+ self.tray_menu.append(
+ RunItem(
+ _(
+ "Updates for {} qubes are available!\n"
+ "Launch updater"
+ ).format(len(self.vms_needing_update)),
+ self.launch_updater,
+ )
+ )
if self.obsolete_vms:
- self.tray_menu.append(TextItem(
- _("Some qubes are no longer supported!")))
- obsolete_text = _("The following qubes are based on distributions "
- "that are no longer supported:\n")\
- + ", ".join([str(vm) for vm in self.obsolete_vms])\
+ self.tray_menu.append(
+ TextItem(_("Some qubes are no longer supported!"))
+ )
+ obsolete_text = (
+ _(
+ "The following qubes are based on distributions "
+ "that are no longer supported:\n"
+ )
+ + ", ".join([str(vm) for vm in self.obsolete_vms])
+ _("\nInstall new templates with Template Manager")
+ )
self.tray_menu.append(
- RunItem(obsolete_text, self.launch_template_manager))
+ RunItem(obsolete_text, self.launch_template_manager)
+ )
self.tray_menu.show_all()
@@ -115,12 +130,12 @@ def show_menu(self, _unused, _event):
@staticmethod
def launch_updater(*_args, **_kwargs):
# pylint: disable=consider-using-with
- subprocess.Popen(['qubes-update-gui'])
+ subprocess.Popen(["qubes-update-gui"])
@staticmethod
def launch_template_manager(*_args, **_kwargs):
# pylint: disable=consider-using-with
- subprocess.Popen(['qvm-template-gui'])
+ subprocess.Popen(["qvm-template-gui"])
def check_vms_needing_update(self):
self.vms_needing_update.clear()
@@ -134,18 +149,23 @@ def check_vms_needing_update(self):
self.obsolete_vms.add(vm.name)
def connect_events(self):
- self.dispatcher.add_handler('domain-feature-set:updates-available',
- self.feature_change)
- self.dispatcher.add_handler('domain-feature-delete:updates-available',
- self.feature_change)
- self.dispatcher.add_handler('domain-feature-set:skip-update',
- self.feature_change)
- self.dispatcher.add_handler('domain-feature-delete:skip-update',
- self.feature_change)
- self.dispatcher.add_handler('domain-add', self.domain_added)
- self.dispatcher.add_handler('domain-delete', self.domain_removed)
- self.dispatcher.add_handler('domain-feature-set:os-eol',
- self.feature_change)
+ self.dispatcher.add_handler(
+ "domain-feature-set:updates-available", self.feature_change
+ )
+ self.dispatcher.add_handler(
+ "domain-feature-delete:updates-available", self.feature_change
+ )
+ self.dispatcher.add_handler(
+ "domain-feature-set:skip-update", self.feature_change
+ )
+ self.dispatcher.add_handler(
+ "domain-feature-delete:skip-update", self.feature_change
+ )
+ self.dispatcher.add_handler("domain-add", self.domain_added)
+ self.dispatcher.add_handler("domain-delete", self.domain_removed)
+ self.dispatcher.add_handler(
+ "domain-feature-set:os-eol", self.feature_change
+ )
def domain_added(self, _submitter, _event, vm, *_args, **_kwargs):
try:
@@ -180,7 +200,8 @@ def feature_change(self, vm, event, feature, **_kwargs):
if not updated and vm not in self.vms_needing_update:
self.vms_needing_update.add(vm)
notification = Gio.Notification.new(
- _("New updates are available for {}.").format(vm.name))
+ _("New updates are available for {}.").format(vm.name)
+ )
notification.set_priority(Gio.NotificationPriority.NORMAL)
self.send_notification(None, notification)
elif updated and vm in self.vms_needing_update:
@@ -203,15 +224,17 @@ def update_indicator_state(self):
def main():
qapp = qubesadmin.Qubes()
dispatcher = qubesadmin.events.EventsDispatcher(qapp)
- app = UpdatesTray(
- 'org.qubes.qui.tray.Updates', qapp, dispatcher)
+ app = UpdatesTray("org.qubes.qui.tray.Updates", qapp, dispatcher)
app.run()
loop = asyncio.get_event_loop()
- return qui.utils.run_asyncio_and_show_errors(loop, [asyncio.ensure_future(
- dispatcher.listen_for_events())], "Qubes Update Widget")
+ return qui.utils.run_asyncio_and_show_errors(
+ loop,
+ [asyncio.ensure_future(dispatcher.listen_for_events())],
+ "Qubes Update Widget",
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main())
diff --git a/qui/updater/intro_page.py b/qui/updater/intro_page.py
index 3e6dcb02..7b5395ca 100644
--- a/qui/updater/intro_page.py
+++ b/qui/updater/intro_page.py
@@ -26,17 +26,24 @@
from datetime import datetime, timedelta
from typing import Optional
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk # isort:skip
from qubes_config.widgets.gtk_utils import load_icon
from qubesadmin import exc
from qui.utils import check_support
-from qui.updater.utils import disable_checkboxes, HeaderCheckbox, \
- pass_through_event_window, \
- QubeName, label_color_theme, UpdateStatus, RowWrapper, \
- ListWrapper, on_head_checkbox_toggled
+from qui.updater.utils import (
+ disable_checkboxes,
+ HeaderCheckbox,
+ pass_through_event_window,
+ QubeName,
+ label_color_theme,
+ UpdateStatus,
+ RowWrapper,
+ ListWrapper,
+ on_head_checkbox_toggled,
+)
class IntroPage:
@@ -59,14 +66,16 @@ def __init__(self, builder, log, next_button):
self.list_store: Optional[ListWrapper] = None
checkbox_column: Gtk.TreeViewColumn = self.builder.get_object(
- "checkbox_column")
+ "checkbox_column"
+ )
checkbox_column.connect("clicked", self.on_header_toggled)
header_button = checkbox_column.get_button()
- header_button.connect('realize', pass_through_event_window)
+ header_button.connect("realize", pass_through_event_window)
self.checkbox_column_button: Gtk.CheckButton = self.builder.get_object(
- "checkbox_header")
+ "checkbox_header"
+ )
self.checkbox_column_button.set_inconsistent(True)
self.checkbox_column_button.connect("toggled", self.on_header_toggled)
self.head_checkbox = UpdateHeaderCheckbox(
@@ -76,49 +85,59 @@ def __init__(self, builder, log, next_button):
self.vm_list.connect("row-activated", self.on_checkbox_toggled)
self.info_how_it_works: Gtk.Label = self.builder.get_object(
- "info_how_it_works")
+ "info_how_it_works"
+ )
self.info_how_it_works.set_label(
self.info_how_it_works.get_label().format(
MAYBE=f''
- 'MAYBE',
+ "MAYBE",
OBSOLETE=f''
- 'OBSOLETE'
- ))
+ "OBSOLETE",
+ )
+ )
def populate_vm_list(self, qapp, settings):
"""Adds to list any updatable vms with update info."""
self.log.debug("Populate update list")
self.list_store = ListWrapper(
- UpdateRowWrapper, self.vm_list.get_model())
+ UpdateRowWrapper, self.vm_list.get_model()
+ )
for vm in qapp.domains:
- if vm.klass == 'AdminVM':
+ if vm.klass == "AdminVM":
try:
- if settings.hide_skipped and bool(vm.features.get( \
- 'skip-update', False)):
+ if settings.hide_skipped and bool(
+ vm.features.get("skip-update", False)
+ ):
continue
- state = bool(vm.features.get('updates-available', False))
+ state = bool(vm.features.get("updates-available", False))
except exc.QubesDaemonCommunicationError:
state = False
self.list_store.append_vm(vm, state)
- to_update=set()
+ to_update = set()
if settings.hide_updated:
- cmd = ['qubes-vm-update', '--quiet', '--dry-run',
- '--update-if-stale', str(settings.update_if_stale)]
+ cmd = [
+ "qubes-vm-update",
+ "--quiet",
+ "--dry-run",
+ "--update-if-stale",
+ str(settings.update_if_stale),
+ ]
to_update = self._get_stale_qubes(cmd)
for vm in qapp.domains:
try:
- if settings.hide_skipped and bool(vm.features.get( \
- 'skip-update', False)):
+ if settings.hide_skipped and bool(
+ vm.features.get("skip-update", False)
+ ):
continue
if settings.hide_updated and not vm.name in to_update:
# TODO: Make re-filtering possible without App restart
continue
except exc.QubesDaemonCommunicationError:
continue
- if getattr(vm, 'updateable', False) and vm.klass != 'AdminVM':
+ if getattr(vm, "updateable", False) and vm.klass != "AdminVM":
self.list_store.append_vm(vm)
self.refresh_update_list(settings.update_if_stale)
@@ -131,13 +150,18 @@ def refresh_update_list(self, update_if_stale):
if not self.active:
return
- cmd = ['qubes-vm-update', '--quiet', '--dry-run',
- '--update-if-stale', str(update_if_stale)]
+ cmd = [
+ "qubes-vm-update",
+ "--quiet",
+ "--dry-run",
+ "--update-if-stale",
+ str(update_if_stale),
+ ]
to_update = self._get_stale_qubes(cmd)
for row in self.list_store:
- if row.vm.name == 'dom0':
+ if row.vm.name == "dom0":
continue
row.updates_available = bool(row.vm.name in to_update)
row.selected = bool(row.vm.name in to_update)
@@ -186,22 +210,29 @@ def on_header_toggled(self, _emitter):
the cycle will start from (1).
"""
on_head_checkbox_toggled(
- self.list_store, self.head_checkbox, self.select_rows)
+ self.list_store, self.head_checkbox, self.select_rows
+ )
def select_rows(self):
for row in self.list_store:
- row.selected = row.updates_available \
- in self.head_checkbox.allowed
+ row.selected = row.updates_available in self.head_checkbox.allowed
def select_rows_ignoring_conditions(self, cliargs, dom0):
- cmd = ['qubes-vm-update', '--dry-run', '--quiet']
+ cmd = ["qubes-vm-update", "--dry-run", "--quiet"]
args = [a for a in dir(cliargs) if not a.startswith("_")]
for arg in args:
- if arg in ("non_interactive", "non_default_select",
- "dom0",
- "restart", "apply_to_sys", "apply_to_all", "no_apply",
- "max_concurrency", "log"):
+ if arg in (
+ "non_interactive",
+ "non_default_select",
+ "dom0",
+ "restart",
+ "apply_to_sys",
+ "apply_to_all",
+ "no_apply",
+ "max_concurrency",
+ "log",
+ ):
continue
value = getattr(cliargs, arg)
if value:
@@ -217,7 +248,8 @@ def select_rows_ignoring_conditions(self, cliargs, dom0):
to_update = set()
non_default_select = [
- '--' + arg for arg in cliargs.non_default_select if arg != 'dom0']
+ "--" + arg for arg in cliargs.non_default_select if arg != "dom0"
+ ]
non_default = [a for a in cmd if a in non_default_select]
if non_default or cliargs.non_interactive:
to_update = self._get_stale_qubes(cmd)
@@ -238,8 +270,10 @@ def _get_stale_qubes(self, cmd):
return set()
return {
- vm_name.strip() for vm_name
- in output_lines[0].split(":", maxsplit=1)[1].split(",")
+ vm_name.strip()
+ for vm_name in output_lines[0]
+ .split(":", maxsplit=1)[1]
+ .split(",")
}
except subprocess.CalledProcessError as err:
if err.returncode != 100:
@@ -249,18 +283,23 @@ def _get_stale_qubes(self, cmd):
@staticmethod
def _handle_cli_dom0(dom0, to_update, cliargs):
default_select = not any(
- (getattr(cliargs, arg)
- for arg in cliargs.non_default_select if arg != 'all'))
- if ((
- default_select and cliargs.non_interactive
+ (
+ getattr(cliargs, arg)
+ for arg in cliargs.non_default_select
+ if arg != "all"
+ )
+ )
+ if (
+ default_select
+ and cliargs.non_interactive
or cliargs.all
or cliargs.dom0
) and (
cliargs.force_update
- or bool(dom0.features.get('updates-available', False))
+ or bool(dom0.features.get("updates-available", False))
or is_stale(dom0, cliargs.update_if_stale)
- )):
- to_update.add('dom0')
+ ):
+ to_update.add("dom0")
if cliargs.targets and "dom0" in cliargs.targets.split(","):
to_update.add("dom0")
@@ -274,8 +313,8 @@ def is_stale(vm, expiration_period):
return False
today = datetime.today()
last_update_str = vm.features.get(
- 'last-updates-check',
- datetime.fromtimestamp(0).strftime('%Y-%m-%d %H:%M:%S')
+ "last-updates-check",
+ datetime.fromtimestamp(0).strftime("%Y-%m-%d %H:%M:%S"),
)
last_update = datetime.fromisoformat(last_update_str)
if (today - last_update).days > expiration_period:
@@ -295,14 +334,14 @@ class UpdateRowWrapper(RowWrapper):
_STATUS = 8
def __init__(self, list_store, vm, to_update: bool):
- updates_available = bool(vm.features.get('updates-available', False))
+ updates_available = bool(vm.features.get("updates-available", False))
if to_update and not updates_available:
updates_available = None
selected = updates_available is True
supported = check_support(vm)
- last_updates_check = vm.features.get('last-updates-check', None)
- last_update = vm.features.get('last-update', None)
+ last_updates_check = vm.features.get("last-updates-check", None)
+ last_update = vm.features.get("last-update", None)
icon = load_icon(vm.icon)
name = QubeName(vm.name, str(vm.label))
@@ -352,13 +391,15 @@ def updates_available(self):
@updates_available.setter
def updates_available(self, value):
updates_available = bool(
- self.vm.features.get('updates-available', False))
+ self.vm.features.get("updates-available", False)
+ )
supported = check_support(self.vm)
if value and not updates_available:
updates_available = None
- self.raw_row[self._UPDATES_AVAILABLE] = \
- UpdatesAvailable.from_features(updates_available, supported)
+ self.raw_row[self._UPDATES_AVAILABLE] = UpdatesAvailable.from_features(
+ updates_available, supported
+ )
@property
def last_updates_check(self):
@@ -384,10 +425,10 @@ def set_update_progress(self, progress):
class UpdateHeaderCheckbox(HeaderCheckbox):
def __init__(self, checkbox_column_button, next_button):
- super().__init__(checkbox_column_button,
- [UpdatesAvailable.YES,
- UpdatesAvailable.MAYBE,
- UpdatesAvailable.NO])
+ super().__init__(
+ checkbox_column_button,
+ [UpdatesAvailable.YES, UpdatesAvailable.MAYBE, UpdatesAvailable.NO],
+ )
self.next_button = next_button
def all_action(self, *args, **kwargs):
@@ -458,8 +499,9 @@ class UpdatesAvailable(Enum):
EOL = 3
@staticmethod
- def from_features(updates_available: Optional[bool],
- supported: Optional[str]=None) -> "UpdatesAvailable":
+ def from_features(
+ updates_available: Optional[bool], supported: Optional[str] = None
+ ) -> "UpdatesAvailable":
if not supported:
return UpdatesAvailable.EOL
if updates_available:
@@ -477,7 +519,7 @@ def color(self):
if self is UpdatesAvailable.NO:
return label_color_theme("black")
if self is UpdatesAvailable.EOL:
- return label_color_theme('red')
+ return label_color_theme("red")
def __str__(self):
if self is UpdatesAvailable.YES:
@@ -490,8 +532,7 @@ def __str__(self):
name = "OBSOLETE"
else:
name = "ERROR"
- return f'' \
- + name + ''
+ return f'' + name + ""
def __eq__(self, other: "UpdatesAvailable"):
return self.value == other.value
diff --git a/qui/updater/progress_page.py b/qui/updater/progress_page.py
index ac69e2b1..05f3696c 100644
--- a/qui/updater/progress_page.py
+++ b/qui/updater/progress_page.py
@@ -26,12 +26,14 @@
import gi
from typing import Dict, List
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Gdk, GLib, GObject # isort:skip
from locale import gettext as l
-from qubes_config.widgets.gtk_utils import copy_to_global_clipboard, \
- load_icon_at_gtk_size
+from qubes_config.widgets.gtk_utils import (
+ copy_to_global_clipboard,
+ load_icon_at_gtk_size,
+)
from qui.updater.updater_settings import Settings
from qui.updater.utils import UpdateStatus, RowWrapper
@@ -39,13 +41,7 @@
class ProgressPage:
def __init__(
- self,
- builder,
- log,
- header_label,
- next_button,
- cancel_button,
- callback
+ self, builder, log, header_label, next_button, cancel_button, callback
):
self.builder = builder
self.log = log
@@ -66,16 +62,19 @@ def __init__(
progress_store = self.progressbar.get_model()
progress_store.append([0])
self.total_progress = progress_store[-1]
- self.progressbar_renderer: Gtk.CellRendererProgress = \
+ self.progressbar_renderer: Gtk.CellRendererProgress = (
self.builder.get_object("progressbar_renderer")
+ )
self.progressbar_renderer.set_fixed_size(-1, 26)
self.progress_list: Gtk.TreeView = self.builder.get_object(
- "progress_list")
+ "progress_list"
+ )
self.selection: Gtk.TreeSelection = self.progress_list.get_selection()
self.progress_list.connect("row-activated", self.row_selected)
progress_column: Gtk.TreeViewColumn = self.builder.get_object(
- "progress_column")
+ "progress_column"
+ )
renderer = CellRendererProgressWithResult()
renderer.props.ypad = 10
progress_column.pack_start(renderer, True)
@@ -102,8 +101,7 @@ def init_update(self, vms_to_update, settings):
self.header_label.set_halign(Gtk.Align.CENTER)
self.update_thread = threading.Thread(
- target=self.perform_update,
- args=(settings,)
+ target=self.perform_update, args=(settings,)
)
self.update_thread.start()
@@ -113,15 +111,18 @@ def interrupt_update(self):
"""
self.log.debug("Interrupting updates")
self.exit_triggered = True
- GLib.idle_add(self.header_label.set_text,
- l("Interrupting the update..."))
+ GLib.idle_add(
+ self.header_label.set_text, l("Interrupting the update...")
+ )
def perform_update(self, settings):
"""Updates dom0 and then other vms."""
- admins = [row for row in self.vms_to_update
- if row.vm.klass == 'AdminVM']
- templs = [row for row in self.vms_to_update
- if row.vm.klass != 'AdminVM']
+ admins = [
+ row for row in self.vms_to_update if row.vm.klass == "AdminVM"
+ ]
+ templs = [
+ row for row in self.vms_to_update if row.vm.klass != "AdminVM"
+ ]
GLib.idle_add(self.set_total_progress, 0)
if admins:
@@ -143,37 +144,39 @@ def update_admin_vm(self, admins):
GLib.idle_add(admin.set_status, UpdateStatus.Cancelled)
GLib.idle_add(
admin.append_text_view,
- l("Canceled update for {}\n").format(admin.vm.name))
+ l("Canceled update for {}\n").format(admin.vm.name),
+ )
self.update_details.update_buffer()
return
self.log.debug("Start adminVM updating")
info = f"Checking for available updates for {admin.name}...\n"
- GLib.idle_add(
- admin.append_text_view,
- l(info).format(admin.name))
+ GLib.idle_add(admin.append_text_view, l(info).format(admin.name))
GLib.idle_add(admin.set_status, UpdateStatus.ProgressUnknown)
self.update_details.update_buffer()
def qubes_dom0_update(*args):
- with subprocess.Popen(['sudo', 'qubes-dom0-update'] + list(args),
- stderr=subprocess.PIPE, stdout=subprocess.PIPE) as subproc:
+ with subprocess.Popen(
+ ["sudo", "qubes-dom0-update"] + list(args),
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ ) as subproc:
read_err_thread = threading.Thread(
- target=self.dump_to_textview,
- args=(subproc.stderr, admin)
+ target=self.dump_to_textview, args=(subproc.stderr, admin)
)
read_out_thread = threading.Thread(
- target=self.dump_to_textview,
- args=(subproc.stdout, admin)
+ target=self.dump_to_textview, args=(subproc.stdout, admin)
)
read_err_thread.start()
read_out_thread.start()
- while subproc.poll() is None \
- or read_out_thread.is_alive() \
- or read_err_thread.is_alive():
+ while (
+ subproc.poll() is None
+ or read_out_thread.is_alive()
+ or read_err_thread.is_alive()
+ ):
time.sleep(1)
if self.exit_triggered and subproc.poll() is None:
subproc.send_signal(signal.SIGINT)
@@ -187,17 +190,18 @@ def qubes_dom0_update(*args):
with Ticker(admin):
curr_pkg = self._get_packages_admin()
- returncode = qubes_dom0_update('--refresh', '--check-only')
+ returncode = qubes_dom0_update("--refresh", "--check-only")
if returncode != 100:
if returncode != 0:
GLib.idle_add(admin.set_status, UpdateStatus.Error)
else:
GLib.idle_add(
- admin.set_status, UpdateStatus.NoUpdatesFound)
+ admin.set_status, UpdateStatus.NoUpdatesFound
+ )
self.update_details.update_buffer()
return
- returncode = qubes_dom0_update('-y')
+ returncode = qubes_dom0_update("-y")
if returncode != 0:
GLib.idle_add(admin.set_status, UpdateStatus.Error)
self.update_details.update_buffer()
@@ -213,7 +217,9 @@ def qubes_dom0_update(*args):
GLib.idle_add(
admin.append_text_view,
l("Error on updating {}: {}\n{}").format(
- admin.vm.name, str(ex), ex.output.decode()))
+ admin.vm.name, str(ex), ex.output.decode()
+ ),
+ )
GLib.idle_add(admin.set_status, UpdateStatus.Error)
self.update_details.update_buffer()
@@ -224,8 +230,12 @@ def _get_packages_admin() -> Dict[str, List[str]]:
Use rpm to return the installed packages and their versions.
"""
- cmd = ["rpm", "-qa", "--queryformat",
- "%{NAME} %{VERSION}-%{RELEASE}\n",]
+ cmd = [
+ "rpm",
+ "-qa",
+ "--queryformat",
+ "%{NAME} %{VERSION}-%{RELEASE}\n",
+ ]
# EXAMPLE OUTPUT:
# qubes-core-agent 4.1.351.fc34
package_list = subprocess.check_output(cmd).decode().splitlines()
@@ -240,7 +250,7 @@ def _get_packages_admin() -> Dict[str, List[str]]:
@staticmethod
def _compare_packages(
- old: Dict[str, List[str]], new: Dict[str, List[str]]
+ old: Dict[str, List[str]], new: Dict[str, List[str]]
) -> Dict[str, Dict]:
"""
Compare installed packages and return dictionary with differences.
@@ -248,12 +258,15 @@ def _compare_packages(
:param old: Dict[package_name, version] packages before update
:param new: Dict[package_name, version] packages after update
"""
- return {"installed": {pkg: new[pkg] for pkg in new if pkg not in old},
- "updated": {pkg: {"old": old[pkg], "new": new[pkg]}
- for pkg in new
- if pkg in old and old[pkg] != new[pkg]
- },
- "removed": {pkg: old[pkg] for pkg in old if pkg not in new}}
+ return {
+ "installed": {pkg: new[pkg] for pkg in new if pkg not in old},
+ "updated": {
+ pkg: {"old": old[pkg], "new": new[pkg]}
+ for pkg in new
+ if pkg in old and old[pkg] != new[pkg]
+ },
+ "removed": {pkg: old[pkg] for pkg in old if pkg not in new},
+ }
@staticmethod
def _print_changes(changes: Dict[str, Dict]) -> str:
@@ -270,7 +283,7 @@ def _print_changes(changes: Dict[str, Dict]) -> str:
for pkg in changes["updated"]:
old_ver = str(changes["updated"][pkg]["old"])[2:-2]
new_ver = str(changes["updated"][pkg]["new"])[2:-2]
- result += f'{pkg} {old_ver} -> {new_ver}\n'
+ result += f"{pkg} {old_ver} -> {new_ver}\n"
else:
result += "None\n"
@@ -290,7 +303,8 @@ def update_templates(self, to_update, settings):
GLib.idle_add(row.set_status, UpdateStatus.Cancelled)
GLib.idle_add(
row.append_text_view,
- l("Canceled update for {}\n").format(row.vm.name))
+ l("Canceled update for {}\n").format(row.vm.name),
+ )
GLib.idle_add(self.set_total_progress, 100)
self.update_details.update_buffer()
return
@@ -298,8 +312,8 @@ def update_templates(self, to_update, settings):
for row in to_update:
GLib.idle_add(
- row.append_text_view,
- l("Updating {}\n").format(row.name))
+ row.append_text_view, l("Updating {}\n").format(row.name)
+ )
GLib.idle_add(row.set_status, UpdateStatus.InProgress)
self.update_details.update_buffer()
@@ -312,45 +326,51 @@ def update_templates(self, to_update, settings):
GLib.idle_add(
row.append_text_view,
l("Error on updating {}: {}\n{}").format(
- row.name, str(ex), ex.output.decode()))
+ row.name, str(ex), ex.output.decode()
+ ),
+ )
GLib.idle_add(row.set_status, UpdateStatus.Error)
self.update_details.update_buffer()
def do_update_templates(
- self, rows: Dict[str, RowWrapper], settings: Settings):
+ self, rows: Dict[str, RowWrapper], settings: Settings
+ ):
"""Runs `qubes-vm-update` command."""
targets = ",".join((name for name in rows.keys()))
args = []
if settings.max_concurrency is not None:
- args.extend(
- ('--max-concurrency',
- str(settings.max_concurrency)))
+ args.extend(("--max-concurrency", str(settings.max_concurrency)))
# pylint: disable=consider-using-with
proc = subprocess.Popen(
- ['qubes-vm-update',
- '--show-output',
- '--just-print-progress',
- '--force-update',
- *args,
- '--targets', targets],
- stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ [
+ "qubes-vm-update",
+ "--show-output",
+ "--just-print-progress",
+ "--force-update",
+ *args,
+ "--targets",
+ targets,
+ ],
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
read_err_thread = threading.Thread(
- target=self.read_stderrs,
- args=(proc, rows)
+ target=self.read_stderrs, args=(proc, rows)
)
read_out_thread = threading.Thread(
- target=self.read_stdouts,
- args=(proc, rows)
+ target=self.read_stdouts, args=(proc, rows)
)
read_err_thread.start()
read_out_thread.start()
- while proc.poll() is None \
- or read_out_thread.is_alive() \
- or read_err_thread.is_alive():
+ while (
+ proc.poll() is None
+ or read_out_thread.is_alive()
+ or read_err_thread.is_alive()
+ ):
time.sleep(1)
if self.exit_triggered and proc.poll() is None:
proc.send_signal(signal.SIGINT)
@@ -360,7 +380,7 @@ def do_update_templates(
self.retcode = proc.returncode
def read_stderrs(self, proc, rows):
- for untrusted_line in iter(proc.stderr.readline, ''):
+ for untrusted_line in iter(proc.stderr.readline, ""):
if untrusted_line:
self.handle_err_line(untrusted_line, rows)
else:
@@ -375,8 +395,8 @@ def handle_err_line(self, untrusted_line, rows):
progress = int(float(info))
GLib.idle_add(rows[name].set_update_progress, progress)
total_progress = sum(
- row.get_update_progress()
- for row in rows.values()) / len(rows)
+ row.get_update_progress() for row in rows.values()
+ ) / len(rows)
GLib.idle_add(self.set_total_progress, total_progress)
except ValueError:
@@ -392,17 +412,19 @@ def handle_err_line(self, untrusted_line, rows):
def read_stdouts(self, proc, rows):
curr_name_out = ""
- for untrusted_line in iter(proc.stdout.readline, ''):
+ for untrusted_line in iter(proc.stdout.readline, ""):
if untrusted_line:
line = self._sanitize_line(untrusted_line)
- maybe_name, text = line.split(' ', 1)
+ maybe_name, text = line.split(" ", 1)
suffix = len(":out:")
if maybe_name[:-suffix] in rows.keys():
curr_name_out = maybe_name[:-suffix]
if curr_name_out:
rows[curr_name_out].append_text_view(text)
- if (self.update_details.active_row is not None and
- curr_name_out == self.update_details.active_row.name):
+ if (
+ self.update_details.active_row is not None
+ and curr_name_out == self.update_details.active_row.name
+ ):
self.update_details.update_buffer()
else:
break
@@ -411,13 +433,15 @@ def read_stdouts(self, proc, rows):
def dump_to_textview(self, stream, row):
curr_name_out = row.name
- for untrusted_line in iter(stream.readline, ''):
+ for untrusted_line in iter(stream.readline, ""):
if untrusted_line:
text = self._sanitize_line(untrusted_line)
if curr_name_out:
row.append_text_view(text)
- if (self.update_details.active_row is not None and
- curr_name_out == self.update_details.active_row.name):
+ if (
+ self.update_details.active_row is not None
+ and curr_name_out == self.update_details.active_row.name
+ ):
self.update_details.update_buffer()
else:
break
@@ -426,8 +450,8 @@ def dump_to_textview(self, stream, row):
@staticmethod
def _sanitize_line(untrusted_line: bytes) -> str:
- ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
- line = ansi_escape.sub('', untrusted_line.decode())
+ ansi_escape = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]")
+ line = ansi_escape.sub("", untrusted_line.decode())
return line
def set_total_progress(self, progress):
@@ -456,7 +480,8 @@ def row_selected(self, _emitter, path, _col):
self.selection.unselect_all()
self.selection.select_path(path)
self.update_details.set_active_row(
- self.vms_to_update[path.get_indices()[0]])
+ self.vms_to_update[path.get_indices()[0]]
+ )
def get_update_summary(self):
"""Returns update summary.
@@ -467,22 +492,39 @@ def get_update_summary(self):
3. vms that update was canceled before starting.
"""
updated = len(
- [row for row in self.vms_to_update
- if row.status == UpdateStatus.Success])
+ [
+ row
+ for row in self.vms_to_update
+ if row.status == UpdateStatus.Success
+ ]
+ )
no_updates = len(
- [row for row in self.vms_to_update
- if row.status == UpdateStatus.NoUpdatesFound])
+ [
+ row
+ for row in self.vms_to_update
+ if row.status == UpdateStatus.NoUpdatesFound
+ ]
+ )
failed = len(
- [row for row in self.vms_to_update
- if row.status == UpdateStatus.Error])
+ [
+ row
+ for row in self.vms_to_update
+ if row.status == UpdateStatus.Error
+ ]
+ )
cancelled = len(
- [row for row in self.vms_to_update
- if row.status == UpdateStatus.Cancelled])
+ [
+ row
+ for row in self.vms_to_update
+ if row.status == UpdateStatus.Cancelled
+ ]
+ )
return updated, no_updates, failed, cancelled
class Ticker:
"""Helper for dom0 progressbar."""
+
def __init__(self, *args):
self.ticker_done = False
self.args = args
@@ -517,9 +559,11 @@ def __init__(self, builder):
self.copy_button.connect("clicked", self.copy_content)
self.progress_textview: Gtk.TextView = self.builder.get_object(
- "progress_textview")
- self.progress_scrolled_window: Gtk.ScrolledWindow = \
+ "progress_textview"
+ )
+ self.progress_scrolled_window: Gtk.ScrolledWindow = (
self.builder.get_object("progress_scrolled_window")
+ )
def copy_content(self, _emitter):
if self.active_row is None:
@@ -558,15 +602,15 @@ def update_buffer(self):
def _autoscroll(self):
adjustment = self.progress_scrolled_window.get_vadjustment()
adjustment.set_value(
- adjustment.get_upper() - adjustment.get_page_size())
+ adjustment.get_upper() - adjustment.get_page_size()
+ )
-class CellRendererProgressWithResult(
- Gtk.CellRendererProgress
-):
+class CellRendererProgressWithResult(Gtk.CellRendererProgress):
"""
Custom Cell Renderer to show progressbar or finish icon.
"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._status = None
@@ -581,20 +625,22 @@ def status(self, value):
# pylint: disable=arguments-differ
def do_render(self, context, widget, background_area, cell_area, flags):
- status: UpdateStatus = self.get_property('status')
+ status: UpdateStatus = self.get_property("status")
if status == UpdateStatus.Success:
- self.draw_icon('qubes-check-yes', context, cell_area)
+ self.draw_icon("qubes-check-yes", context, cell_area)
elif status == UpdateStatus.NoUpdatesFound:
- self.draw_icon('qubes-check-maybe', context, cell_area)
+ self.draw_icon("qubes-check-maybe", context, cell_area)
elif status in (UpdateStatus.Error, UpdateStatus.Cancelled):
- self.draw_icon('qubes-delete-x', context, cell_area)
+ self.draw_icon("qubes-delete-x", context, cell_area)
elif status == UpdateStatus.ProgressUnknown:
Gtk.CellRendererProgress.do_render(
- self, context, widget, background_area, cell_area, flags)
+ self, context, widget, background_area, cell_area, flags
+ )
else:
self.set_property("pulse", -1)
Gtk.CellRendererProgress.do_render(
- self, context, widget, background_area, cell_area, flags)
+ self, context, widget, background_area, cell_area, flags
+ )
def draw_icon(self, icon_name: str, context, cell_area):
# pylint: disable=no-member
@@ -603,6 +649,6 @@ def draw_icon(self, icon_name: str, context, cell_area):
context,
pixbuf,
cell_area.x + self.props.xpad,
- cell_area.y + self.props.ypad
+ cell_area.y + self.props.ypad,
)
context.paint()
diff --git a/qui/updater/summary_page.py b/qui/updater/summary_page.py
index 504e6b3e..fc8cdf24 100644
--- a/qui/updater/summary_page.py
+++ b/qui/updater/summary_page.py
@@ -26,19 +26,31 @@
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk # isort:skip
from typing import Optional, Any
import qubesadmin
from qubesadmin.events.utils import wait_for_domain_shutdown
-from qubes_config.widgets.gtk_utils import load_icon, show_dialog, \
- show_dialog_with_icon, show_error, RESPONSES_OK
+from qubes_config.widgets.gtk_utils import (
+ load_icon,
+ show_dialog,
+ show_dialog_with_icon,
+ show_error,
+ RESPONSES_OK,
+)
from qubes_config.widgets.utils import get_boolean_feature
-from qui.updater.utils import disable_checkboxes, pass_through_event_window, \
- HeaderCheckbox, QubeClass, QubeName, \
- RowWrapper, ListWrapper, on_head_checkbox_toggled
+from qui.updater.utils import (
+ disable_checkboxes,
+ pass_through_event_window,
+ HeaderCheckbox,
+ QubeClass,
+ QubeName,
+ RowWrapper,
+ ListWrapper,
+ on_head_checkbox_toggled,
+)
from locale import gettext as l
@@ -51,12 +63,7 @@ class SummaryPage:
"""
def __init__(
- self,
- builder,
- log,
- next_button,
- cancel_button,
- back_by_row_selection
+ self, builder, log, next_button, cancel_button, back_by_row_selection
):
self.builder = builder
self.log = log
@@ -70,38 +77,43 @@ def __init__(
self.updated_tmpls: Optional[list] = None
self.restart_list: Gtk.TreeView = self.builder.get_object(
- "restart_list")
+ "restart_list"
+ )
self.list_store: Optional[ListWrapper] = None
self.stack: Gtk.Stack = self.builder.get_object("main_stack")
self.page: Gtk.Box = self.builder.get_object("restart_page")
self.label_summary: Gtk.Label = self.builder.get_object("label_summary")
- self.restart_list.connect("row-activated",
- self.on_checkbox_toggled)
+ self.restart_list.connect("row-activated", self.on_checkbox_toggled)
self.app_vm_list: Gtk.ListStore = self.builder.get_object(
- "restart_list_store")
+ "restart_list_store"
+ )
restart_checkbox_column: Gtk.TreeViewColumn = self.builder.get_object(
- "restart_checkbox_column")
- restart_checkbox_column.connect("clicked",
- self.on_header_toggled)
+ "restart_checkbox_column"
+ )
+ restart_checkbox_column.connect("clicked", self.on_header_toggled)
restart_header_button: Gtk.Button = restart_checkbox_column.get_button()
- restart_header_button.connect('realize', pass_through_event_window)
+ restart_header_button.connect("realize", pass_through_event_window)
self.restart_header: Gtk.Label = self.builder.get_object(
- "restart_header")
- self.restart_scrolled_window: Gtk.ScrolledWindow = \
+ "restart_header"
+ )
+ self.restart_scrolled_window: Gtk.ScrolledWindow = (
self.builder.get_object("restart_scrolled_window")
+ )
self.head_checkbox_button: Gtk.CheckButton = self.builder.get_object(
- "restart_checkbox_header")
+ "restart_checkbox_header"
+ )
self.head_checkbox_button.set_inconsistent(True)
- self.head_checkbox_button.connect(
- "toggled", self.on_header_toggled)
+ self.head_checkbox_button.connect("toggled", self.on_header_toggled)
self.head_checkbox = RestartHeaderCheckbox(
- self.head_checkbox_button, self.next_button)
+ self.head_checkbox_button, self.next_button
+ )
self.summary_list: Gtk.TreeView = self.builder.get_object(
- "summary_list")
+ "summary_list"
+ )
self.summary_list.connect("row-activated", back_by_row_selection)
@disable_checkboxes
@@ -117,8 +129,7 @@ def refresh_buttons(self):
"""Refresh additional info column and finish button info."""
for row in self.list_store:
row.refresh_additional_info()
- selected_num = sum(
- row.selected for row in self.list_store)
+ selected_num = sum(row.selected for row in self.list_store)
if selected_num == 0:
self.head_checkbox.state = HeaderCheckbox.NONE
elif selected_num == len(self.list_store):
@@ -141,7 +152,8 @@ def on_header_toggled(self, _emitter):
the cycle will start from (1).
"""
on_head_checkbox_toggled(
- self.list_store, self.head_checkbox, self.select_rows)
+ self.list_store, self.head_checkbox, self.select_rows
+ )
@property
def is_populated(self) -> bool:
@@ -155,10 +167,10 @@ def is_visible(self):
@disable_checkboxes
def show(
- self,
- qube_updated_num: int,
- qube_no_updates_num: int,
- qube_failed_num: int
+ self,
+ qube_updated_num: int,
+ qube_no_updates_num: int,
+ qube_failed_num: int,
):
"""Show this page and handle buttons."""
self.log.debug("Show summary page")
@@ -166,15 +178,18 @@ def show(
summary_1 = ngettext(
"%(num)d qube updated successfully.",
"%(num)d qubes updated successfully.",
- qube_updated_num) % {'num': qube_updated_num}
+ qube_updated_num,
+ ) % {"num": qube_updated_num}
summary_2 = ngettext(
"%(num)d qube attempted to update but found no updates.",
"%(num)d qubes attempted to update but found no updates.",
- qube_no_updates_num) % {'num': qube_no_updates_num}
+ qube_no_updates_num,
+ ) % {"num": qube_no_updates_num}
summary_3 = ngettext(
"%(num)d qube failed to update.",
"%(num)d qubes failed to update.",
- qube_failed_num) % {'num': qube_failed_num}
+ qube_failed_num,
+ ) % {"num": qube_failed_num}
summary = "\n".join((summary_1, summary_2, summary_3))
self.label_summary.set_label(summary)
self.cancel_button.set_label(l("_Back"))
@@ -194,7 +209,8 @@ def populate_restart_list(self, restart, vm_updated, settings):
self.log.debug("populate restart list")
self.summary_list.set_model(vm_updated.list_store_raw)
self.updated_tmpls = [
- row for row in vm_updated
+ row
+ for row in vm_updated
if bool(row.status)
and QubeClass[row.vm.klass] == QubeClass.TemplateVM
]
@@ -203,11 +219,13 @@ def populate_restart_list(self, restart, vm_updated, settings):
possibly_changed_vms.update(template.vm.derived_vms)
self.list_store = ListWrapper(
- RestartRowWrapper, self.restart_list.get_model())
+ RestartRowWrapper, self.restart_list.get_model()
+ )
for vm in possibly_changed_vms:
if vm.is_running() and (
- vm.klass != 'DispVM' or not vm.auto_cleanup):
+ vm.klass != "DispVM" or not vm.auto_cleanup
+ ):
self.list_store.append_vm(vm)
if settings.restart_service_vms:
@@ -226,22 +244,18 @@ def populate_restart_list(self, restart, vm_updated, settings):
def select_rows(self):
for row in self.list_store:
row.selected = (
- row.is_service_qube
- and not row.is_excluded
- and AppVMType.SERVICEVM in self.head_checkbox.allowed
- or
- not row.is_service_qube
- and not row.is_excluded
- and AppVMType.NON_SERVICEVM in self.head_checkbox.allowed
- or
- AppVMType.EXCLUDED in self.head_checkbox.allowed
+ row.is_service_qube
+ and not row.is_excluded
+ and AppVMType.SERVICEVM in self.head_checkbox.allowed
+ or not row.is_service_qube
+ and not row.is_excluded
+ and AppVMType.NON_SERVICEVM in self.head_checkbox.allowed
+ or AppVMType.EXCLUDED in self.head_checkbox.allowed
)
def restart_selected_vms(self, show_only_error: bool):
self.log.debug("Start restarting")
- self.restart_thread = threading.Thread(
- target=self.perform_restart
- )
+ self.restart_thread = threading.Thread(target=self.perform_restart)
self.restart_thread.start()
@@ -254,9 +268,13 @@ def restart_selected_vms(self, show_only_error: bool):
# show waiting dialog
spinner = Gtk.Spinner()
spinner.start()
- dialog = show_dialog(None, l("Applying updates to qubes"), l(
- "Waiting for qubes to be restarted/shutdown."),
- {}, spinner)
+ dialog = show_dialog(
+ None,
+ l("Applying updates to qubes"),
+ l("Waiting for qubes to be restarted/shutdown."),
+ {},
+ spinner,
+ )
dialog.set_deletable(False)
dialog.show()
self.log.debug("Show restart dialog")
@@ -276,24 +294,26 @@ def restart_selected_vms(self, show_only_error: bool):
def perform_restart(self):
- tmpls_to_shutdown = [row.vm
- for row in self.updated_tmpls
- if row.vm.is_running()]
- to_restart = [qube_row.vm
- for qube_row in self.list_store
- if qube_row.selected
- and qube_row.is_service_qube]
- to_shutdown = [qube_row.vm
- for qube_row in self.list_store
- if qube_row.selected
- and not qube_row.is_service_qube]
+ tmpls_to_shutdown = [
+ row.vm for row in self.updated_tmpls if row.vm.is_running()
+ ]
+ to_restart = [
+ qube_row.vm
+ for qube_row in self.list_store
+ if qube_row.selected and qube_row.is_service_qube
+ ]
+ to_shutdown = [
+ qube_row.vm
+ for qube_row in self.list_store
+ if qube_row.selected and not qube_row.is_service_qube
+ ]
if not any([tmpls_to_shutdown, to_restart, to_shutdown]):
self.status = RestartStatus.NOTHING_TO_DO
return
# clear err and perform shutdown/start
- self.err = ''
+ self.err = ""
self.shutdown_domains(tmpls_to_shutdown)
self.restart_vms(to_restart)
self.shutdown_domains(to_shutdown)
@@ -312,9 +332,10 @@ def shutdown_domains(self, to_shutdown):
wait_for.append(vm)
self.log.info("Shutdown %s", vm.name)
except qubesadmin.exc.QubesVMError as err:
- self.err += vm.name + " cannot shutdown: " + str(err) + '\n'
- self.log.error("Cannot shutdown %s because %s",
- vm.name, str(err))
+ self.err += vm.name + " cannot shutdown: " + str(err) + "\n"
+ self.log.error(
+ "Cannot shutdown %s because %s", vm.name, str(err)
+ )
self.status = RestartStatus.ERROR_TMPL_DOWN
asyncio.run(wait_for_domain_shutdown(wait_for))
@@ -333,7 +354,7 @@ def restart_vms(self, to_restart):
vm.start()
self.log.info("Restart %s", vm.name)
except qubesadmin.exc.QubesVMError as err:
- self.err += vm.name + " cannot start: " + str(err) + '\n'
+ self.err += vm.name + " cannot start: " + str(err) + "\n"
self.log.error("Cannot start %s because %s", vm.name, str(err))
self.status = RestartStatus.ERROR_APP_DOWN
@@ -344,13 +365,14 @@ def _show_status_dialog(self, show_only_error: bool):
l("Success"),
l("All qubes were restarted/shutdown successfully."),
buttons=RESPONSES_OK,
- icon_name="qubes-check-yes"
+ icon_name="qubes-check-yes",
)
elif self.status.is_error():
- show_error(None, "Failure",
- l("During restarting following errors occurs: ")
- + self.err
- )
+ show_error(
+ None,
+ "Failure",
+ l("During restarting following errors occurs: ") + self.err,
+ )
self.log.error("Restart error: %s", self.err)
self.status = RestartStatus.ERROR_APP_START
@@ -367,7 +389,7 @@ def __init__(self, list_store, vm, _selection: Any):
False,
load_icon(vm.icon),
QubeName(vm.name, str(vm.label)),
- '',
+ "",
]
super().__init__(list_store, vm, raw_row)
@@ -381,14 +403,16 @@ def selected(self, value):
self.refresh_additional_info()
def refresh_additional_info(self):
- self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = ''
+ self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = ""
if self.selected and not self.is_service_qube:
- self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = \
- 'This qube and all running applications within will be shutdown'
+ self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = (
+ "This qube and all running applications within will be shutdown"
+ )
if self.selected and self.is_excluded:
- self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = \
- 'This qube has been explicitly ' \
- 'disabled from restarting in settings'
+ self.raw_row[RestartRowWrapper._ADDITIONAL_INFO] = (
+ 'This qube has been explicitly '
+ "disabled from restarting in settings"
+ )
@property
def icon(self):
@@ -408,11 +432,11 @@ def additional_info(self):
@property
def is_service_qube(self):
- return get_boolean_feature(self.vm, 'servicevm', False)
+ return get_boolean_feature(self.vm, "servicevm", False)
@property
def is_excluded(self):
- return not get_boolean_feature(self.vm, 'restart-after-update', True)
+ return not get_boolean_feature(self.vm, "restart-after-update", True)
class AppVMType:
@@ -429,16 +453,19 @@ class RestartStatus(Enum):
ERROR_APP_DOWN = 12
ERROR_APP_START = 13
- def is_error(self: 'RestartStatus') -> bool:
+ def is_error(self: "RestartStatus") -> bool:
return self in [
- RestartStatus.ERROR_TMPL_DOWN, RestartStatus.ERROR_APP_DOWN,
- RestartStatus.ERROR_APP_START]
+ RestartStatus.ERROR_TMPL_DOWN,
+ RestartStatus.ERROR_APP_DOWN,
+ RestartStatus.ERROR_APP_START,
+ ]
class RestartHeaderCheckbox(HeaderCheckbox):
def __init__(self, checkbox_column_button, next_button):
- super().__init__(checkbox_column_button,
- [None, None, AppVMType.EXCLUDED])
+ super().__init__(
+ checkbox_column_button, [None, None, AppVMType.EXCLUDED]
+ )
self.next_button = next_button
def allow_service_vms(self, value=True):
@@ -455,9 +482,11 @@ def allow_non_service_vms(self, value=True):
# pylint: disable=arguments-differ
def all_action(self, num, *args, **kwargs):
- text = ngettext("_Finish and restart/shutdown %(num)d qube",
- "_Finish and restart/shutdown %(num)d qubes",
- num) % {'num': num}
+ text = ngettext(
+ "_Finish and restart/shutdown %(num)d qube",
+ "_Finish and restart/shutdown %(num)d qubes",
+ num,
+ ) % {"num": num}
self.next_button.set_label(text)
def inconsistent_action(self, *args, **kwargs):
diff --git a/qui/updater/tests/conftest.py b/qui/updater/tests/conftest.py
index 16d9cc0f..1db8787a 100644
--- a/qui/updater/tests/conftest.py
+++ b/qui/updater/tests/conftest.py
@@ -22,9 +22,14 @@
import pytest
import importlib.resources
-from qubes_config.tests.conftest import add_dom0_vm_property, \
- add_dom0_text_property, add_dom0_feature, add_expected_vm, \
- add_feature_with_template_to_all, add_feature_to_all
+from qubes_config.tests.conftest import (
+ add_dom0_vm_property,
+ add_dom0_text_property,
+ add_dom0_feature,
+ add_expected_vm,
+ add_feature_with_template_to_all,
+ add_feature_to_all,
+)
from qubesadmin.tests import QubesTest
import gi
@@ -33,8 +38,8 @@
from qui.updater.summary_page import RestartRowWrapper
from qui.updater.utils import ListWrapper
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
@@ -46,104 +51,163 @@ def test_qapp():
def test_qapp_impl():
"""Test QubesApp"""
qapp = QubesTest()
- qapp._local_name = 'dom0' # pylint: disable=protected-access
+ qapp._local_name = "dom0" # pylint: disable=protected-access
- add_dom0_vm_property(qapp, 'clockvm', 'sys-net')
- add_dom0_vm_property(qapp, 'updatevm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_netvm', 'sys-net')
- add_dom0_vm_property(qapp, 'default_template', 'fedora-36')
- add_dom0_vm_property(qapp, 'default_dispvm', 'fedora-36')
+ add_dom0_vm_property(qapp, "clockvm", "sys-net")
+ add_dom0_vm_property(qapp, "updatevm", "sys-net")
+ add_dom0_vm_property(qapp, "default_netvm", "sys-net")
+ add_dom0_vm_property(qapp, "default_template", "fedora-36")
+ add_dom0_vm_property(qapp, "default_dispvm", "fedora-36")
- add_dom0_text_property(qapp, 'default_kernel', '1.1')
- add_dom0_text_property(qapp, 'default_pool', 'file')
+ add_dom0_text_property(qapp, "default_kernel", "1.1")
+ add_dom0_text_property(qapp, "default_pool", "file")
- add_dom0_feature(qapp, 'gui-default-allow-fullscreen', '')
- add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
- add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
- add_dom0_feature(qapp, 'qubes-vm-update-update-if-stale', None)
- add_dom0_feature(qapp, 'skip-update', None)
- add_dom0_feature(qapp, 'qubes-vm-update-hide-skipped', None)
- add_dom0_feature(qapp, 'qubes-vm-update-hide-updated', None)
+ add_dom0_feature(qapp, "gui-default-allow-fullscreen", "")
+ add_dom0_feature(qapp, "gui-default-allow-utf8-titles", "")
+ add_dom0_feature(qapp, "gui-default-trayicon-mode", "")
+ add_dom0_feature(qapp, "qubes-vm-update-update-if-stale", None)
+ add_dom0_feature(qapp, "skip-update", None)
+ add_dom0_feature(qapp, "qubes-vm-update-hide-skipped", None)
+ add_dom0_feature(qapp, "qubes-vm-update-hide-updated", None)
# setup labels
- qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
- b'0\x00red\nblue\ngreen\n'
+ qapp.expected_calls[("dom0", "admin.label.List", None, None)] = (
+ b"0\x00red\nblue\ngreen\n"
+ )
# setup pools:
- qapp.expected_calls[('dom0', 'admin.pool.List', None, None)] = \
- b'0\x00linux-kernel\nlvm\nfile\n'
- qapp.expected_calls[('dom0', 'admin.pool.volume.List',
- 'linux-kernel', None)] = \
- b'0\x001.1\nmisc\n4.2\n'
-
- add_expected_vm(qapp, 'dom0', 'AdminVM',
- {}, {'service.qubes-update-check': 1,
- 'config.default.qubes-update-check': None,
- 'config-usbvm-name': None,
- 'gui-default-secure-copy-sequence': None,
- 'gui-default-secure-paste-sequence': None
- }, [])
- add_expected_vm(qapp, 'sys-net', 'AppVM',
- {'provides_network': ('bool', False, 'True')},
- {'service.qubes-update-check': None,
- 'service.qubes-updates-proxy': 1}, [])
-
- add_expected_vm(qapp, 'sys-firewall', 'AppVM',
- {'provides_network': ('bool', False, 'True')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'sys-usb', 'AppVM',
- {},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'fedora-36', 'TemplateVM',
- {"netvm": ("vm", False, ''),
- 'updateable': ('bool', True, "True")},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'fedora-35', 'TemplateVM',
- {"netvm": ("vm", False, ''),
- 'updateable': ('bool', True, "True")},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'default-dvm', 'DispVM',
- {'template_for_dispvms': ('bool', False, 'True'),
- 'auto_cleanup': ('bool', False, 'False')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-vm', 'AppVM',
- {}, {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-blue', 'AppVM',
- {'label': ('str', False, 'blue')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-red', 'AppVM',
- {'label': ('str', False, 'red')},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'test-standalone', 'StandaloneVM',
- {'label': ('str', False, 'green'),
- 'updateable': ('bool', True, "True")},
- {'service.qubes-update-check': None}, [])
-
- add_expected_vm(qapp, 'vault', 'AppVM',
- {"netvm": ("vm", False, '')},
- {'service.qubes-update-check': None}, [])
-
- add_feature_with_template_to_all(qapp, 'supported-service.qubes-u2f-proxy',
- ['test-vm', 'fedora-35', 'sys-usb'])
- add_feature_to_all(qapp, 'service.qubes-u2f-proxy',
- ['test-vm'])
- add_feature_to_all(qapp, 'restart-after-update', [])
- add_feature_to_all(qapp, 'updates-available', [])
- add_feature_to_all(qapp, 'last-update', [])
- add_feature_to_all(qapp, 'last-updates-check', [])
- add_feature_to_all(qapp, 'template-name', [])
- add_feature_to_all(qapp, 'servicevm',
- ['sys-usb', 'sys-firewall', 'sys-net'])
- add_feature_to_all(qapp, 'os-eol', [])
- add_feature_to_all(qapp, 'skip-update', [])
+ qapp.expected_calls[("dom0", "admin.pool.List", None, None)] = (
+ b"0\x00linux-kernel\nlvm\nfile\n"
+ )
+ qapp.expected_calls[
+ ("dom0", "admin.pool.volume.List", "linux-kernel", None)
+ ] = b"0\x001.1\nmisc\n4.2\n"
+
+ add_expected_vm(
+ qapp,
+ "dom0",
+ "AdminVM",
+ {},
+ {
+ "service.qubes-update-check": 1,
+ "config.default.qubes-update-check": None,
+ "config-usbvm-name": None,
+ "gui-default-secure-copy-sequence": None,
+ "gui-default-secure-paste-sequence": None,
+ },
+ [],
+ )
+ add_expected_vm(
+ qapp,
+ "sys-net",
+ "AppVM",
+ {"provides_network": ("bool", False, "True")},
+ {"service.qubes-update-check": None, "service.qubes-updates-proxy": 1},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "sys-firewall",
+ "AppVM",
+ {"provides_network": ("bool", False, "True")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp, "sys-usb", "AppVM", {}, {"service.qubes-update-check": None}, []
+ )
+
+ add_expected_vm(
+ qapp,
+ "fedora-36",
+ "TemplateVM",
+ {"netvm": ("vm", False, ""), "updateable": ("bool", True, "True")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "fedora-35",
+ "TemplateVM",
+ {"netvm": ("vm", False, ""), "updateable": ("bool", True, "True")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "default-dvm",
+ "DispVM",
+ {
+ "template_for_dispvms": ("bool", False, "True"),
+ "auto_cleanup": ("bool", False, "False"),
+ },
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp, "test-vm", "AppVM", {}, {"service.qubes-update-check": None}, []
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-blue",
+ "AppVM",
+ {"label": ("str", False, "blue")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-red",
+ "AppVM",
+ {"label": ("str", False, "red")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "test-standalone",
+ "StandaloneVM",
+ {
+ "label": ("str", False, "green"),
+ "updateable": ("bool", True, "True"),
+ },
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_expected_vm(
+ qapp,
+ "vault",
+ "AppVM",
+ {"netvm": ("vm", False, "")},
+ {"service.qubes-update-check": None},
+ [],
+ )
+
+ add_feature_with_template_to_all(
+ qapp,
+ "supported-service.qubes-u2f-proxy",
+ ["test-vm", "fedora-35", "sys-usb"],
+ )
+ add_feature_to_all(qapp, "service.qubes-u2f-proxy", ["test-vm"])
+ add_feature_to_all(qapp, "restart-after-update", [])
+ add_feature_to_all(qapp, "updates-available", [])
+ add_feature_to_all(qapp, "last-update", [])
+ add_feature_to_all(qapp, "last-updates-check", [])
+ add_feature_to_all(qapp, "template-name", [])
+ add_feature_to_all(
+ qapp, "servicevm", ["sys-usb", "sys-firewall", "sys-net"]
+ )
+ add_feature_to_all(qapp, "os-eol", [])
+ add_feature_to_all(qapp, "skip-update", [])
return qapp
@@ -153,8 +217,7 @@ def real_builder():
"""Gtk builder with actual config glade file registered"""
builder = Gtk.Builder()
builder.set_translation_domain("desktop-linux-manager")
- glade_ref = (importlib.resources.files('qui') /
- 'updater.glade')
+ glade_ref = importlib.resources.files("qui") / "updater.glade"
with importlib.resources.as_file(glade_ref) as path:
builder.add_from_file(str(path))
return builder
diff --git a/qui/updater/tests/test_intro_page.py b/qui/updater/tests/test_intro_page.py
index dfb7e501..06dd37c0 100644
--- a/qui/updater/tests/test_intro_page.py
+++ b/qui/updater/tests/test_intro_page.py
@@ -29,19 +29,20 @@
from qui.updater.updater import parse_args
from qui.updater.utils import ListWrapper, HeaderCheckbox
-@patch('subprocess.check_output')
+
+@patch("subprocess.check_output")
def test_populate_vm_list(
- mock_subprocess, real_builder, test_qapp,
- mock_next_button, mock_settings
+ mock_subprocess, real_builder, test_qapp, mock_next_button, mock_settings
):
mock_log = Mock()
sut = IntroPage(real_builder, mock_log, mock_next_button)
test_qapp.expected_calls[
- ('test-standalone', "admin.vm.feature.Get", 'updates-available', None)
- ] = b"0\x00" + str(1).encode()
+ ("test-standalone", "admin.vm.feature.Get", "updates-available", None)
+ ] = (b"0\x00" + str(1).encode())
# inconsistent output of qubes-vm-update, but it does not matter
- mock_subprocess.return_value = \
- b'Following templates will be updated:test-standalone'
+ mock_subprocess.return_value = (
+ b"Following templates will be updated:test-standalone"
+ )
assert not sut.is_populated
@@ -52,10 +53,11 @@ def test_populate_vm_list(
assert len(sut.get_vms_to_update()) == 1
test_qapp.expected_calls[
- ('fedora-36', "admin.vm.feature.Get", 'updates-available', None)
- ] = b"0\x00" + str(1).encode()
- mock_subprocess.return_value = \
- b'Following templates will be updated:test-standalone,fedora-36'
+ ("fedora-36", "admin.vm.feature.Get", "updates-available", None)
+ ] = (b"0\x00" + str(1).encode())
+ mock_subprocess.return_value = (
+ b"Following templates will be updated:test-standalone,fedora-36"
+ )
sut.populate_vm_list(test_qapp, mock_settings)
assert len(sut.list_store) == 4
@@ -70,8 +72,13 @@ def test_populate_vm_list(
),
)
def test_on_header_toggled(
- real_builder, test_qapp, updates_available, expectations,
- mock_next_button, mock_settings, mock_list_store
+ real_builder,
+ test_qapp,
+ updates_available,
+ expectations,
+ mock_next_button,
+ mock_settings,
+ mock_list_store,
):
mock_log = Mock()
sut = IntroPage(real_builder, mock_log, mock_next_button)
@@ -90,25 +97,28 @@ def test_on_header_toggled(
value = None
else:
value = False
- row.raw_row[row._UPDATES_AVAILABLE] = UpdatesAvailable.from_features(value, True)
+ row.raw_row[row._UPDATES_AVAILABLE] = UpdatesAvailable.from_features(
+ value, True
+ )
sut.head_checkbox.state = HeaderCheckbox.NONE
for expected in expectations:
selected_num = len([row for row in sut.list_store if row.selected])
assert selected_num == expected
- assert sut.checkbox_column_button.get_inconsistent() \
- and expected not in (0, 12) \
- or sut.checkbox_column_button.get_active() \
- and expected == 12 \
- or not sut.checkbox_column_button.get_active() \
- and expected == 0
+ assert (
+ sut.checkbox_column_button.get_inconsistent()
+ and expected not in (0, 12)
+ or sut.checkbox_column_button.get_active()
+ and expected == 12
+ or not sut.checkbox_column_button.get_active()
+ and expected == 0
+ )
assert mock_next_button.sensitive or expected == 0
sut.on_header_toggled(None)
def test_on_checkbox_toggled(
- real_builder, test_qapp,
- mock_next_button, mock_settings, mock_list_store
+ real_builder, test_qapp, mock_next_button, mock_settings, mock_list_store
):
mock_log = Mock()
sut = IntroPage(real_builder, mock_log, mock_next_button)
@@ -171,7 +181,7 @@ def test_on_checkbox_toggled(
_derived_qubes = _domains.difference(_non_derived_qubes)
-@patch('subprocess.check_output')
+@patch("subprocess.check_output")
@pytest.mark.parametrize(
# args: for `qubes-vm-update`
# selection is based on a result of `qubes-vm-update --dry-run *args`
@@ -183,77 +193,111 @@ def test_on_checkbox_toggled(
# `qubes-update-gui --all`
# Target all updatable VMs (AdminVM, TemplateVMs and StandaloneVMs)
pytest.param(
- ('--all', '--force-update'),
+ ("--all", "--force-update"),
",".join(_tmpls_and_stndas).encode(),
- ",".join(_derived_qubes).encode(), _non_derived_qubes,
- ('--all', '--force-update'),
- id="all"),
+ ",".join(_derived_qubes).encode(),
+ _non_derived_qubes,
+ ("--all", "--force-update"),
+ id="all",
+ ),
# `qubes-update-gui --update-if-stale 10`
# Target all TemplateVMs and StandaloneVMs with known updates or for
# which last update check was more than <10> days ago.
pytest.param(
- ('--non-interactive', '--update-if-stale', '10'),
- b'fedora-36', b'', {'fedora-36', 'dom0'},
- ('--update-if-stale', '10'),
- id="if-stale with dom0"),
+ ("--non-interactive", "--update-if-stale", "10"),
+ b"fedora-36",
+ b"",
+ {"fedora-36", "dom0"},
+ ("--update-if-stale", "10"),
+ id="if-stale with dom0",
+ ),
pytest.param(
- ('--non-interactive', '--update-if-stale', '10'),
- b'fedora-36', b'', {'fedora-36'},
- ('--update-if-stale', '10'),
- id="if-stale without dom0"),
+ ("--non-interactive", "--update-if-stale", "10"),
+ b"fedora-36",
+ b"",
+ {"fedora-36"},
+ ("--update-if-stale", "10"),
+ id="if-stale without dom0",
+ ),
# `qubes-update-gui --targets dom0,fedora-36`
# Comma separated list of VMs to target
pytest.param(
- ('--targets', 'dom0,fedora-36'), b'fedora-36',
- b'', {'dom0', 'fedora-36'}, ('--targets', 'fedora-36'),
- id="targets"),
+ ("--targets", "dom0,fedora-36"),
+ b"fedora-36",
+ b"",
+ {"dom0", "fedora-36"},
+ ("--targets", "fedora-36"),
+ id="targets",
+ ),
# `qubes-update-gui --standalones`
# Target all StandaloneVMs
pytest.param(
- ('--standalones',), b'',
- ",".join(_standalones).encode(), _standalones, ('--standalones',),
- id="standalones"),
+ ("--standalones",),
+ b"",
+ ",".join(_standalones).encode(),
+ _standalones,
+ ("--standalones",),
+ id="standalones",
+ ),
# `qubes-update-gui --dom0`
# Target dom0
- pytest.param(('--dom0', '--force-update'), b'', b'', {'dom0'},
- None, id="dom0"),
+ pytest.param(
+ ("--dom0", "--force-update"), b"", b"", {"dom0"}, None, id="dom0"
+ ),
# `qubes-update-gui --dom0 --skip dom0`
# Comma separated list of VMs to be skipped,
# works with all other options.
- pytest.param(('--dom0', '--skip', 'dom0'), b'', b'', set(), None,
- id="skip all"),
+ pytest.param(
+ ("--dom0", "--skip", "dom0"), b"", b"", set(), None, id="skip all"
+ ),
# `qubes-update-gui --skip dom0`
- pytest.param(('--skip', 'dom0'), b'', b'', set(), None,
- id="skip dom0"),
+ pytest.param(("--skip", "dom0"), b"", b"", set(), None, id="skip dom0"),
# `qubes-update-gui --skip garbage-name`
- pytest.param(('--skip', 'garbage-name'),
- ",".join(_tmpls_and_stndas).encode(),
- ",".join(_derived_qubes).encode(), _tmpls_and_stndas,
- ('--skip', 'garbage-name'),
- id="skip non dom0"),
+ pytest.param(
+ ("--skip", "garbage-name"),
+ ",".join(_tmpls_and_stndas).encode(),
+ ",".join(_derived_qubes).encode(),
+ _tmpls_and_stndas,
+ ("--skip", "garbage-name"),
+ id="skip non dom0",
+ ),
# `qubes-update-gui --targets dom0 --skip dom0`
# the same as `qubes-update-gui --dom0 --skip dom0`
pytest.param(
- ('--targets', 'dom0', '--skip', 'dom0'), b'', b'', set(),
- None, id="skip all targets"),
+ ("--targets", "dom0", "--skip", "dom0"),
+ b"",
+ b"",
+ set(),
+ None,
+ id="skip all targets",
+ ),
# `qubes-update-gui --templates dom0 --skip fedora-36,garbage-name`
# expected args are in alphabetical order
- pytest.param(('--templates', '--skip', 'fedora-36,garbage-name'),
- ",".join(_templates.difference({"fedora-36"})).encode(),
- b'',
- _templates.difference({"fedora-36"}),
- ('--skip', 'fedora-36,garbage-name', '--templates'),
- id="templates with skip"),
- pytest.param(('--force-update',), b'', b'', set(), None,
- id="force-update"),
+ pytest.param(
+ ("--templates", "--skip", "fedora-36,garbage-name"),
+ ",".join(_templates.difference({"fedora-36"})).encode(),
+ b"",
+ _templates.difference({"fedora-36"}),
+ ("--skip", "fedora-36,garbage-name", "--templates"),
+ id="templates with skip",
+ ),
+ pytest.param(
+ ("--force-update",), b"", b"", set(), None, id="force-update"
+ ),
),
)
def test_select_rows_ignoring_conditions(
- mock_subprocess,
- args, tmpls_and_stndas, derived_qubes, expected_selection,
- expected_args,
- real_builder, test_qapp, mock_next_button, mock_settings,
- mock_list_store
+ mock_subprocess,
+ args,
+ tmpls_and_stndas,
+ derived_qubes,
+ expected_selection,
+ expected_args,
+ real_builder,
+ test_qapp,
+ mock_next_button,
+ mock_settings,
+ mock_list_store,
):
mock_log = Mock()
sut = IntroPage(real_builder, mock_log, mock_next_button)
@@ -265,24 +309,27 @@ def test_select_rows_ignoring_conditions(
assert len(sut.list_store) == 12
- result = b''
+ result = b""
if tmpls_and_stndas:
- result += (b'Following templates and standalones will be updated: '
- + tmpls_and_stndas)
+ result += (
+ b"Following templates and standalones will be updated: "
+ + tmpls_and_stndas
+ )
if derived_qubes:
if result:
- result += b'\n'
- result += b'Following qubes will be updated: ' + derived_qubes
+ result += b"\n"
+ result += b"Following qubes will be updated: " + derived_qubes
mock_subprocess.return_value = result
- if (expected_args == ('--update-if-stale', '10')
- and expected_selection == {'fedora-36'}):
+ if expected_args == ("--update-if-stale", "10") and expected_selection == {
+ "fedora-36"
+ }:
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'last-updates-check', None)] = \
- b'0\x00' + b'3020-01-01 00:00:00'
+ ("dom0", "admin.vm.feature.Get", "last-updates-check", None)
+ ] = (b"0\x00" + b"3020-01-01 00:00:00")
cliargs = parse_args(args, test_qapp)
- sut.select_rows_ignoring_conditions(cliargs, test_qapp.domains['dom0'])
+ sut.select_rows_ignoring_conditions(cliargs, test_qapp.domains["dom0"])
to_update = {row.name for row in sut.list_store if row.selected}
assert to_update == expected_selection
@@ -291,6 +338,5 @@ def test_select_rows_ignoring_conditions(
mock_subprocess.assert_not_called()
else:
mock_subprocess.assert_called_with(
- ['qubes-vm-update', '--dry-run', '--quiet', *expected_args])
-
-
+ ["qubes-vm-update", "--dry-run", "--quiet", *expected_args]
+ )
diff --git a/qui/updater/tests/test_progress_page.py b/qui/updater/tests/test_progress_page.py
index b58ae11a..99692e20 100644
--- a/qui/updater/tests/test_progress_page.py
+++ b/qui/updater/tests/test_progress_page.py
@@ -25,8 +25,8 @@
import pytest
-gi.require_version('Gtk', '3.0')
-gi.require_version('GdkPixbuf', '2.0')
+gi.require_version("Gtk", "3.0")
+gi.require_version("GdkPixbuf", "2.0")
from gi.repository import Gtk
from qui.updater.intro_page import UpdateRowWrapper
@@ -35,18 +35,29 @@
from qui.updater.utils import ListWrapper, UpdateStatus
-@patch('threading.Thread')
+@patch("threading.Thread")
def test_init_update(
- mock_threading, mock_thread, real_builder, test_qapp,
- mock_next_button, mock_cancel_button, mock_label, mock_tree_view,
- all_vms_list):
+ mock_threading,
+ mock_thread,
+ real_builder,
+ test_qapp,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ mock_tree_view,
+ all_vms_list,
+):
mock_threading.return_value = mock_thread
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
sut.progress_list = mock_tree_view
@@ -66,16 +77,24 @@ def test_init_update(
mock_callback.assert_not_called()
-@patch('gi.repository.GLib.idle_add')
+@patch("gi.repository.GLib.idle_add")
def test_perform_update(
- idle_add, real_builder,
- mock_next_button, mock_cancel_button, mock_label, updatable_vms_list
+ idle_add,
+ real_builder,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ updatable_vms_list,
):
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
sut.vms_to_update = updatable_vms_list
@@ -92,32 +111,45 @@ def __call__(self, vm_rows, *args, **kwargs):
assert len(sut.update_admin_vm.vm_rows) == 1
assert len(sut.update_templates.vm_rows) == 3
- calls = [call(mock_next_button.set_sensitive, True),
- call(mock_label.set_text, "Update finished"),
- call(mock_cancel_button.set_visible, False)]
+ calls = [
+ call(mock_next_button.set_sensitive, True),
+ call(mock_label.set_text, "Update finished"),
+ call(mock_cancel_button.set_visible, False),
+ ]
idle_add.assert_has_calls(calls, any_order=True)
mock_callback.assert_called_once()
-@patch('gi.repository.GLib.idle_add')
-@patch('subprocess.check_output', return_value=b'')
+@patch("gi.repository.GLib.idle_add")
+@patch("subprocess.check_output", return_value=b"")
@pytest.mark.parametrize(
"interrupted",
(
pytest.param(True, id="interrupted"),
pytest.param(False, id="not interrupted"),
- )
+ ),
)
def test_update_admin_vm(
- mock_subprocess, idle_add, interrupted, real_builder, test_qapp,
- mock_next_button, mock_cancel_button, mock_label, mock_text_view,
- mock_list_store
+ mock_subprocess,
+ idle_add,
+ interrupted,
+ real_builder,
+ test_qapp,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ mock_text_view,
+ mock_list_store,
):
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
admins = ListWrapper(UpdateRowWrapper, mock_list_store)
@@ -141,23 +173,33 @@ def test_update_admin_vm(
mock_callback.assert_not_called()
-@patch('gi.repository.GLib.idle_add')
+@patch("gi.repository.GLib.idle_add")
@pytest.mark.parametrize(
"interrupted",
(
pytest.param(True, id="interrupted"),
pytest.param(False, id="not interrupted"),
- )
+ ),
)
def test_update_templates(
- idle_add, interrupted, real_builder, updatable_vms_list,
- mock_next_button, mock_cancel_button, mock_label, mock_text_view
+ idle_add,
+ interrupted,
+ real_builder,
+ updatable_vms_list,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ mock_text_view,
):
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
sut.do_update_templates = Mock()
@@ -176,21 +218,27 @@ def test_update_templates(
sut.update_details.set_active_row(updatable_vms_list[2])
- calls = [call(sut.set_total_progress, 100),
- call(mock_text_view.buffer.set_text, "Details 0"),
- call(mock_text_view.buffer.set_text, "Details 2"),
- ]
+ calls = [
+ call(sut.set_total_progress, 100),
+ call(mock_text_view.buffer.set_text, "Details 0"),
+ call(mock_text_view.buffer.set_text, "Details 2"),
+ ]
idle_add.assert_has_calls(calls, any_order=True)
if not interrupted:
sut.do_update_templates.assert_called()
mock_callback.assert_not_called()
-@patch('subprocess.Popen')
+@patch("subprocess.Popen")
def test_do_update_templates(
- mock_subprocess, real_builder, test_qapp,
- mock_next_button, mock_cancel_button, mock_label, mock_list_store,
- mock_settings
+ mock_subprocess,
+ real_builder,
+ test_qapp,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ mock_list_store,
+ mock_settings,
):
class MockPorc:
def __init__(self, finish_after_n_polls=2):
@@ -209,14 +257,17 @@ def poll(self):
return None
return self.returncode
-
mock_subprocess.return_value = MockPorc()
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
sut.read_stderrs = lambda *_args, **_kwargs: None
sut.read_stdouts = lambda *_args, **_kwargs: None
@@ -230,28 +281,41 @@ def poll(self):
sut.do_update_templates(rows, mock_settings)
- calls = [call(
- ['qubes-vm-update',
- '--show-output',
- '--just-print-progress',
- '--force-update',
- '--targets',
- 'fedora-35,fedora-36,test-standalone'],
- stderr=subprocess.PIPE, stdout=subprocess.PIPE)]
+ calls = [
+ call(
+ [
+ "qubes-vm-update",
+ "--show-output",
+ "--just-print-progress",
+ "--force-update",
+ "--targets",
+ "fedora-35,fedora-36,test-standalone",
+ ],
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
+ ]
mock_subprocess.assert_has_calls(calls)
mock_callback.assert_not_called()
assert sut.retcode == 40
def test_get_update_summary(
- real_builder,
- mock_next_button, mock_cancel_button, mock_label, updatable_vms_list
+ real_builder,
+ mock_next_button,
+ mock_cancel_button,
+ mock_label,
+ updatable_vms_list,
):
mock_log = Mock()
mock_callback = Mock()
sut = ProgressPage(
- real_builder, mock_log, mock_label, mock_next_button,
- mock_cancel_button, mock_callback
+ real_builder,
+ mock_log,
+ mock_label,
+ mock_next_button,
+ mock_cancel_button,
+ mock_callback,
)
updatable_vms_list[0].set_status(UpdateStatus.NoUpdatesFound)
diff --git a/qui/updater/tests/test_summary_page.py b/qui/updater/tests/test_summary_page.py
index 58573f83..fdc2d121 100644
--- a/qui/updater/tests/test_summary_page.py
+++ b/qui/updater/tests/test_summary_page.py
@@ -22,30 +22,42 @@
from unittest.mock import patch, call, Mock
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk # isort:skip
from qubes_config.widgets.gtk_utils import RESPONSES_OK
from qui.updater.intro_page import UpdateRowWrapper
-from qui.updater.summary_page import SummaryPage, AppVMType, RestartStatus, \
- RestartRowWrapper
+from qui.updater.summary_page import (
+ SummaryPage,
+ AppVMType,
+ RestartStatus,
+ RestartRowWrapper,
+)
from qui.updater.utils import HeaderCheckbox, UpdateStatus, ListWrapper
-@patch('qui.updater.summary_page.SummaryPage.refresh_buttons')
+@patch("qui.updater.summary_page.SummaryPage.refresh_buttons")
def test_show(
- refresh_buttons, real_builder, test_qapp, appvms_list,
- mock_next_button, mock_cancel_button
+ refresh_buttons,
+ real_builder,
+ test_qapp,
+ appvms_list,
+ mock_next_button,
+ mock_cancel_button,
):
test_qapp.expected_calls[
- ('test-blue', "admin.vm.feature.Get", 'restart-after-update', None)
- ] = b"0\x00" + "".encode()
+ ("test-blue", "admin.vm.feature.Get", "restart-after-update", None)
+ ] = (b"0\x00" + "".encode())
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
sut.list_store = appvms_list
@@ -53,27 +65,30 @@ def test_show(
sut.show(0, 1, 2)
assert sut.stack.get_visible_child() == sut.page
- assert sut.label_summary.get_text() == \
- "0 qubes updated successfully.\n" \
- "1 qube attempted to update but found no updates.\n" \
- "2 qubes failed to update."
+ assert (
+ sut.label_summary.get_text() == "0 qubes updated successfully.\n"
+ "1 qube attempted to update but found no updates.\n"
+ "2 qubes failed to update."
+ )
assert sut.cancel_button.visible
assert sut.cancel_button.label == "_Back"
refresh_buttons.assert_called_once()
def test_on_header_toggled(
- real_builder, test_qapp, appvms_list,
- mock_next_button, mock_cancel_button
+ real_builder, test_qapp, appvms_list, mock_next_button, mock_cancel_button
):
test_qapp.expected_calls[
- ('test-blue', "admin.vm.feature.Get", 'restart-after-update', None)
- ] = b"0\x00" + "".encode()
+ ("test-blue", "admin.vm.feature.Get", "restart-after-update", None)
+ ] = (b"0\x00" + "".encode())
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
sut.list_store = appvms_list
@@ -88,24 +103,33 @@ def test_on_header_toggled(
for expected in (0, service_num, non_excluded_num, all_num, 0):
selected_num = len([row for row in sut.list_store if row.selected])
assert selected_num == expected
- assert sut.head_checkbox_button.get_inconsistent() \
- and expected not in (0, all_num) \
- or sut.head_checkbox_button.get_active() \
- and expected == all_num \
- or not sut.head_checkbox_button.get_active() \
- and expected == 0
+ assert (
+ sut.head_checkbox_button.get_inconsistent()
+ and expected not in (0, all_num)
+ or sut.head_checkbox_button.get_active()
+ and expected == all_num
+ or not sut.head_checkbox_button.get_active()
+ and expected == 0
+ )
sut.on_header_toggled(None)
def test_on_checkbox_toggled(
- real_builder, test_qapp, appvms_list,
- mock_next_button, mock_cancel_button, mock_settings
+ real_builder,
+ test_qapp,
+ appvms_list,
+ mock_next_button,
+ mock_cancel_button,
+ mock_settings,
):
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
sut.list_store = appvms_list
@@ -171,21 +195,32 @@ def test_on_checkbox_toggled(
),
)
def test_populate_restart_list(
- restart_service_vms, restart_other_vms, excluded, expected,
- real_builder, test_qapp, updatable_vms_list,
- mock_next_button, mock_cancel_button, mock_settings, mock_tree_view
+ restart_service_vms,
+ restart_other_vms,
+ excluded,
+ expected,
+ real_builder,
+ test_qapp,
+ updatable_vms_list,
+ mock_next_button,
+ mock_cancel_button,
+ mock_settings,
+ mock_tree_view,
):
mock_settings.restart_other_vms = restart_other_vms
mock_settings.restart_service_vms = restart_service_vms
for exclude in excluded:
test_qapp.expected_calls[
- (exclude, "admin.vm.feature.Get", 'restart-after-update', None)
- ] = b"0\x00" + "".encode()
+ (exclude, "admin.vm.feature.Get", "restart-after-update", None)
+ ] = (b"0\x00" + "".encode())
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
sut.summary_list = mock_tree_view
@@ -204,28 +239,40 @@ def test_populate_restart_list(
assert sum(row.selected for row in sut.list_store) == expected
-@patch('qubes_config.widgets.gtk_utils.show_dialog')
-@patch('qui.updater.summary_page.show_dialog')
-@patch('gi.repository.Gtk.Image.new_from_pixbuf')
-@patch('threading.Thread')
+@patch("qubes_config.widgets.gtk_utils.show_dialog")
+@patch("qui.updater.summary_page.show_dialog")
+@patch("gi.repository.Gtk.Image.new_from_pixbuf")
+@patch("threading.Thread")
@pytest.mark.parametrize(
"alive_requests_max, status",
- (pytest.param(3, RestartStatus.OK),
- pytest.param(0, RestartStatus.OK),
- pytest.param(1, RestartStatus.ERROR_TMPL_DOWN),
- pytest.param(1, RestartStatus.NOTHING_TO_DO),
- ),
+ (
+ pytest.param(3, RestartStatus.OK),
+ pytest.param(0, RestartStatus.OK),
+ pytest.param(1, RestartStatus.ERROR_TMPL_DOWN),
+ pytest.param(1, RestartStatus.NOTHING_TO_DO),
+ ),
)
def test_restart_selected_vms(
- mock_threading, mock_new_from_pixbuf, mock_show_dialog_qui,
- mock_show_dialog, alive_requests_max, status, mock_thread, test_qapp,
- real_builder, mock_next_button, mock_cancel_button
+ mock_threading,
+ mock_new_from_pixbuf,
+ mock_show_dialog_qui,
+ mock_show_dialog,
+ alive_requests_max,
+ status,
+ mock_thread,
+ test_qapp,
+ real_builder,
+ mock_next_button,
+ mock_cancel_button,
):
# ARRANGE
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
mock_thread.alive_requests_max = alive_requests_max
mock_threading.return_value = mock_thread
@@ -285,45 +332,81 @@ def set_deletable(self, deletable):
calls = []
if status == RestartStatus.OK:
- calls = [call(None, "Success",
- "All qubes were restarted/shutdown successfully.",
- RESPONSES_OK, icon)]
+ calls = [
+ call(
+ None,
+ "Success",
+ "All qubes were restarted/shutdown successfully.",
+ RESPONSES_OK,
+ icon,
+ )
+ ]
if status.is_error():
- calls = [call(None, "Failure",
- "During restarting following errors occurs: " + sut.err,
- RESPONSES_OK, icon)]
+ calls = [
+ call(
+ None,
+ "Failure",
+ "During restarting following errors occurs: " + sut.err,
+ RESPONSES_OK,
+ icon,
+ )
+ ]
mock_show_dialog.assert_has_calls(calls)
@patch("qui.updater.summary_page.wait_for_domain_shutdown")
def test_perform_restart(
- _mock_wait_for_domain_shutdown, test_qapp,
- real_builder, mock_next_button, mock_cancel_button, mock_list_store
+ _mock_wait_for_domain_shutdown,
+ test_qapp,
+ real_builder,
+ mock_next_button,
+ mock_cancel_button,
+ mock_list_store,
):
# ARRANGE
- expected_state_calls = [(tmpl, 'admin.vm.CurrentState', None, None)
- for tmpl in ('fedora-35', 'fedora-36')]
+ expected_state_calls = [
+ (tmpl, "admin.vm.CurrentState", None, None)
+ for tmpl in ("fedora-35", "fedora-36")
+ ]
for call_ in expected_state_calls:
- test_qapp.expected_calls[call_] = b'0\x00power_state=Running mem=1024'
-
- to_shutdown = ('fedora-35', 'fedora-36', 'sys-firewall', 'sys-net',
- 'sys-usb', 'test-blue', 'test-red', 'test-vm', 'vault')
- expected_shutdown_calls = [(tmpl, 'admin.vm.Shutdown', 'force', None)
- for tmpl in to_shutdown]
+ test_qapp.expected_calls[call_] = b"0\x00power_state=Running mem=1024"
+
+ to_shutdown = (
+ "fedora-35",
+ "fedora-36",
+ "sys-firewall",
+ "sys-net",
+ "sys-usb",
+ "test-blue",
+ "test-red",
+ "test-vm",
+ "vault",
+ )
+ expected_shutdown_calls = [
+ (tmpl, "admin.vm.Shutdown", "force", None) for tmpl in to_shutdown
+ ]
for call_ in expected_shutdown_calls:
- test_qapp.expected_calls[call_] = b'0\x00'
+ test_qapp.expected_calls[call_] = b"0\x00"
- to_start = ('sys-firewall', 'sys-net', 'sys-usb',)
- expected_start_calls = [(tmpl, 'admin.vm.Start', None, None)
- for tmpl in to_start]
+ to_start = (
+ "sys-firewall",
+ "sys-net",
+ "sys-usb",
+ )
+ expected_start_calls = [
+ (tmpl, "admin.vm.Start", None, None) for tmpl in to_start
+ ]
for call_ in expected_start_calls:
- test_qapp.expected_calls[call_] = b'0\x00'
+ test_qapp.expected_calls[call_] = b"0\x00"
mock_log = Mock()
sut = SummaryPage(
- real_builder, mock_log, mock_next_button, mock_cancel_button,
- back_by_row_selection=lambda *args: None # callback
+ real_builder,
+ mock_log,
+ mock_next_button,
+ mock_cancel_button,
+ back_by_row_selection=lambda *args: None, # callback
)
sut.updated_tmpls = ListWrapper(UpdateRowWrapper, mock_list_store)
@@ -339,6 +422,9 @@ def test_perform_restart(
sut.perform_restart()
# ASSERT
- assert all(item in test_qapp.actual_calls
- for item in expected_state_calls + expected_shutdown_calls
- + expected_start_calls)
+ assert all(
+ item in test_qapp.actual_calls
+ for item in expected_state_calls
+ + expected_shutdown_calls
+ + expected_start_calls
+ )
diff --git a/qui/updater/tests/test_updater.py b/qui/updater/tests/test_updater.py
index 1b78ec83..53c34b04 100644
--- a/qui/updater/tests/test_updater.py
+++ b/qui/updater/tests/test_updater.py
@@ -27,9 +27,9 @@
from qubes_config.widgets import gtk_utils
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
def test_setup(populate_vm_list, _mock_logging, __mock_logging, test_qapp):
sut = QubesUpdater(test_qapp, parse_args((), test_qapp))
sut.perform_setup()
@@ -37,81 +37,97 @@ def test_setup(populate_vm_list, _mock_logging, __mock_logging, test_qapp):
populate_vm_list.assert_has_calls(calls)
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('subprocess.check_output')
-@patch('qui.updater.intro_page.IntroPage.select_rows_ignoring_conditions')
-@patch('qui.updater.intro_page.IntroPage.get_vms_to_update')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("subprocess.check_output")
+@patch("qui.updater.intro_page.IntroPage.select_rows_ignoring_conditions")
+@patch("qui.updater.intro_page.IntroPage.get_vms_to_update")
def test_setup_non_interactive_nothing_to_do(
- get_vms, select, subproc, _mock_logging, __mock_logging, test_qapp):
- sut = QubesUpdater(test_qapp, parse_args(('-n',), test_qapp))
- subproc.return_value = b''
+ get_vms, select, subproc, _mock_logging, __mock_logging, test_qapp
+):
+ sut = QubesUpdater(test_qapp, parse_args(("-n",), test_qapp))
+ subproc.return_value = b""
get_vms.return_value = ()
sut.perform_setup()
select.assert_called_once()
get_vms.assert_called_once()
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
-@patch('qui.updater.intro_page.IntroPage.select_rows')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
+@patch("qui.updater.intro_page.IntroPage.select_rows")
def test_setup_update_if_available(
- select, populate_vm_list, _mock_logging, __mock_logging, test_qapp):
+ select, populate_vm_list, _mock_logging, __mock_logging, test_qapp
+):
sut = QubesUpdater(
- test_qapp, parse_args(('--update-if-available',), test_qapp))
+ test_qapp, parse_args(("--update-if-available",), test_qapp)
+ )
sut.perform_setup()
calls = [call(sut.qapp, sut.settings)]
populate_vm_list.assert_has_calls(calls)
select.assert_called_once()
- assert (sut.intro_page.head_checkbox.state ==
- sut.intro_page.head_checkbox.SAFE)
+ assert (
+ sut.intro_page.head_checkbox.state == sut.intro_page.head_checkbox.SAFE
+ )
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
-@patch('qui.updater.intro_page.IntroPage.select_rows')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
+@patch("qui.updater.intro_page.IntroPage.select_rows")
def test_setup_force_update(
- select, populate_vm_list, _mock_logging, __mock_logging, test_qapp):
- sut = QubesUpdater(
- test_qapp, parse_args(('--force-update',), test_qapp))
+ select, populate_vm_list, _mock_logging, __mock_logging, test_qapp
+):
+ sut = QubesUpdater(test_qapp, parse_args(("--force-update",), test_qapp))
sut.perform_setup()
calls = [call(sut.qapp, sut.settings)]
populate_vm_list.assert_has_calls(calls)
select.assert_called_once()
- assert (sut.intro_page.head_checkbox.state ==
- sut.intro_page.head_checkbox.ALL)
+ assert (
+ sut.intro_page.head_checkbox.state == sut.intro_page.head_checkbox.ALL
+ )
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
-@patch('qui.updater.intro_page.IntroPage.select_rows')
-@patch('qui.updater.updater_settings.get_boolean_feature')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
+@patch("qui.updater.intro_page.IntroPage.select_rows")
+@patch("qui.updater.updater_settings.get_boolean_feature")
@pytest.mark.parametrize(
"args, sys, non_sys",
(
- pytest.param(('--apply-to-all',), True, True, id="all"),
- pytest.param(('--apply-to-sys',), True, None, id="sys"),
- pytest.param(('--no-apply',), False, False, id="none"),
- )
+ pytest.param(("--apply-to-all",), True, True, id="all"),
+ pytest.param(("--apply-to-sys",), True, None, id="sys"),
+ pytest.param(("--no-apply",), False, False, id="none"),
+ ),
)
def test_setup_apply(
- get_feature, __select, populate_vm_list, _mock_logging, __mock_logging, test_qapp, args, sys, non_sys):
- sut = QubesUpdater(
- test_qapp, parse_args(args, test_qapp))
+ get_feature,
+ __select,
+ populate_vm_list,
+ _mock_logging,
+ __mock_logging,
+ test_qapp,
+ args,
+ sys,
+ non_sys,
+):
+ sut = QubesUpdater(test_qapp, parse_args(args, test_qapp))
sut.perform_setup()
calls = [call(sut.qapp, sut.settings)]
populate_vm_list.assert_has_calls(calls)
assert sut.settings.restart_service_vms == sys
- assert (non_sys is not None and sut.settings.restart_other_vms == non_sys
- or sut.settings.overrides.apply_to_other is None)
+ assert (
+ non_sys is not None
+ and sut.settings.restart_other_vms == non_sys
+ or sut.settings.overrides.apply_to_other is None
+ )
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
@pytest.mark.parametrize(
"update_results, ret_code",
(
@@ -126,10 +142,16 @@ def test_setup_apply(
pytest.param((1, 0, 1, 0), 1, id="success + failed"),
pytest.param((1, 1, 0, 0), 0, id="success + no updated"),
pytest.param((1, 1, 1, 1), 130, id="all"),
- )
+ ),
)
-def test_retcode(_populate_vm_list, _mock_logging, __mock_logging,
- update_results, ret_code, test_qapp):
+def test_retcode(
+ _populate_vm_list,
+ _mock_logging,
+ __mock_logging,
+ update_results,
+ ret_code,
+ test_qapp,
+):
sut = QubesUpdater(test_qapp, parse_args((), test_qapp))
sut.perform_setup()
@@ -139,6 +161,7 @@ def test_retcode(_populate_vm_list, _mock_logging, __mock_logging,
def set_vms(_vms_to_update, _settings):
sut.progress_page.vms_to_update = _vms_to_update
+
sut.progress_page.init_update = Mock(side_effect=set_vms)
sut.next_clicked(None)
@@ -146,12 +169,15 @@ def set_vms(_vms_to_update, _settings):
assert not sut.intro_page.active
assert sut.progress_page.is_visible
sut.progress_page.init_update.assert_called_once_with(
- vms_to_update, sut.settings)
+ vms_to_update, sut.settings
+ )
# set sut.summary_page.is_populated = False
sut.summary_page.list_store = None
+
def populate(**_kwargs):
sut.summary_page.list_store = []
+
sut.summary_page.populate_restart_list = Mock(side_effect=populate)
sut.progress_page.retcode = ret_code
sut.progress_page.get_update_summary = Mock()
@@ -162,21 +188,33 @@ def populate(**_kwargs):
sut.next_clicked(None)
sut.summary_page.populate_restart_list.assert_called_once_with(
- restart=True, vm_updated=vms_to_update, settings=sut.settings)
+ restart=True, vm_updated=vms_to_update, settings=sut.settings
+ )
assert sut.retcode == ret_code
- expected_summary = (update_results[0], update_results[1],
- update_results[2] + update_results[3])
+ expected_summary = (
+ update_results[0],
+ update_results[1],
+ update_results[2] + update_results[3],
+ )
sut.summary_page.show.assert_called_once_with(*expected_summary)
-@patch('threading.Thread')
-@patch('qui.updater.updater.show_dialog_with_icon')
-@patch('qui.updater.summary_page.show_dialog_with_icon')
-@patch('logging.FileHandler')
-@patch('logging.getLogger')
-@patch('qui.updater.intro_page.IntroPage.populate_vm_list')
-def test_dialog(_populate_vm_list, _mock_logging, __mock_logging,
- dialog, dialog2, thread, test_qapp, monkeypatch):
+@patch("threading.Thread")
+@patch("qui.updater.updater.show_dialog_with_icon")
+@patch("qui.updater.summary_page.show_dialog_with_icon")
+@patch("logging.FileHandler")
+@patch("logging.getLogger")
+@patch("qui.updater.intro_page.IntroPage.populate_vm_list")
+def test_dialog(
+ _populate_vm_list,
+ _mock_logging,
+ __mock_logging,
+ dialog,
+ dialog2,
+ thread,
+ test_qapp,
+ monkeypatch,
+):
monkeypatch.setattr(SummaryPage, "perform_restart", lambda *_: None)
sut = QubesUpdater(test_qapp, parse_args((), test_qapp))
sut.perform_setup()
@@ -189,6 +227,7 @@ def test_dialog(_populate_vm_list, _mock_logging, __mock_logging,
def set_vms(_vms_to_update, _settings):
sut.progress_page.vms_to_update = _vms_to_update
+
sut.progress_page.init_update = Mock(side_effect=set_vms)
sut.next_clicked(None)
@@ -196,12 +235,15 @@ def set_vms(_vms_to_update, _settings):
assert not sut.intro_page.active
assert sut.progress_page.is_visible
sut.progress_page.init_update.assert_called_once_with(
- vms_to_update, sut.settings)
+ vms_to_update, sut.settings
+ )
# set sut.summary_page.is_populated = False
sut.summary_page.list_store = None
+
def populate(**_kwargs):
sut.summary_page.list_store = []
+
sut.summary_page.populate_restart_list = Mock(side_effect=populate)
sut.progress_page.get_update_summary = Mock()
sut.progress_page.get_update_summary.return_value = (1, 0, 0, 0)
@@ -213,12 +255,21 @@ def ok(**_kwargs):
t = Mock()
t.is_alive = Mock(return_value=False)
return t
+
thread.side_effect = ok
sut.summary_page.status = RestartStatus.OK
sut.next_clicked(None)
- dialog2.assert_has_calls(calls=[call(
- None, "Success", "Qubes OS is up to date.",
- buttons=gtk_utils.RESPONSES_OK, icon_name="qubes-check-yes")])
+ dialog2.assert_has_calls(
+ calls=[
+ call(
+ None,
+ "Success",
+ "Qubes OS is up to date.",
+ buttons=gtk_utils.RESPONSES_OK,
+ icon_name="qubes-check-yes",
+ )
+ ]
+ )
dialog.assert_not_called()
diff --git a/qui/updater/tests/test_updater_settings.py b/qui/updater/tests/test_updater_settings.py
index e2989bdf..18c3f6b4 100644
--- a/qui/updater/tests/test_updater_settings.py
+++ b/qui/updater/tests/test_updater_settings.py
@@ -23,7 +23,7 @@
import gi
import pytest
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk
from qui.updater.updater_settings import Settings
@@ -31,15 +31,28 @@
def init_features(test_qapp):
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-servicevms', None)
- ] = b"0\x00" + str(1).encode()
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-restart-servicevms",
+ None,
+ )
+ ] = (
+ b"0\x00" + str(1).encode()
+ )
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-other', None)
- ] = b"0\x00" + str(1).encode()
+ ("dom0", "admin.vm.feature.Get", "qubes-vm-update-restart-other", None)
+ ] = (b"0\x00" + str(1).encode())
test_qapp.expected_calls[
(
- 'dom0', 'admin.vm.feature.Get', 'qubes-vm-update-max-concurrency', None)
- ] = b"0\x00" + str(1).encode()
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-max-concurrency",
+ None,
+ )
+ ] = (
+ b"0\x00" + str(1).encode()
+ )
def test_show_and_hide(test_qapp):
@@ -55,14 +68,28 @@ def test_update_if_stale(test_qapp):
sut = Settings(Gtk.Window(), test_qapp, mock_log, lambda *args: None)
assert sut.update_if_stale == 7
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-update-if-stale', None)
- ] = b"0\x00" + str(32).encode()
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-update-if-stale",
+ None,
+ )
+ ] = (
+ b"0\x00" + str(32).encode()
+ )
assert sut.update_if_stale == 32
test_qapp.expected_calls[
(
- 'dom0', 'admin.vm.feature.Get', 'qubes-vm-update-update-if-stale', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-update-if-stale' + b'\x00'
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-update-if-stale",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-update-if-stale"
+ + b"\x00"
+ )
assert sut.update_if_stale == 7
@@ -70,23 +97,48 @@ def test_restart_service_vms(test_qapp):
mock_log = Mock()
sut = Settings(Gtk.Window(), test_qapp, mock_log, lambda *args: None)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-servicevms', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-restart-servicevms' + b'\x00'
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-restart-servicevms",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-restart-servicevms"
+ + b"\x00"
+ )
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-system', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-restart-system' + b'\x00'
+ ("dom0", "admin.vm.feature.Get", "qubes-vm-update-restart-system", None)
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-restart-system"
+ + b"\x00"
+ )
assert sut.restart_service_vms
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-servicevms', None)
- ] = b"0\x00" + ''.encode()
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-restart-servicevms",
+ None,
+ )
+ ] = (
+ b"0\x00" + "".encode()
+ )
assert not sut.restart_service_vms
test_qapp.expected_calls[
(
- 'dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-servicevms', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-restart-servicevms' + b'\x00'
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-restart-servicevms",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-restart-servicevms"
+ + b"\x00"
+ )
assert sut.restart_service_vms
@@ -94,19 +146,24 @@ def test_restart_other_vms(test_qapp):
mock_log = Mock()
sut = Settings(Gtk.Window(), test_qapp, mock_log, lambda *args: None)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-other', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-restart-other' + b'\x00'
+ ("dom0", "admin.vm.feature.Get", "qubes-vm-update-restart-other", None)
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-restart-other"
+ + b"\x00"
+ )
assert not sut.restart_other_vms
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-other', None)
- ] = b"0\x00" + '1'.encode()
+ ("dom0", "admin.vm.feature.Get", "qubes-vm-update-restart-other", None)
+ ] = (b"0\x00" + "1".encode())
assert sut.restart_other_vms
test_qapp.expected_calls[
- (
- 'dom0', 'admin.vm.feature.Get', 'qubes-vm-update-restart-other', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-restart-other' + b'\x00'
+ ("dom0", "admin.vm.feature.Get", "qubes-vm-update-restart-other", None)
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-restart-other"
+ + b"\x00"
+ )
assert not sut.restart_other_vms
@@ -114,21 +171,41 @@ def test_max_concurrency(test_qapp):
mock_log = Mock()
sut = Settings(Gtk.Window(), test_qapp, mock_log, lambda *args: None)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get',
- 'qubes-vm-update-max-concurrency', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-max-concurrency' + b'\x00'
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-max-concurrency",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-max-concurrency"
+ + b"\x00"
+ )
assert sut.max_concurrency is None
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get',
- 'qubes-vm-update-max-concurrency', None)
- ] = b"0\x00" + '8'.encode()
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-max-concurrency",
+ None,
+ )
+ ] = (
+ b"0\x00" + "8".encode()
+ )
assert sut.max_concurrency == 8
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get',
- 'qubes-vm-update-max-concurrency', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + b'qubes-vm-update-max-concurrency' + b'\x00'
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-max-concurrency",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + b"qubes-vm-update-max-concurrency"
+ + b"\x00"
+ )
assert sut.max_concurrency is None
@@ -145,12 +222,24 @@ def __call__(self, value):
@pytest.mark.parametrize(
"feature, default_value, new_value, button_name",
(
- pytest.param("update-if-stale", Settings.DEFAULT_UPDATE_IF_STALE, 30,
- "days_without_update_button"),
- pytest.param("restart-servicevms", Settings.DEFAULT_RESTART_SERVICEVMS,
- False, "restart_servicevms_checkbox"),
- pytest.param("restart-other", Settings.DEFAULT_RESTART_OTHER_VMS,
- True, "restart_other_checkbox"),
+ pytest.param(
+ "update-if-stale",
+ Settings.DEFAULT_UPDATE_IF_STALE,
+ 30,
+ "days_without_update_button",
+ ),
+ pytest.param(
+ "restart-servicevms",
+ Settings.DEFAULT_RESTART_SERVICEVMS,
+ False,
+ "restart_servicevms_checkbox",
+ ),
+ pytest.param(
+ "restart-other",
+ Settings.DEFAULT_RESTART_OTHER_VMS,
+ True,
+ "restart_other_checkbox",
+ ),
),
)
def test_save(feature, default_value, new_value, test_qapp, button_name):
@@ -162,10 +251,17 @@ def test_save(feature, default_value, new_value, test_qapp, button_name):
# set the backwards-compatible feature appropriately
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get',
- f'qubes-vm-update-restart-system', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + f'qubes-vm-update-restart-system'.encode() + b'\x00'
+ (
+ "dom0",
+ "admin.vm.feature.Get",
+ f"qubes-vm-update-restart-system",
+ None,
+ )
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + f"qubes-vm-update-restart-system".encode()
+ + b"\x00"
+ )
sut.show()
sut.save_and_close(None)
@@ -179,19 +275,23 @@ def test_save(feature, default_value, new_value, test_qapp, button_name):
button = getattr(sut, button_name)
if button_name.endswith("checkbox"):
button.set_active(new_value)
- new_value_str = '1' if new_value else ''
+ new_value_str = "1" if new_value else ""
else:
button.set_value(new_value)
new_value_str = str(new_value)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Set',
- f'qubes-vm-update-{feature}', new_value_str.encode())
- ] = b'0\x00'
+ (
+ "dom0",
+ "admin.vm.feature.Set",
+ f"qubes-vm-update-{feature}",
+ new_value_str.encode(),
+ )
+ ] = b"0\x00"
sut.save_and_close(None)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get', f'qubes-vm-update-{feature}', None)
- ] = b"0\x00" + str(new_value).encode()
+ ("dom0", "admin.vm.feature.Get", f"qubes-vm-update-{feature}", None)
+ ] = (b"0\x00" + str(new_value).encode())
assert mock_callback.call_num == 2
if feature == "update-if-stale":
@@ -204,23 +304,25 @@ def test_save(feature, default_value, new_value, test_qapp, button_name):
else:
button.set_value(default_value)
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Set',
- f'qubes-vm-update-{feature}', None)
- ] = b'0\x00'
+ ("dom0", "admin.vm.feature.Set", f"qubes-vm-update-{feature}", None)
+ ] = b"0\x00"
sut.save_and_close(None)
assert mock_callback.call_num == 3
if feature == "update-if-stale":
assert mock_callback.value == new_value
test_qapp.expected_calls[
- ('dom0', 'admin.vm.feature.Get',
- f'qubes-vm-update-{feature}', None)
- ] = b'2\x00QubesFeatureNotFoundError\x00\x00' \
- + f'qubes-vm-update-{feature}'.encode() + b'\x00'
+ ("dom0", "admin.vm.feature.Get", f"qubes-vm-update-{feature}", None)
+ ] = (
+ b"2\x00QubesFeatureNotFoundError\x00\x00"
+ + f"qubes-vm-update-{feature}".encode()
+ + b"\x00"
+ )
# do not set adminVM feature if nothing change
sut.show()
- del test_qapp.expected_calls[('dom0', 'admin.vm.feature.Set',
- f'qubes-vm-update-{feature}', None)]
+ del test_qapp.expected_calls[
+ ("dom0", "admin.vm.feature.Set", f"qubes-vm-update-{feature}", None)
+ ]
if button_name.endswith("checkbox"):
button.set_active(default_value)
else:
@@ -232,10 +334,17 @@ def test_save(feature, default_value, new_value, test_qapp, button_name):
def test_limit_concurrency(test_qapp):
- dom0_set_max_concurrency = ('dom0', 'admin.vm.feature.Set',
- 'qubes-vm-update-max-concurrency',)
- dom0_get_max_concurrency = ('dom0', 'admin.vm.feature.Get',
- 'qubes-vm-update-max-concurrency', None)
+ dom0_set_max_concurrency = (
+ "dom0",
+ "admin.vm.feature.Set",
+ "qubes-vm-update-max-concurrency",
+ )
+ dom0_get_max_concurrency = (
+ "dom0",
+ "admin.vm.feature.Get",
+ "qubes-vm-update-max-concurrency",
+ None,
+ )
mock_log = Mock()
sut = Settings(Gtk.Window(), test_qapp, mock_log, lambda *args: None)
@@ -249,20 +358,22 @@ def test_limit_concurrency(test_qapp):
sut.limit_concurrency_checkbox.set_active(True)
test_qapp.expected_calls[
(*dom0_set_max_concurrency, sut.DEFAULT_CONCURRENCY)
- ] = b'0\x00'
+ ] = b"0\x00"
sut.save_and_close(None)
- test_qapp.expected_calls[dom0_get_max_concurrency] = \
+ test_qapp.expected_calls[dom0_get_max_concurrency] = (
b"0\x00" + str(sut.DEFAULT_CONCURRENCY).encode()
+ )
# Set concurrency to max value
sut.show()
sut.max_concurrency_button.set_value(sut.MAX_CONCURRENCY)
test_qapp.expected_calls[
(*dom0_set_max_concurrency, str(sut.MAX_CONCURRENCY).encode())
- ] = b'0\x00'
+ ] = b"0\x00"
sut.save_and_close(None)
- test_qapp.expected_calls[dom0_get_max_concurrency] = \
+ test_qapp.expected_calls[dom0_get_max_concurrency] = (
b"0\x00" + str(sut.MAX_CONCURRENCY).encode()
+ )
# Set concurrency to max value again
sut.show()
@@ -275,10 +386,5 @@ def test_limit_concurrency(test_qapp):
# Set False
sut.show()
sut.limit_concurrency_checkbox.set_active(False)
- test_qapp.expected_calls[
- (*dom0_set_max_concurrency, None)
- ] = b'0\x00'
+ test_qapp.expected_calls[(*dom0_set_max_concurrency, None)] = b"0\x00"
sut.save_and_close(None)
-
-
-
diff --git a/qui/updater/tests/test_utils.py b/qui/updater/tests/test_utils.py
index 54d5b561..321ebe74 100644
--- a/qui/updater/tests/test_utils.py
+++ b/qui/updater/tests/test_utils.py
@@ -20,22 +20,30 @@
from qui.utils import check_support
from qubesadmin.tests.mock_app import MockQubes, MockQube
+
def test_check_support():
qapp = MockQubes()
- vm_supported = MockQube("test-qube-1", qapp,
- features={"os-eol": "2060-01-01"})
- vm_not_supported = MockQube("test-qube-2", qapp,
- features={"os-eol": "1990-01-01"})
- fedora_min = MockQube("test-qube-3", qapp,
- features={"template-name": "fedora-36-minimal"})
- fedora_xfce = MockQube("test-qube-4", qapp,
- features={"template-name": "fedora-35-xfce"})
- wrong_name = MockQube("test-qube-5", qapp,
- features={"template-name": "faedora-66"})
- debian_minimal = MockQube("test-qube-6", qapp,
- features={"template-name": "debian-9-minimal"})
- normal_debian = MockQube("test-qube-7", qapp,
- features={"template-name": "debian-8"})
+ vm_supported = MockQube(
+ "test-qube-1", qapp, features={"os-eol": "2060-01-01"}
+ )
+ vm_not_supported = MockQube(
+ "test-qube-2", qapp, features={"os-eol": "1990-01-01"}
+ )
+ fedora_min = MockQube(
+ "test-qube-3", qapp, features={"template-name": "fedora-36-minimal"}
+ )
+ fedora_xfce = MockQube(
+ "test-qube-4", qapp, features={"template-name": "fedora-35-xfce"}
+ )
+ wrong_name = MockQube(
+ "test-qube-5", qapp, features={"template-name": "faedora-66"}
+ )
+ debian_minimal = MockQube(
+ "test-qube-6", qapp, features={"template-name": "debian-9-minimal"}
+ )
+ normal_debian = MockQube(
+ "test-qube-7", qapp, features={"template-name": "debian-8"}
+ )
nothing_special = MockQube("test-qube-8", qapp)
qapp.update_vm_calls()
@@ -49,4 +57,3 @@ def test_check_support():
assert not check_support(debian_minimal)
assert not check_support(normal_debian)
assert check_support(nothing_special)
-
diff --git a/qui/updater/updater.py b/qui/updater/updater.py
index 47443f36..fe231664 100644
--- a/qui/updater/updater.py
+++ b/qui/updater/updater.py
@@ -9,15 +9,19 @@
import importlib.resources
import gi # isort:skip
-from qubes_config.widgets.gtk_utils import load_icon_at_gtk_size, load_theme, \
- show_dialog_with_icon, RESPONSES_OK
+from qubes_config.widgets.gtk_utils import (
+ load_icon_at_gtk_size,
+ load_theme,
+ show_dialog_with_icon,
+ RESPONSES_OK,
+)
from qui.updater.progress_page import ProgressPage
from qui.updater.updater_settings import Settings, OverriddenSettings
from qui.updater.summary_page import SummaryPage
from qui.updater.intro_page import IntroPage
import qui.updater.utils
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, Gdk, Gio # isort:skip
from qubesadmin import Qubes
import qubesadmin.exc
@@ -28,7 +32,7 @@
from locale import gettext as l
locale.bindtextdomain("desktop-linux-manager", "/usr/locales/")
-locale.textdomain('desktop-linux-manager')
+locale.textdomain("desktop-linux-manager")
class ArgumentError(Exception):
@@ -38,13 +42,13 @@ class ArgumentError(Exception):
class QubesUpdater(Gtk.Application):
# pylint: disable=too-many-instance-attributes
- LOGPATH = '/var/log/qubes/qui.updater.log'
- LOG_FORMAT = '%(asctime)s %(message)s'
+ LOGPATH = "/var/log/qubes/qui.updater.log"
+ LOG_FORMAT = "%(asctime)s %(message)s"
def __init__(self, qapp, cliargs):
super().__init__(
application_id="org.gnome.example",
- flags=Gio.ApplicationFlags.FLAGS_NONE
+ flags=Gio.ApplicationFlags.FLAGS_NONE,
)
self.qapp = qapp
self.primary = False
@@ -54,11 +58,12 @@ def __init__(self, qapp, cliargs):
self.retcode = 0
log_handler = logging.FileHandler(
- QubesUpdater.LOGPATH, encoding='utf-8')
+ QubesUpdater.LOGPATH, encoding="utf-8"
+ )
log_formatter = logging.Formatter(QubesUpdater.LOG_FORMAT)
log_handler.setFormatter(log_formatter)
- self.log = logging.getLogger('vm-update.agent.PackageManager')
+ self.log = logging.getLogger("vm-update.agent.PackageManager")
self.log.addHandler(log_handler)
self.log.setLevel(self.cliargs.log)
@@ -82,8 +87,7 @@ def perform_setup(self, *_args, **_kwargs):
self.builder = Gtk.Builder()
self.builder.set_translation_domain("desktop-linux-manager")
- glade_ref = (importlib.resources.files('qui') /
- 'updater.glade')
+ glade_ref = importlib.resources.files("qui") / "updater.glade"
with importlib.resources.as_file(glade_ref) as path:
self.builder.add_from_file(str(path))
@@ -91,13 +95,16 @@ def perform_setup(self, *_args, **_kwargs):
self.next_button: Gtk.Button = self.builder.get_object("button_next")
self.next_button.connect("clicked", self.next_clicked)
self.cancel_button: Gtk.Button = self.builder.get_object(
- "button_cancel")
+ "button_cancel"
+ )
self.cancel_button.connect("clicked", self.cancel_clicked)
- self.EffectiveCssProvider = load_theme(widget=self.main_window,
- package_name='qui',
- light_file_name='qubes-updater-light.css',
- dark_file_name='qubes-updater-dark.css')
+ self.EffectiveCssProvider = load_theme(
+ widget=self.main_window,
+ package_name="qui",
+ light_file_name="qubes-updater-light.css",
+ dark_file_name="qubes-updater-dark.css",
+ )
qui.updater.utils.SetEffectiveCssProvider(self.EffectiveCssProvider)
self.header_label: Gtk.Label = self.builder.get_object("header_label")
@@ -109,22 +116,27 @@ def perform_setup(self, *_args, **_kwargs):
self.header_label,
self.next_button,
self.cancel_button,
- callback=lambda: self.next_clicked(None)
- if self.cliargs.non_interactive else lambda: None
+ callback=lambda: (
+ self.next_clicked(None)
+ if self.cliargs.non_interactive
+ else lambda: None
+ ),
)
self.summary_page = SummaryPage(
self.builder,
self.log,
self.next_button,
self.cancel_button,
- self.progress_page.back_by_row_selection
+ self.progress_page.back_by_row_selection,
)
self.button_settings: Gtk.Button = self.builder.get_object(
- "button_settings")
+ "button_settings"
+ )
self.button_settings.connect("clicked", self.open_settings_window)
settings_pixbuf = load_icon_at_gtk_size(
- 'qubes-customize', Gtk.IconSize.LARGE_TOOLBAR)
+ "qubes-customize", Gtk.IconSize.LARGE_TOOLBAR
+ )
settings_image = Gtk.Image.new_from_pixbuf(settings_pixbuf)
self.button_settings.set_image(settings_image)
@@ -154,9 +166,16 @@ def perform_setup(self, *_args, **_kwargs):
overrides=overrides,
)
- headers = [(3, "intro_name"), (3, "progress_name"), (3, "summary_name"),
- (3, "restart_name"), (4, "available"), (5, "check"),
- (6, "update"), (8, "summary_status")]
+ headers = [
+ (3, "intro_name"),
+ (3, "progress_name"),
+ (3, "summary_name"),
+ (3, "restart_name"),
+ (4, "available"),
+ (5, "check"),
+ (6, "update"),
+ (8, "summary_status"),
+ ]
def cell_data_func(_column, cell, model, it, data):
# Get the object from the model
@@ -166,9 +185,11 @@ def cell_data_func(_column, cell, model, it, data):
for col, name in headers:
renderer: Gtk.CellRenderer = self.builder.get_object(
- name + "_renderer")
+ name + "_renderer"
+ )
column: Gtk.TreeViewColumn = self.builder.get_object(
- name + "_column")
+ name + "_column"
+ )
column.set_cell_data_func(renderer, cell_data_func, col)
renderer.props.ypad = 10
if not name.endswith("name") and name != "summary_status":
@@ -183,7 +204,8 @@ def cell_data_func(_column, cell, model, it, data):
if skip_intro_if_args(self.cliargs):
self.log.info("Skipping intro page.")
self.intro_page.select_rows_ignoring_conditions(
- cliargs=self.cliargs, dom0=self.qapp.domains['dom0'])
+ cliargs=self.cliargs, dom0=self.qapp.domains["dom0"]
+ )
if len(self.intro_page.get_vms_to_update()) == 0:
self.do_nothing = True
return
@@ -192,17 +214,20 @@ def cell_data_func(_column, cell, model, it, data):
# default update_if_stale -> do nothing
if self.cliargs.update_if_available:
self.intro_page.head_checkbox.state = (
- self.intro_page.head_checkbox.SAFE)
+ self.intro_page.head_checkbox.SAFE
+ )
self.intro_page.select_rows()
elif self.cliargs.force_update:
self.intro_page.head_checkbox.state = (
- self.intro_page.head_checkbox.ALL)
+ self.intro_page.head_checkbox.ALL
+ )
self.intro_page.select_rows()
self.log.info("Show intro page.")
self.main_window.show_all()
width = self.intro_page.vm_list.get_preferred_width().natural_width
- height = min(int(width * 1.2),
- self.main_window.get_screen().get_height() - 48)
+ height = min(
+ int(width * 1.2), self.main_window.get_screen().get_height() - 48
+ )
self.main_window.resize(width + 50, height)
self.main_window.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
@@ -221,10 +246,11 @@ def next_clicked(self, _emitter, skip_intro=False):
self.summary_page.populate_restart_list(
restart=True,
vm_updated=self.progress_page.vms_to_update,
- settings=self.settings
+ settings=self.settings,
)
updated, no_updates, failed, cancelled = (
- self.progress_page.get_update_summary())
+ self.progress_page.get_update_summary()
+ )
if updated == 0:
# no updates
self.retcode = 100
@@ -240,7 +266,8 @@ def next_clicked(self, _emitter, skip_intro=False):
else:
# at this point retcode is in (0, 100)
self._restart_phase(
- show_only_error=self.cliargs.non_interactive)
+ show_only_error=self.cliargs.non_interactive
+ )
# at thi point retcode is in (0, 100)
# or an error message have been already shown
if self.cliargs.non_interactive and self.retcode in (0, 100):
@@ -267,16 +294,17 @@ def _show_success_dialog(self):
we should show some feedback to the user.
"""
non_default_select = any(
- (getattr(self.cliargs, arg)
- for arg in self.cliargs.non_default_select if arg != 'all'))
+ (
+ getattr(self.cliargs, arg)
+ for arg in self.cliargs.non_default_select
+ if arg != "all"
+ )
+ )
msg = "Nothing to do."
- if (
- ( # at least all vms with available updates was updated
- (self.cliargs.all and not self.cliargs.skip)
- or not non_default_select
- )
- and self.retcode in (0, 100)
- ):
+ if ( # at least all vms with available updates was updated
+ (self.cliargs.all and not self.cliargs.skip)
+ or not non_default_select
+ ) and self.retcode in (0, 100):
msg = "Qubes OS is up to date."
elif self.retcode == 0:
msg = "All selected qubes have been updated."
@@ -287,7 +315,7 @@ def _show_success_dialog(self):
l("Success"),
l(msg),
buttons=RESPONSES_OK,
- icon_name="qubes-check-yes"
+ icon_name="qubes-check-yes",
)
def cancel_clicked(self, _emitter):
@@ -301,14 +329,22 @@ def cancel_clicked(self, _emitter):
def cancel_updates(self, *_args, **_kwargs):
self.log.info("User initialize interruption")
- if self.progress_page.update_thread \
- and self.progress_page.update_thread.is_alive():
+ if (
+ self.progress_page.update_thread
+ and self.progress_page.update_thread.is_alive()
+ ):
self.progress_page.interrupt_update()
self.log.info("Update interrupted")
- show_dialog_with_icon(self.main_window, l("Updating cancelled"), l(
- "Waiting for current qube to finish updating."
- " Updates for remaining qubes have been cancelled."),
- buttons=RESPONSES_OK, icon_name="qubes-info")
+ show_dialog_with_icon(
+ self.main_window,
+ l("Updating cancelled"),
+ l(
+ "Waiting for current qube to finish updating."
+ " Updates for remaining qubes have been cancelled."
+ ),
+ buttons=RESPONSES_OK,
+ icon_name="qubes-info",
+ )
self.log.debug("Waiting to finish ongoing updates")
while self.progress_page.update_thread.is_alive():
@@ -338,90 +374,142 @@ def exit_updater(self, _emitter=None):
def parse_args(args, app):
parser = argparse.ArgumentParser()
try:
- default_update_if_stale = int(app.domains["dom0"].features.get(
- "qubes-vm-update-update-if-stale", Settings.DEFAULT_UPDATE_IF_STALE)
+ default_update_if_stale = int(
+ app.domains["dom0"].features.get(
+ "qubes-vm-update-update-if-stale",
+ Settings.DEFAULT_UPDATE_IF_STALE,
+ )
)
except qubesadmin.exc.QubesDaemonAccessError:
default_update_if_stale = Settings.DEFAULT_UPDATE_IF_STALE
- parser.add_argument('--log', action='store', default='WARNING',
- help='Provide logging level. Values: DEBUG, INFO, '
- 'WARNING (default), ERROR, CRITICAL')
+ parser.add_argument(
+ "--log",
+ action="store",
+ default="WARNING",
+ help="Provide logging level. Values: DEBUG, INFO, "
+ "WARNING (default), ERROR, CRITICAL",
+ )
- parser.add_argument('--max-concurrency', action='store',
- help='Maximum number of VMs configured simultaneously '
- '(default: number of cpus)',
- type=int)
parser.add_argument(
- '--signal-no-updates', action='store_true',
- help='Return exit code 100 instread of 0 '
- 'if there is no updates available.')
+ "--max-concurrency",
+ action="store",
+ help="Maximum number of VMs configured simultaneously "
+ "(default: number of cpus)",
+ type=int,
+ )
+ parser.add_argument(
+ "--signal-no-updates",
+ action="store_true",
+ help="Return exit code 100 instread of 0 "
+ "if there is no updates available.",
+ )
restart = parser.add_mutually_exclusive_group()
restart.add_argument(
- '--apply-to-sys', '--restart', '-r',
- action='store_true',
- help='Restart not updated ServiceVMs whose template has been updated.')
+ "--apply-to-sys",
+ "--restart",
+ "-r",
+ action="store_true",
+ help="Restart not updated ServiceVMs whose template has been updated.",
+ )
restart.add_argument(
- '--apply-to-all', '-R', action='store_true',
- help='Restart not updated ServiceVMs and shutdown not updated AppVMs '
- 'whose template has been updated.')
+ "--apply-to-all",
+ "-R",
+ action="store_true",
+ help="Restart not updated ServiceVMs and shutdown not updated AppVMs "
+ "whose template has been updated.",
+ )
restart.add_argument(
- '--no-apply', action='store_true',
- help='DEFAULT. Do not restart/shutdown any AppVMs.')
+ "--no-apply",
+ action="store_true",
+ help="DEFAULT. Do not restart/shutdown any AppVMs.",
+ )
update_state = parser.add_mutually_exclusive_group()
update_state.add_argument(
- '--force-update', action='store_true',
- help='Attempt to update all targeted VMs '
- 'even if no updates are available')
+ "--force-update",
+ action="store_true",
+ help="Attempt to update all targeted VMs "
+ "even if no updates are available",
+ )
update_state.add_argument(
- '--update-if-stale', action='store',
- help='DEFAULT. '
- 'Attempt to update targeted VMs with known updates available '
- 'or for which last update check was more than N days ago. '
- '(default: %(default)d)',
- type=int, default=default_update_if_stale)
+ "--update-if-stale",
+ action="store",
+ help="DEFAULT. "
+ "Attempt to update targeted VMs with known updates available "
+ "or for which last update check was more than N days ago. "
+ "(default: %(default)d)",
+ type=int,
+ default=default_update_if_stale,
+ )
update_state.add_argument(
- '--update-if-available', action='store_true',
- help='Update targeted VMs with known updates available.')
+ "--update-if-available",
+ action="store_true",
+ help="Update targeted VMs with known updates available.",
+ )
parser.add_argument(
- '--skip', action='store',
- help='Comma separated list of VMs to be skipped, '
- 'works with all other options. '
- 'If present, skip manual selection of qubes to update.',
- default="")
+ "--skip",
+ action="store",
+ help="Comma separated list of VMs to be skipped, "
+ "works with all other options. "
+ "If present, skip manual selection of qubes to update.",
+ default="",
+ )
parser.add_argument(
- '--targets', action='store',
- help='Comma separated list of updatable VMs to target. '
- 'If present, skip manual selection of qubes to update.')
+ "--targets",
+ action="store",
+ help="Comma separated list of updatable VMs to target. "
+ "If present, skip manual selection of qubes to update.",
+ )
parser.add_argument(
- '--templates', '-T', action='store_true',
- help='Target all updatable TemplateVMs. '
- 'If present, skip manual selection of qubes to update.')
+ "--templates",
+ "-T",
+ action="store_true",
+ help="Target all updatable TemplateVMs. "
+ "If present, skip manual selection of qubes to update.",
+ )
parser.add_argument(
- '--standalones', '-S', action='store_true',
- help='Target all updatable StandaloneVMs. '
- 'If present, skip manual selection of qubes to update.')
+ "--standalones",
+ "-S",
+ action="store_true",
+ help="Target all updatable StandaloneVMs. "
+ "If present, skip manual selection of qubes to update.",
+ )
parser.add_argument(
- '--all', action='store_true',
- help='DEFAULT. Target AdminVM, TemplateVMs and StandaloneVMs.'
- 'Use explicitly with "--targets" to include both. '
- 'If explicitly present, skip manual selection of qubes to update.')
+ "--all",
+ action="store_true",
+ help="DEFAULT. Target AdminVM, TemplateVMs and StandaloneVMs."
+ 'Use explicitly with "--targets" to include both. '
+ "If explicitly present, skip manual selection of qubes to update.",
+ )
parser.add_argument(
- '--dom0', action='store_true', help='Target dom0. '
- 'If present, skip manual selection of qubes to update.')
+ "--dom0",
+ action="store_true",
+ help="Target dom0. "
+ "If present, skip manual selection of qubes to update.",
+ )
- parser.add_argument('--non-interactive', '-n', action='store_true',
- help='Run the updater GUI in non-interactive mode. '
- 'Interaction will be required in the event '
- 'of an update error.')
+ parser.add_argument(
+ "--non-interactive",
+ "-n",
+ action="store_true",
+ help="Run the updater GUI in non-interactive mode. "
+ "Interaction will be required in the event "
+ "of an update error.",
+ )
args = parser.parse_args(args)
args.non_default_select = {
- 'skip', 'targets', 'templates', 'standalones', 'all', 'dom0'}
+ "skip",
+ "targets",
+ "templates",
+ "standalones",
+ "all",
+ "dom0",
+ }
if args.update_if_stale < 0:
raise ArgumentError("Wrong value for --update-if-stale")
@@ -432,8 +520,9 @@ def parse_args(args, app):
def skip_intro_if_args(args):
- auto_select = [getattr(args, arg) for arg in args.non_default_select
- ] + [args.non_interactive]
+ auto_select = [getattr(args, arg) for arg in args.non_default_select] + [
+ args.non_interactive
+ ]
return any(auto_select)
@@ -447,5 +536,5 @@ def main(args=None):
sys.exit(app.retcode)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/qui/updater/updater_settings.py b/qui/updater/updater_settings.py
index 612b8bbd..92838519 100644
--- a/qui/updater/updater_settings.py
+++ b/qui/updater/updater_settings.py
@@ -24,18 +24,24 @@
import importlib.resources
import gi
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk, GObject
from qubes_config.global_config.vm_flowbox import VMFlowboxHandler
-from qubes_config.widgets.utils import get_boolean_feature, \
- apply_feature_change, get_feature
+from qubes_config.widgets.utils import (
+ get_boolean_feature,
+ apply_feature_change,
+ get_feature,
+)
-GObject.signal_new('child-removed',
- Gtk.FlowBox,
- GObject.SignalFlags.RUN_LAST, GObject.TYPE_PYOBJECT,
- (GObject.TYPE_PYOBJECT,))
+GObject.signal_new(
+ "child-removed",
+ Gtk.FlowBox,
+ GObject.SignalFlags.RUN_LAST,
+ GObject.TYPE_PYOBJECT,
+ (GObject.TYPE_PYOBJECT,),
+)
@dataclasses.dataclass(frozen=True)
@@ -57,12 +63,12 @@ class Settings:
DEFAULT_HIDE_UPDATED = False
def __init__(
- self,
- main_window,
- qapp,
- log,
- refresh_callback: Callable,
- overrides: OverriddenSettings = OverriddenSettings(),
+ self,
+ main_window,
+ qapp,
+ log,
+ refresh_callback: Callable,
+ overrides: OverriddenSettings = OverriddenSettings(),
):
self.qapp = qapp
self.log = log
@@ -73,72 +79,103 @@ def __init__(
self.builder = Gtk.Builder()
self.builder.set_translation_domain("desktop-linux-manager")
- glade_ref = (importlib.resources.files('qui') /
- 'updater_settings.glade')
+ glade_ref = importlib.resources.files("qui") / "updater_settings.glade"
with importlib.resources.as_file(glade_ref) as path:
self.builder.add_from_file(str(path))
self.settings_window: Gtk.Window = self.builder.get_object(
- "main_window")
+ "main_window"
+ )
self.settings_window.set_transient_for(main_window)
self.settings_window.connect("delete-event", self.close_without_saving)
self.cancel_button: Gtk.Button = self.builder.get_object(
- "button_settings_cancel")
+ "button_settings_cancel"
+ )
self.cancel_button.connect(
- "clicked", lambda _: self.settings_window.close())
+ "clicked", lambda _: self.settings_window.close()
+ )
self.save_button: Gtk.Button = self.builder.get_object(
- "button_settings_save")
+ "button_settings_save"
+ )
self.save_button.connect("clicked", self.save_and_close)
- self.days_without_update_button: Gtk.SpinButton = \
+ self.days_without_update_button: Gtk.SpinButton = (
self.builder.get_object("days_without_update")
+ )
adj = Gtk.Adjustment(
- Settings.DEFAULT_UPDATE_IF_STALE, 1, Settings.MAX_UPDATE_IF_STALE,
- 1, 1, 1
+ Settings.DEFAULT_UPDATE_IF_STALE,
+ 1,
+ Settings.MAX_UPDATE_IF_STALE,
+ 1,
+ 1,
+ 1,
)
self.days_without_update_button.configure(adj, 1, 0)
- self.restart_servicevms_checkbox: Gtk.CheckButton = \
+ self.restart_servicevms_checkbox: Gtk.CheckButton = (
self.builder.get_object("restart_servicevms")
+ )
self.restart_servicevms_checkbox.connect(
- "toggled", self._show_restart_exceptions)
+ "toggled", self._show_restart_exceptions
+ )
self.restart_other_checkbox: Gtk.CheckButton = self.builder.get_object(
- "restart_other")
+ "restart_other"
+ )
self.restart_other_checkbox.connect(
- "toggled", self._show_restart_exceptions)
+ "toggled", self._show_restart_exceptions
+ )
- self.hide_skipped_checkbox: Gtk.CheckButton = \
- self.builder.get_object("hide_skipped")
+ self.hide_skipped_checkbox: Gtk.CheckButton = self.builder.get_object(
+ "hide_skipped"
+ )
- self.hide_updated_checkbox: Gtk.CheckButton = \
- self.builder.get_object("hide_updated")
+ self.hide_updated_checkbox: Gtk.CheckButton = self.builder.get_object(
+ "hide_updated"
+ )
self.available_vms = [
- vm for vm in self.qapp.domains
- if vm.klass == 'DispVM' and not vm.auto_cleanup
- or vm.klass == 'AppVM']
+ vm
+ for vm in self.qapp.domains
+ if vm.klass == "DispVM"
+ and not vm.auto_cleanup
+ or vm.klass == "AppVM"
+ ]
self.excluded_vms = [
- vm for vm in self.available_vms
- if not get_boolean_feature(vm, 'restart-after-update', True)]
+ vm
+ for vm in self.available_vms
+ if not get_boolean_feature(vm, "restart-after-update", True)
+ ]
self.exceptions = VMFlowboxHandler(
- self.builder, self.qapp, "restart_exceptions",
- self.excluded_vms, lambda vm: vm in self.available_vms)
+ self.builder,
+ self.qapp,
+ "restart_exceptions",
+ self.excluded_vms,
+ lambda vm: vm in self.available_vms,
+ )
self.restart_exceptions_page: Gtk.Box = self.builder.get_object(
- "restart_exceptions_page")
+ "restart_exceptions_page"
+ )
- self.limit_concurrency_checkbox: Gtk.CheckButton = \
+ self.limit_concurrency_checkbox: Gtk.CheckButton = (
self.builder.get_object("limit_concurrency")
+ )
self.limit_concurrency_checkbox.connect(
- "toggled", self._limit_concurrency_toggled)
- self.max_concurrency_button: Gtk.SpinButton = \
- self.builder.get_object("max_concurrency")
+ "toggled", self._limit_concurrency_toggled
+ )
+ self.max_concurrency_button: Gtk.SpinButton = self.builder.get_object(
+ "max_concurrency"
+ )
adj = Gtk.Adjustment(
- Settings.DEFAULT_CONCURRENCY, 1, Settings.MAX_CONCURRENCY + 1,
- 1, 1, 1
+ Settings.DEFAULT_CONCURRENCY,
+ 1,
+ Settings.MAX_CONCURRENCY + 1,
+ 1,
+ 1,
+ 1,
)
self.max_concurrency_button.configure(adj, 1, 0)
@@ -155,8 +192,13 @@ def update_if_stale(self) -> int:
"""Return the current (set by this window or manually) option value."""
if self.overrides.update_if_stale is not None:
return self.overrides.update_if_stale
- return int(get_feature(self.vm, "qubes-vm-update-update-if-stale",
- Settings.DEFAULT_UPDATE_IF_STALE))
+ return int(
+ get_feature(
+ self.vm,
+ "qubes-vm-update-update-if-stale",
+ Settings.DEFAULT_UPDATE_IF_STALE,
+ )
+ )
@property
def restart_service_vms(self) -> bool:
@@ -165,16 +207,18 @@ def restart_service_vms(self) -> bool:
return self.overrides.apply_to_sys
result = get_boolean_feature(
- self.vm, "qubes-vm-update-restart-servicevms",
- None)
+ self.vm, "qubes-vm-update-restart-servicevms", None
+ )
# TODO
# If not set, try to use a deprecated flag instead of
# the default value. This is only for backward compatibility
# and should be removed in future (e.g. Qubes 5.0 or later)
if result is None:
result = get_boolean_feature(
- self.vm, "qubes-vm-update-restart-system",
- Settings.DEFAULT_RESTART_SERVICEVMS)
+ self.vm,
+ "qubes-vm-update-restart-system",
+ Settings.DEFAULT_RESTART_SERVICEVMS,
+ )
return result
@@ -184,20 +228,26 @@ def restart_other_vms(self) -> bool:
if self.overrides.apply_to_other is not None:
return self.overrides.apply_to_other
return get_boolean_feature(
- self.vm, "qubes-vm-update-restart-other",
- Settings.DEFAULT_RESTART_OTHER_VMS)
+ self.vm,
+ "qubes-vm-update-restart-other",
+ Settings.DEFAULT_RESTART_OTHER_VMS,
+ )
@property
def hide_skipped(self) -> bool:
return get_boolean_feature(
- self.vm, "qubes-vm-update-hide-skipped",
- Settings.DEFAULT_HIDE_SKIPPED)
+ self.vm,
+ "qubes-vm-update-hide-skipped",
+ Settings.DEFAULT_HIDE_SKIPPED,
+ )
@property
def hide_updated(self) -> bool:
return get_boolean_feature(
- self.vm, "qubes-vm-update-hide-updated",
- Settings.DEFAULT_HIDE_UPDATED)
+ self.vm,
+ "qubes-vm-update-hide-updated",
+ Settings.DEFAULT_HIDE_UPDATED,
+ )
@property
def max_concurrency(self) -> Optional[int]:
@@ -213,23 +263,28 @@ def load_settings(self):
self._init_update_if_stale = self.update_if_stale
self.days_without_update_button.set_value(self._init_update_if_stale)
self.days_without_update_button.set_sensitive(
- not self.overrides.update_if_stale)
+ not self.overrides.update_if_stale
+ )
self._init_restart_servicevms = self.restart_service_vms
self._init_restart_other_vms = self.restart_other_vms
self.restart_servicevms_checkbox.set_sensitive(
- not self.overrides.apply_to_sys)
+ not self.overrides.apply_to_sys
+ )
self.restart_servicevms_checkbox.set_active(
- self._init_restart_servicevms)
+ self._init_restart_servicevms
+ )
self.restart_other_checkbox.set_active(self._init_restart_other_vms)
self.restart_other_checkbox.set_sensitive(
- not self.overrides.apply_to_other)
+ not self.overrides.apply_to_other
+ )
self._init_max_concurrency = self.max_concurrency
self._init_limit_concurrency = self._init_max_concurrency is not None
self.limit_concurrency_checkbox.set_active(self._init_limit_concurrency)
self.limit_concurrency_checkbox.set_sensitive(
- not self.overrides.max_concurrency)
+ not self.overrides.max_concurrency
+ )
if self._init_limit_concurrency:
self.max_concurrency_button.set_value(self._init_max_concurrency)
@@ -269,14 +324,14 @@ def save_and_close(self, _emitter):
name="update-if-stale",
value=int(self.days_without_update_button.get_value()),
init=self._init_update_if_stale,
- default=Settings.DEFAULT_UPDATE_IF_STALE
+ default=Settings.DEFAULT_UPDATE_IF_STALE,
)
self._save_option(
name="restart-servicevms",
value=self.restart_servicevms_checkbox.get_active(),
init=self._init_restart_servicevms,
- default=Settings.DEFAULT_RESTART_SERVICEVMS
+ default=Settings.DEFAULT_RESTART_SERVICEVMS,
)
# TODO
# Make sure that the deprecated flag is unset.
@@ -287,21 +342,21 @@ def save_and_close(self, _emitter):
name="restart-other",
value=self.restart_other_checkbox.get_active(),
init=self._init_restart_other_vms,
- default=Settings.DEFAULT_RESTART_OTHER_VMS
+ default=Settings.DEFAULT_RESTART_OTHER_VMS,
)
self._save_option(
name="hide-skipped",
value=self.hide_skipped_checkbox.get_active(),
init=self._init_hide_skipped,
- default=Settings.DEFAULT_HIDE_SKIPPED
+ default=Settings.DEFAULT_HIDE_SKIPPED,
)
self._save_option(
name="hide-updated",
value=self.hide_updated_checkbox.get_active(),
init=self._init_hide_updated,
- default=Settings.DEFAULT_HIDE_UPDATED
+ default=Settings.DEFAULT_HIDE_UPDATED,
)
limit_concurrency = self.limit_concurrency_checkbox.get_active()
@@ -312,23 +367,25 @@ def save_and_close(self, _emitter):
max_concurrency = None
if self._init_max_concurrency != max_concurrency:
apply_feature_change(
- self.vm, "qubes-vm-update-max-concurrency", max_concurrency)
+ self.vm, "qubes-vm-update-max-concurrency", max_concurrency
+ )
if self.exceptions.is_changed():
for vm in self.exceptions.added_vms:
- apply_feature_change(vm, 'restart-after-update', False)
+ apply_feature_change(vm, "restart-after-update", False)
for vm in self.exceptions.removed_vms:
- apply_feature_change(vm, 'restart-after-update', None)
+ apply_feature_change(vm, "restart-after-update", None)
self.exceptions.save()
self.refresh_callback(self.update_if_stale)
self.settings_window.close()
def _save_option(
- self, name: str,
- value: Union[int, bool],
- init: Union[int, bool],
- default: Union[int, bool]
+ self,
+ name: str,
+ value: Union[int, bool],
+ init: Union[int, bool],
+ default: Union[int, bool],
):
if value != init:
if value == default:
diff --git a/qui/updater/utils.py b/qui/updater/utils.py
index 2a646521..aaefb7f9 100644
--- a/qui/updater/utils.py
+++ b/qui/updater/utils.py
@@ -25,7 +25,7 @@
from enum import Enum
from typing import List
-gi.require_version('Gtk', '3.0') # isort:skip
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk
@@ -40,8 +40,10 @@ def disable_checkboxes(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if not hasattr(self, "disable_checkboxes"):
- raise TypeError("To use this decorator inside the class yu need to"
- " add attribute `disable_checkboxes`.")
+ raise TypeError(
+ "To use this decorator inside the class yu need to"
+ " add attribute `disable_checkboxes`."
+ )
if self.disable_checkboxes:
return
self.disable_checkboxes = True
@@ -116,12 +118,12 @@ def on_head_checkbox_toggled(list_store, head_checkbox, select_rows):
selected_num = 0
else:
selected_num = selected_num_old = sum(
- row.selected for row in list_store)
+ row.selected for row in list_store
+ )
while selected_num == selected_num_old:
head_checkbox.next_state()
select_rows()
- selected_num = sum(
- row.selected for row in list_store)
+ selected_num = sum(row.selected for row in list_store)
head_checkbox.set_buttons(selected_num)
@@ -129,32 +131,36 @@ class QubeClass(Enum):
"""
Sorting order by vm type.
"""
+
AdminVM = 0
TemplateVM = 1
StandaloneVM = 2
AppVM = 3
DispVM = 4
+
# pylint: disable=global-statement
# TODO: Encapsulate the below variable within a class
__effective_css_provider__: Gtk.CssProvider = None
+
def SetEffectiveCssProvider(CssProvider: Gtk.CssProvider) -> None:
global __effective_css_provider__
__effective_css_provider__ = CssProvider
+
def label_color_theme(color: str) -> str:
widget = Gtk.Label()
if not __effective_css_provider__:
# Replicating the old behaviour. Both forward and backward compatible
- widget.get_style_context().add_class(f'qube-box-{color}')
- elif f'.qube-box-{color}' in __effective_css_provider__.to_string():
- widget.get_style_context().add_class(f'qube-box-{color}')
+ widget.get_style_context().add_class(f"qube-box-{color}")
+ elif f".qube-box-{color}" in __effective_css_provider__.to_string():
+ widget.get_style_context().add_class(f"qube-box-{color}")
else:
- widget.get_style_context().add_class('qube-box-custom-label')
+ widget.get_style_context().add_class("qube-box-custom-label")
gtk_color = widget.get_style_context().get_color(Gtk.StateFlags.NORMAL)
color_rgb = ast.literal_eval(gtk_color.to_string()[3:])
- color_hex = '#{:02x}{:02x}{:02x}'.format(*color_rgb)
+ color_hex = "#{:02x}{:02x}{:02x}".format(*color_rgb)
return color_hex
@@ -164,8 +170,10 @@ def __init__(self, name, color):
self.color = color
def __str__(self):
- return f'' + self.name + ''
+ return (
+ f'' + self.name + ""
+ )
def __eq__(self, other):
return self.name == other.name
@@ -197,7 +205,7 @@ def __str__(self):
elif self in (UpdateStatus.InProgress, UpdateStatus.ProgressUnknown):
text = "In progress"
- return f'' + text + ''
+ return f'' + text + ""
def __eq__(self, other):
return self.value == other.value
@@ -210,10 +218,12 @@ def __bool__(self):
@staticmethod
def from_name(name):
- names = {"success": UpdateStatus.Success,
- "error": UpdateStatus.Error,
- "no_updates": UpdateStatus.NoUpdatesFound,
- "cancelled": UpdateStatus.Cancelled}
+ names = {
+ "success": UpdateStatus.Success,
+ "error": UpdateStatus.Error,
+ "no_updates": UpdateStatus.NoUpdatesFound,
+ "cancelled": UpdateStatus.Cancelled,
+ }
return names[name]
@@ -294,14 +304,17 @@ def append_vm(self, vm, state: bool = False):
def invert_selection(self, path):
it = self.list_store_raw.get_iter(path)
- self.list_store_raw[it][0].selected = \
- not self.list_store_raw[it][0].selected
+ self.list_store_raw[it][0].selected = not self.list_store_raw[it][
+ 0
+ ].selected
def get_selected(self) -> "ListWrapper":
- empty_copy = Gtk.ListStore(*(
- self.list_store_raw.get_column_type(i)
- for i in range(self.list_store_raw.get_n_columns())
- ))
+ empty_copy = Gtk.ListStore(
+ *(
+ self.list_store_raw.get_column_type(i)
+ for i in range(self.list_store_raw.get_n_columns())
+ )
+ )
result = ListWrapper(self.row_type, empty_copy)
selected_rows = [row for row in self if row.selected]
for row in selected_rows:
diff --git a/qui/utils.py b/qui/utils.py
index ffbdf26b..59b00626 100644
--- a/qui/utils.py
+++ b/qui/utils.py
@@ -36,13 +36,15 @@
_ = t.gettext
import gi # isort:skip
-gi.require_version('Gtk', '3.0') # isort:skip
+
+gi.require_version("Gtk", "3.0") # isort:skip
from gi.repository import Gtk # isort:skip
-with importlib.resources.files('qui').joinpath('eol.json').open() as stream:
+with importlib.resources.files("qui").joinpath("eol.json").open() as stream:
EOL_DATES = json.load(stream)
# remove the following suffixes when checking for EOL
-SUFFIXES = ['-minimal', '-xfce']
+SUFFIXES = ["-minimal", "-xfce"]
+
def run_asyncio_and_show_errors(loop, tasks, name, restart=True):
"""
@@ -54,13 +56,16 @@ def run_asyncio_and_show_errors(loop, tasks, name, restart=True):
:param restart: should the user be told that the widget will restart itself?
:return: exit code
"""
- done, _unused = loop.run_until_complete(asyncio.wait(
- tasks, return_when=asyncio.FIRST_EXCEPTION))
+ done, _unused = loop.run_until_complete(
+ asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)
+ )
exit_code = 0
- message = _("Whoops. A critical error in {} has occurred."
- " This is most likely a bug.").format(name)
+ message = _(
+ "Whoops. A critical error in {} has occurred."
+ " This is most likely a bug."
+ ).format(name)
if restart:
message += _(" {} will restart itself.").format(name)
@@ -70,45 +75,49 @@ def run_asyncio_and_show_errors(loop, tasks, name, restart=True):
except Exception as _ex: # pylint: disable=broad-except
exc_type, exc_value = sys.exc_info()[:2]
dialog = Gtk.MessageDialog(
- None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK)
+ None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK
+ )
dialog.set_title(_("Houston, we have a problem..."))
dialog.set_markup(message)
exc_value_descr = escape(str(exc_value))
traceback_descr = escape(traceback.format_exc(limit=10))
exc_description = "\n{}: {}\n{}".format(
- exc_type.__name__, exc_value_descr, traceback_descr)
+ exc_type.__name__, exc_value_descr, traceback_descr
+ )
dialog.format_secondary_markup(exc_description)
dialog.run()
exit_code = 1
return exit_code
+
def check_update(vm) -> bool:
"""Return true if the given template/standalone vm is updated or not
updateable or skipped. default returns true"""
try:
- if not vm.features.get('updates-available', False):
+ if not vm.features.get("updates-available", False):
return True
- if not getattr(vm, 'updateable', False):
+ if not getattr(vm, "updateable", False):
return True
- if bool(vm.features.get('skip-update', False)):
+ if bool(vm.features.get("skip-update", False)):
return True
except exc.QubesException:
return True
return False
+
def check_support(vm) -> bool:
"""Return true if the given template/standalone vm is still supported, by
default returns true"""
try:
# first, we skip VMs with `skip-update` feature set to true
- if bool(vm.features.get('skip-update', False)):
+ if bool(vm.features.get("skip-update", False)):
return True
# next, check if qube itself has known eol
- eol_string: str = vm.features.get('os-eol', '')
+ eol_string: str = vm.features.get("os-eol", "")
if not eol_string:
- template_name: str = vm.features.get('template-name', '')
+ template_name: str = vm.features.get("template-name", "")
if not template_name:
return True
for suffix in SUFFIXES:
@@ -118,5 +127,5 @@ def check_support(vm) -> bool:
return True
except exc.QubesException:
return True
- eol = datetime.strptime(eol_string, '%Y-%m-%d')
+ eol = datetime.strptime(eol_string, "%Y-%m-%d")
return eol > datetime.now()
diff --git a/setup.py b/setup.py
index 09104331..10c616a1 100644
--- a/setup.py
+++ b/setup.py
@@ -9,22 +9,26 @@
class InstallWithLocale(setuptools.command.install.install):
def create_mo_files(self):
data_files = []
- localedir = 'locale'
- po_dirs = [localedir + '/' + l + '/LC_MESSAGES/'
- for l in next(os.walk(localedir))[1]]
+ localedir = "locale"
+ po_dirs = [
+ localedir + "/" + l + "/LC_MESSAGES/"
+ for l in next(os.walk(localedir))[1]
+ ]
for d in po_dirs:
- mo_dir = os.path.join(self.root, 'usr/share', d)
+ mo_dir = os.path.join(self.root, "usr/share", d)
os.makedirs(mo_dir, exist_ok=True)
mo_files = []
- po_files = [f
- for f in next(os.walk(d))[2]
- if os.path.splitext(f)[1] == '.po']
+ po_files = [
+ f
+ for f in next(os.walk(d))[2]
+ if os.path.splitext(f)[1] == ".po"
+ ]
for po_file in po_files:
filename, extension = os.path.splitext(po_file)
- mo_file = filename + '.mo'
- msgfmt_cmd = 'msgfmt {} -o {}'.format(
- d + po_file,
- os.path.join(mo_dir, mo_file))
+ mo_file = filename + ".mo"
+ msgfmt_cmd = "msgfmt {} -o {}".format(
+ d + po_file, os.path.join(mo_dir, mo_file)
+ )
subprocess.check_call(msgfmt_cmd, shell=True)
mo_files.append(d + mo_file)
data_files.append((d, mo_files))
@@ -46,77 +50,92 @@ def install_custom_scripts(self):
path = os.path.join(bin, file)
with open(path, "w") as f:
f.write(
-"""#!/usr/bin/python3
+ """#!/usr/bin/python3
from {} import main
import sys
if __name__ == '__main__':
sys.exit(main())
-""".format(pkg))
+""".format(
+ pkg
+ )
+ )
os.chmod(path, 0o755)
+
# don't import: import * is unreliable and there is no need, since this is
# compile time and we have source files
def get_console_scripts():
- for filename in os.listdir('./qui/tools'):
+ for filename in os.listdir("./qui/tools"):
basename, ext = os.path.splitext(os.path.basename(filename))
- if basename == '__init__' or ext != '.py':
+ if basename == "__init__" or ext != ".py":
continue
- yield basename.replace('_', '-'), 'qui.tools.{}'.format(basename)
+ yield basename.replace("_", "-"), "qui.tools.{}".format(basename)
setuptools.setup(
- name='qui',
- version='0.1',
- author='Invisible Things Lab',
- author_email='marmarta@invisiblethingslab.com',
- description='Qubes User Interface And Configuration Package',
- license='GPL2+',
- url='https://www.qubes-os.org/',
- packages=["qui", "qui.updater", "qui.devices", "qui.tools", "qui.tray",
- "qubes_config", "qubes_config.global_config",
- "qubes_config.widgets", "qubes_config.new_qube",
- 'qubes_config.policy_editor'],
+ name="qui",
+ version="0.1",
+ author="Invisible Things Lab",
+ author_email="marmarta@invisiblethingslab.com",
+ description="Qubes User Interface And Configuration Package",
+ license="GPL2+",
+ url="https://www.qubes-os.org/",
+ packages=[
+ "qui",
+ "qui.updater",
+ "qui.devices",
+ "qui.tools",
+ "qui.tray",
+ "qubes_config",
+ "qubes_config.global_config",
+ "qubes_config.widgets",
+ "qubes_config.new_qube",
+ "qubes_config.policy_editor",
+ ],
entry_points={
- 'gui_scripts': [
- 'qui-domains = qui.tray.domains:main',
- 'qui-devices = qui.devices.device_widget:main',
- 'qui-disk-space = qui.tray.disk_space:main',
- 'qui-updates = qui.tray.updates:main',
- 'qubes-update-gui = qui.updater.updater:main',
- 'qui-clipboard = qui.clipboard:main',
- 'qubes-new-qube = qubes_config.new_qube.new_qube_app:main',
- 'qubes-global-config = qubes_config.global_config.global_config:main',
- 'qubes-policy-editor-gui = qubes_config.policy_editor.policy_editor:main'
+ "gui_scripts": [
+ "qui-domains = qui.tray.domains:main",
+ "qui-devices = qui.devices.device_widget:main",
+ "qui-disk-space = qui.tray.disk_space:main",
+ "qui-updates = qui.tray.updates:main",
+ "qubes-update-gui = qui.updater.updater:main",
+ "qui-clipboard = qui.clipboard:main",
+ "qubes-new-qube = qubes_config.new_qube.new_qube_app:main",
+ "qubes-global-config = qubes_config.global_config.global_config:main",
+ "qubes-policy-editor-gui = qubes_config.policy_editor.policy_editor:main",
]
},
- package_data={'qui': ["updater.glade",
- "updater_settings.glade",
- "qubes-updater-base.css",
- "qubes-updater-light.css",
- "qubes-updater-dark.css",
- "styles/qubes-colors-light.css",
- "styles/qubes-colors-dark.css",
- "styles/qubes-widgets-base.css",
- "eol.json",
- "qubes-devices-light.css",
- "qubes-devices-dark.css",
- "devices/AttachConfirmationWindow.glade"
- ],
- 'qubes_config': ["new_qube.glade",
- "global_config.glade",
- "qubes-new-qube-base.css",
- "qubes-new-qube-light.css",
- "qubes-new-qube-dark.css",
- "qubes-global-config-base.css",
- "qubes-global-config-light.css",
- "qubes-global-config-dark.css",
- "qubes-policy-editor-base.css",
- "qubes-policy-editor-light.css",
- "qubes-policy-editor-dark.css",
- "policy_editor.glade",
- "policy_editor/policy_help.txt"]},
- cmdclass={
- 'install': InstallWithLocale
+ package_data={
+ "qui": [
+ "updater.glade",
+ "updater_settings.glade",
+ "qubes-updater-base.css",
+ "qubes-updater-light.css",
+ "qubes-updater-dark.css",
+ "styles/qubes-colors-light.css",
+ "styles/qubes-colors-dark.css",
+ "styles/qubes-widgets-base.css",
+ "eol.json",
+ "qubes-devices-light.css",
+ "qubes-devices-dark.css",
+ "devices/AttachConfirmationWindow.glade",
+ ],
+ "qubes_config": [
+ "new_qube.glade",
+ "global_config.glade",
+ "qubes-new-qube-base.css",
+ "qubes-new-qube-light.css",
+ "qubes-new-qube-dark.css",
+ "qubes-global-config-base.css",
+ "qubes-global-config-light.css",
+ "qubes-global-config-dark.css",
+ "qubes-policy-editor-base.css",
+ "qubes-policy-editor-light.css",
+ "qubes-policy-editor-dark.css",
+ "policy_editor.glade",
+ "policy_editor/policy_help.txt",
+ ],
},
+ cmdclass={"install": InstallWithLocale},
)
diff --git a/stubs/dbus/__init__.pyi b/stubs/dbus/__init__.pyi
index 0246d153..f241ea85 100644
--- a/stubs/dbus/__init__.pyi
+++ b/stubs/dbus/__init__.pyi
@@ -5,30 +5,36 @@ import dbus.proxies
import dbus._dbus
from typing import Optional, Union, Callable
-
class Interface(object):
- def __init__(self, object: Union[dbus.proxies.ProxyObject, Interface], dbus_interface: str) -> None: ...
- def get_dbus_method(self, member: str, dbus_interface: Optional[str]=...) -> Callable: ...
-
+ def __init__(
+ self,
+ object: Union[dbus.proxies.ProxyObject, Interface],
+ dbus_interface: str,
+ ) -> None: ...
+ def get_dbus_method(
+ self, member: str, dbus_interface: Optional[str] = ...
+ ) -> Callable: ...
class Bus(dbus.bus.BusConnection):
- def __new__(cls,
- bus_type: Optional[int], private: Optional[bool]=...,
- mainloop: Optional[dbus.mainloop.NativeMainLoop]=...
- ) -> dbus.bus.BusConnection: ...
+ def __new__(
+ cls,
+ bus_type: Optional[int],
+ private: Optional[bool] = ...,
+ mainloop: Optional[dbus.mainloop.NativeMainLoop] = ...,
+ ) -> dbus.bus.BusConnection: ...
class SessionBus(Bus):
- def __new__(cls,
- private: Optional[bool]=...,
- mainloop: Optional[dbus.mainloop.NativeMainLoop]=...
- ) -> Bus: ...
+ def __new__(
+ cls,
+ private: Optional[bool] = ...,
+ mainloop: Optional[dbus.mainloop.NativeMainLoop] = ...,
+ ) -> Bus: ...
class String(str): ...
class Boolean(int): ...
class Int32(int): ...
-
class Array(list): ...
class Dictionary(dict): ...
class ObjectPath(str): ...
-# vim: syntax=python
+# vim: syntax=python
diff --git a/stubs/dbus/_dbus.pyi b/stubs/dbus/_dbus.pyi
index b4089375..45aff770 100644
--- a/stubs/dbus/_dbus.pyi
+++ b/stubs/dbus/_dbus.pyi
@@ -4,9 +4,10 @@ import dbus.bus
import dbus.mainloop
class SessionBus(dbus.bus.BusConnection):
- def __new__(cls,
- private: Optional[bool]=...,
- mainloop: Optional[dbus.mainloop.NativeMainLoop]=...
- ) -> dbus.Bus: ...
+ def __new__(
+ cls,
+ private: Optional[bool] = ...,
+ mainloop: Optional[dbus.mainloop.NativeMainLoop] = ...,
+ ) -> dbus.Bus: ...
# vim: syntax=python
diff --git a/stubs/dbus/bus.pyi b/stubs/dbus/bus.pyi
index 8ae82799..fa111836 100644
--- a/stubs/dbus/bus.pyi
+++ b/stubs/dbus/bus.pyi
@@ -2,14 +2,13 @@ import dbus.connection
import dbus.proxies
from typing import Optional
-
class BusConnection(dbus.connection.Connection):
- def get_object(self,
- bus_name: str=...,
- object_path: str=...,
- introspect: Optional[bool]=...,
- follow_name_owner_changes: Optional[bool]=...
- ) -> dbus.proxies.ProxyObject:
- ...
+ def get_object(
+ self,
+ bus_name: str = ...,
+ object_path: str = ...,
+ introspect: Optional[bool] = ...,
+ follow_name_owner_changes: Optional[bool] = ...,
+ ) -> dbus.proxies.ProxyObject: ...
# vim: syntax=python
diff --git a/stubs/dbus/mainloop/__init__.pyi b/stubs/dbus/mainloop/__init__.pyi
index 66905579..c92069c6 100644
--- a/stubs/dbus/mainloop/__init__.pyi
+++ b/stubs/dbus/mainloop/__init__.pyi
@@ -1,3 +1 @@
-
-class NativeMainLoop:
- ...
+class NativeMainLoop: ...
diff --git a/stubs/dbus/mainloop/glib.pyi b/stubs/dbus/mainloop/glib.pyi
index fc14aeea..5a098b7e 100644
--- a/stubs/dbus/mainloop/glib.pyi
+++ b/stubs/dbus/mainloop/glib.pyi
@@ -1,3 +1,5 @@
import dbus.mainloop
-def DBusGMainLoop(set_as_default: bool = ... ) -> dbus.mainloop.NativeMainLoop: ...
+def DBusGMainLoop(
+ set_as_default: bool = ...,
+) -> dbus.mainloop.NativeMainLoop: ...
diff --git a/stubs/dbus/proxies.pyi b/stubs/dbus/proxies.pyi
index accbc394..80c661bd 100644
--- a/stubs/dbus/proxies.pyi
+++ b/stubs/dbus/proxies.pyi
@@ -5,24 +5,28 @@ from typing import Callable, Optional, Union
ObjectPath = Union[dbus.ObjectPath, str]
-
class ProxyObject(object):
- bus_name = ... # type: str
-
- def __init__(conn: dbus.Bus=..., bus_name: str=...,
- object_path: ObjectPath=..., follow_name_owner_changes: bool=..., introspect: bool=..., **kwargs) -> None:
- ...
-
- def get_dbus_method(self, member: str=..., dbus_interface: Optional[str]=...) -> Callable:
- ...
-
- def connect_to_signal(self, signal_name: str=...,
- handler_function: Callable=...,
- dbus_interface: Optional[str]=..., **keywords) -> None:
- ...
-
- def Introspect(self) -> dbus.String:
- ...
+ bus_name = ... # type: str
+
+ def __init__(
+ conn: dbus.Bus = ...,
+ bus_name: str = ...,
+ object_path: ObjectPath = ...,
+ follow_name_owner_changes: bool = ...,
+ introspect: bool = ...,
+ **kwargs
+ ) -> None: ...
+ def get_dbus_method(
+ self, member: str = ..., dbus_interface: Optional[str] = ...
+ ) -> Callable: ...
+ def connect_to_signal(
+ self,
+ signal_name: str = ...,
+ handler_function: Callable = ...,
+ dbus_interface: Optional[str] = ...,
+ **keywords
+ ) -> None: ...
+ def Introspect(self) -> dbus.String: ...
# vim: ft=python tw=0 syntax=python
diff --git a/stubs/dbus/service.pyi b/stubs/dbus/service.pyi
index 90cabd7d..02640c3b 100644
--- a/stubs/dbus/service.pyi
+++ b/stubs/dbus/service.pyi
@@ -5,41 +5,43 @@ from dbus.connection import Connection
from dbus._dbus import SessionBus
from typing import Optional, Tuple, Callable, Union
-
class Object(dbus.Interface):
- def __init__(self, conn: Optional[Union[BusName,Connection]] = ...,
- object_path: Optional[str] = ...,
- bus_name: Optional[BusName] = ...) -> None: ...
+ def __init__(
+ self,
+ conn: Optional[Union[BusName, Connection]] = ...,
+ object_path: Optional[str] = ...,
+ bus_name: Optional[BusName] = ...,
+ ) -> None: ...
def method(
- dbus_interface: str=...,
- in_signature: Optional[str]=...,
- out_signature: Optional[str]=...,
- async_callbacks: Tuple[str,str]=...,
- sender_keyword: Optional[str]=...,
- path_keyword: Optional[str]=...,
- rel_path_keyword: Optional[str]=...,
- destination_keyword: Optional[str]=...,
- message_keyword: Optional[str]=...,
- connection_keyword: Optional[str]=...,
- utf8_strings: bool=...,
- byte_arrays: bool=...,
-
- ) -> Callable: ...
-
+ dbus_interface: str = ...,
+ in_signature: Optional[str] = ...,
+ out_signature: Optional[str] = ...,
+ async_callbacks: Tuple[str, str] = ...,
+ sender_keyword: Optional[str] = ...,
+ path_keyword: Optional[str] = ...,
+ rel_path_keyword: Optional[str] = ...,
+ destination_keyword: Optional[str] = ...,
+ message_keyword: Optional[str] = ...,
+ connection_keyword: Optional[str] = ...,
+ utf8_strings: bool = ...,
+ byte_arrays: bool = ...,
+) -> Callable: ...
def signal(
- dbus_interface: str=...,
- signature: Optional[str]=...,
- path_keyword: Optional[str]=...,
- rel_path_keyword: Optional[str]=...,
- ) -> Callable: ...
+ dbus_interface: str = ...,
+ signature: Optional[str] = ...,
+ path_keyword: Optional[str] = ...,
+ rel_path_keyword: Optional[str] = ...,
+) -> Callable: ...
class BusName(object):
- def __new__(cls,
- name: str,
- bus: Union[dbus.Bus, SessionBus],
- allow_replacement: Optional[bool]=...,
- replace_existing: Optional[bool]=...,
- do_not_queue: Optional[bool]=...,
- ) -> BusName: ...
+ def __new__(
+ cls,
+ name: str,
+ bus: Union[dbus.Bus, SessionBus],
+ allow_replacement: Optional[bool] = ...,
+ replace_existing: Optional[bool] = ...,
+ do_not_queue: Optional[bool] = ...,
+ ) -> BusName: ...
+
# vim: syntax=python tw=0
diff --git a/stubs/pkg_resources/__init__.pyi b/stubs/pkg_resources/__init__.pyi
index 5dd0427f..7c579256 100644
--- a/stubs/pkg_resources/__init__.pyi
+++ b/stubs/pkg_resources/__init__.pyi
@@ -1,6 +1,10 @@
-from typing import (Iterable, Optional, Any, Callable)
-class EntryPoint(object):
- def load(self, require: bool = ..., *args: Any, **kwargs: Any) -> Callable: ...
+from typing import Iterable, Optional, Any, Callable
+class EntryPoint(object):
+ def load(
+ self, require: bool = ..., *args: Any, **kwargs: Any
+ ) -> Callable: ...
-def iter_entry_points(group: str, name: Optional[str] = ...) -> Iterable[EntryPoint]: ...
+def iter_entry_points(
+ group: str, name: Optional[str] = ...
+) -> Iterable[EntryPoint]: ...
diff --git a/stubs/systemd/journal.pyi b/stubs/systemd/journal.pyi
index a9bbf38a..fc69fdc1 100644
--- a/stubs/systemd/journal.pyi
+++ b/stubs/systemd/journal.pyi
@@ -5,5 +5,8 @@ import logging
from typing import Optional
class JournalHandler(logging.Handler):
- def __init__(self, level: Optional[int]=..., SYSLOG_IDENTIFIER: Optional[str]=...) -> None: ...
+ def __init__(
+ self, level: Optional[int] = ..., SYSLOG_IDENTIFIER: Optional[str] = ...
+ ) -> None: ...
+
# vim: syntax=python tw=0
diff --git a/stubs/unittest.pyi b/stubs/unittest.pyi
index 3d16f5d9..ea91e490 100644
--- a/stubs/unittest.pyi
+++ b/stubs/unittest.pyi
@@ -5,17 +5,27 @@
# Only a subset of functionality is included.
from typing import (
- Any, Callable, Iterable, Tuple, List, TextIO, Sequence,
- overload, TypeVar, Pattern, Type, IO
+ Any,
+ Callable,
+ Iterable,
+ Tuple,
+ List,
+ TextIO,
+ Sequence,
+ overload,
+ TypeVar,
+ Pattern,
+ Type,
+ IO,
)
from abc import abstractmethod, ABCMeta
-_T = TypeVar('_T')
-_FT = TypeVar('_FT')
+_T = TypeVar("_T")
+_FT = TypeVar("_FT")
class Testable(metaclass=ABCMeta):
@abstractmethod
- def run(self, result: 'TestResult') -> None: ...
+ def run(self, result: "TestResult") -> None: ...
@abstractmethod
def debug(self) -> None: ...
@abstractmethod
@@ -24,38 +34,40 @@ class Testable(metaclass=ABCMeta):
# TODO ABC for test runners?
class TestResult:
- errors = ... # type: List[Tuple[Testable, str]]
- failures = ... # type: List[Tuple[Testable, str]]
+ errors = ... # type: List[Tuple[Testable, str]]
+ failures = ... # type: List[Tuple[Testable, str]]
testsRun = 0
- shouldStop = ... # type: bool
+ shouldStop = ... # type: bool
def wasSuccessful(self) -> bool: ...
def stop(self) -> None: ...
def startTest(self, test: Testable) -> None: ...
def stopTest(self, test: Testable) -> None: ...
- def addError(self, test: Testable,
- err: Tuple[type, Any, Any]) -> None: ... # TODO
- def addFailure(self, test: Testable,
- err: Tuple[type, Any, Any]) -> None: ... # TODO
+ def addError(
+ self, test: Testable, err: Tuple[type, Any, Any]
+ ) -> None: ... # TODO
+ def addFailure(
+ self, test: Testable, err: Tuple[type, Any, Any]
+ ) -> None: ... # TODO
def addSuccess(self, test: Testable) -> None: ...
class TextTestResult(TestResult):
- separator1 = ... # type: str
- separator2 = ... # type: str
+ separator1 = ... # type: str
+ separator2 = ... # type: str
class _AssertRaisesBaseContext:
- expected = ... # type: Any
- failureException = ... # type: type
+ expected = ... # type: Any
+ failureException = ... # type: type
obj_name = ... # type: str
- expected_regex = ... # type: Pattern[str]
+ expected_regex = ... # type: Pattern[str]
class _AssertRaisesContext(_AssertRaisesBaseContext):
- exception = ... # type: Any # TODO precise type
+ exception = ... # type: Any # TODO precise type
def __enter__(self) -> _AssertRaisesContext: ...
def __exit__(self, exc_type, exc_value, tb) -> bool: ...
class _AssertWarnsContext(_AssertRaisesBaseContext):
- warning = ... # type: Any # TODO precise type
+ warning = ... # type: Any # TODO precise type
filename = ... # type: str
lineno = 0
def __enter__(self) -> _AssertWarnsContext: ...
@@ -71,88 +83,152 @@ class TestCase(Testable):
def assert_(self, expr: Any, msg: object = ...) -> None: ...
def failUnless(self, expr: Any, msg: object = ...) -> None: ...
def assertTrue(self, expr: Any, msg: object = ...) -> None: ...
- def assertEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertEquals(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def failUnlessEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertNotEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertNotEquals(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def failIfEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertAlmostEqual(self, first: float, second: float, places: int = ...,
- msg: object = ...,
- delta: float = ...) -> None: ...
- def assertAlmostEquals(self, first: float, second: float, places: int = ...,
- msg: object = ...,
- delta: float = ...) -> None: ...
- def failUnlessAlmostEqual(self, first: float, second: float, places: int = ...,
- msg: object = ...) -> None: ...
- def assertNotAlmostEqual(self, first: float, second: float, places: int = ...,
- msg: object = ...,
- delta: float = ...) -> None: ...
- def assertNotAlmostEquals(self, first: float, second: float, places: int = ...,
- msg: object = ...,
- delta: float = ...) -> None: ...
- def failIfAlmostEqual(self, first: float, second: float, places: int = ...,
- msg: object = ...,
- delta: float = ...) -> None: ...
- def assertGreater(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertGreaterEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertMultiLineEqual(self, first: str, second: str,
- msg: object = ...) -> None: ...
- def assertSequenceEqual(self, first: Sequence[Any], second: Sequence[Any],
- msg: object = ..., seq_type: type = ...) -> None: ...
- def assertListEqual(self, first: List[Any], second: List[Any],
- msg: object = ...) -> None: ...
- def assertTupleEqual(self, first: Tuple[Any, ...], second: Tuple[Any, ...],
- msg: object = ...) -> None: ...
- def assertSetEqual(self, first: Set[Any], second: Set[Any],
- msg: object = ...) -> None: ...
- def assertDictEqual(self, first: Dict[Any, Any], second: Dict[Any, Any],
- msg: object = ...) -> None: ...
- def assertLess(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertLessEqual(self, first: Any, second: Any,
- msg: object = ...) -> None: ...
- def assertRaises(self, expected_exception: type, *args: Any, **kwargs: Any) -> Any: ...
- def failUnlessRaises(self, expected_exception: type, *args: Any, **kwargs: Any) -> Any: ...
+ def assertEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertEquals(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def failUnlessEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertNotEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertNotEquals(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def failIfEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertAlmostEqual(
+ self,
+ first: float,
+ second: float,
+ places: int = ...,
+ msg: object = ...,
+ delta: float = ...,
+ ) -> None: ...
+ def assertAlmostEquals(
+ self,
+ first: float,
+ second: float,
+ places: int = ...,
+ msg: object = ...,
+ delta: float = ...,
+ ) -> None: ...
+ def failUnlessAlmostEqual(
+ self, first: float, second: float, places: int = ..., msg: object = ...
+ ) -> None: ...
+ def assertNotAlmostEqual(
+ self,
+ first: float,
+ second: float,
+ places: int = ...,
+ msg: object = ...,
+ delta: float = ...,
+ ) -> None: ...
+ def assertNotAlmostEquals(
+ self,
+ first: float,
+ second: float,
+ places: int = ...,
+ msg: object = ...,
+ delta: float = ...,
+ ) -> None: ...
+ def failIfAlmostEqual(
+ self,
+ first: float,
+ second: float,
+ places: int = ...,
+ msg: object = ...,
+ delta: float = ...,
+ ) -> None: ...
+ def assertGreater(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertGreaterEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertMultiLineEqual(
+ self, first: str, second: str, msg: object = ...
+ ) -> None: ...
+ def assertSequenceEqual(
+ self,
+ first: Sequence[Any],
+ second: Sequence[Any],
+ msg: object = ...,
+ seq_type: type = ...,
+ ) -> None: ...
+ def assertListEqual(
+ self, first: List[Any], second: List[Any], msg: object = ...
+ ) -> None: ...
+ def assertTupleEqual(
+ self, first: Tuple[Any, ...], second: Tuple[Any, ...], msg: object = ...
+ ) -> None: ...
+ def assertSetEqual(
+ self, first: Set[Any], second: Set[Any], msg: object = ...
+ ) -> None: ...
+ def assertDictEqual(
+ self, first: Dict[Any, Any], second: Dict[Any, Any], msg: object = ...
+ ) -> None: ...
+ def assertLess(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertLessEqual(
+ self, first: Any, second: Any, msg: object = ...
+ ) -> None: ...
+ def assertRaises(
+ self, expected_exception: type, *args: Any, **kwargs: Any
+ ) -> Any: ...
+ def failUnlessRaises(
+ self, expected_exception: type, *args: Any, **kwargs: Any
+ ) -> Any: ...
def failIf(self, expr: Any, msg: object = ...) -> None: ...
def assertFalse(self, expr: Any, msg: object = ...) -> None: ...
- def assertIs(self, first: object, second: object,
- msg: object = ...) -> None: ...
- def assertIsNot(self, first: object, second: object,
- msg: object = ...) -> None: ...
+ def assertIs(
+ self, first: object, second: object, msg: object = ...
+ ) -> None: ...
+ def assertIsNot(
+ self, first: object, second: object, msg: object = ...
+ ) -> None: ...
def assertIsNone(self, expr: Any, msg: object = ...) -> None: ...
def assertIsNotNone(self, expr: Any, msg: object = ...) -> None: ...
- def assertIn(self, first: _T, second: Iterable[_T],
- msg: object = ...) -> None: ...
- def assertNotIn(self, first: _T, second: Iterable[_T],
- msg: object = ...) -> None: ...
- def assertIsInstance(self, obj: Any, cls: type,
- msg: object = ...) -> None: ...
- def assertNotIsInstance(self, obj: Any, cls: type,
- msg: object = ...) -> None: ...
- def assertWarns(self, expected_warning: type, callable_obj: Any = ...,
- *args: Any, **kwargs: Any) -> _AssertWarnsContext: ...
+ def assertIn(
+ self, first: _T, second: Iterable[_T], msg: object = ...
+ ) -> None: ...
+ def assertNotIn(
+ self, first: _T, second: Iterable[_T], msg: object = ...
+ ) -> None: ...
+ def assertIsInstance(
+ self, obj: Any, cls: type, msg: object = ...
+ ) -> None: ...
+ def assertNotIsInstance(
+ self, obj: Any, cls: type, msg: object = ...
+ ) -> None: ...
+ def assertWarns(
+ self,
+ expected_warning: type,
+ callable_obj: Any = ...,
+ *args: Any,
+ **kwargs: Any
+ ) -> _AssertWarnsContext: ...
def fail(self, msg: object = ...) -> None: ...
def countTestCases(self) -> int: ...
def defaultTestResult(self) -> TestResult: ...
def id(self) -> str: ...
- def shortDescription(self) -> str: ... # May return None
+ def shortDescription(self) -> str: ... # May return None
def addCleanup(function: Any, *args: Any, **kwargs: Any) -> None: ...
def skipTest(self, reason: Any) -> None: ...
class CallableTestCase(Testable):
- def __init__(self, testFunc: Callable[[], None],
- setUp: Callable[[], None] = ...,
- tearDown: Callable[[], None] = ...,
- description: str = ...) -> None: ...
+ def __init__(
+ self,
+ testFunc: Callable[[], None],
+ setUp: Callable[[], None] = ...,
+ tearDown: Callable[[], None] = ...,
+ description: str = ...,
+ ) -> None: ...
def run(self, result: TestResult) -> None: ...
def debug(self) -> None: ...
def countTestCases(self) -> int: ...
@@ -169,23 +245,30 @@ class TestSuite(Testable):
# TODO defaultTestLoader
class TextTestRunner:
- resultclass = ... # type: Type[TestResult]
- def __init__(self, stream: IO[str] = ..., descriptions: bool = ...,
- verbosity: int = ..., failfast: bool = ...) -> None: ...
+ resultclass = ... # type: Type[TestResult]
+ def __init__(
+ self,
+ stream: IO[str] = ...,
+ descriptions: bool = ...,
+ verbosity: int = ...,
+ failfast: bool = ...,
+ ) -> None: ...
def run(self, test: Testable) -> TestResult: ...
-class SkipTest(Exception):
- ...
+class SkipTest(Exception): ...
# TODO precise types
def skipUnless(condition: Any, reason: str) -> Any: ...
def skipIf(condition: Any, reason: str) -> Any: ...
def expectedFailure(func: _FT) -> _FT: ...
def skip(reason: str) -> Any: ...
-
-def main(module: str = ..., defaultTest: str = ...,
- argv: List[str] = ..., testRunner: Any = ...,
- testLoader: Any = ...) -> None: ... # TODO types
+def main(
+ module: str = ...,
+ defaultTest: str = ...,
+ argv: List[str] = ...,
+ testRunner: Any = ...,
+ testLoader: Any = ...,
+) -> None: ... # TODO types
# private but occasionally used
util = ... # type: module
diff --git a/stubs/xml/dom/minidom.pyi b/stubs/xml/dom/minidom.pyi
index 7c292a7a..70fc235b 100644
--- a/stubs/xml/dom/minidom.pyi
+++ b/stubs/xml/dom/minidom.pyi
@@ -4,31 +4,18 @@ from typing import Any, Optional, Union
_basic_type = Union[int, float, bool, str]
-
class Node(xml.dom.Node):
- nodeName =... # type: str
-
- def getElementsByTagName(self, name: str) -> list[Element]:
- ...
-
- def getAttribute(self, attname: str) -> _basic_type:
- ...
-
+ nodeName = ... # type: str
-class DocumentLS(object):
- ...
+ def getElementsByTagName(self, name: str) -> list[Element]: ...
+ def getAttribute(self, attname: str) -> _basic_type: ...
-
-class Element(Node):
- ...
+class DocumentLS(object): ...
+class Element(Node): ...
NodeList = list[Element]
-
class Document(Node, DocumentLS):
- childNodes =... # type: NodeList
-
-
+ childNodes = ... # type: NodeList
-def parseString(string: str, parser: Optional[Any]=...) -> Document:
- ...
+def parseString(string: str, parser: Optional[Any] = ...) -> Document: ...