[master] Fix the way categories are handled in text mode.
by Samantha N. Bueno
This statically maintained list of spoke categories for text mode has
been bothering me for almost a year, so it's time to get rid of it.
This now more or less mirrors the GUI logic we have for handling spoke
categories. I wanted to originally make the category classes something
that could be used by both GUI and TUI to reduce duplication, but it is
not meant to be.
This also moves the collectCategoriesAndSpokes, collect_categories, and
collect_spokes functions into pyanaconda.ui.common since I could at
least consolidate that.
---
po/POTFILES.in | 6 ++++
pyanaconda/ui/common.py | 46 +++++++++++++++++++++++++++
pyanaconda/ui/gui/categories/__init__.py | 13 +-------
pyanaconda/ui/gui/hubs/__init__.py | 23 ++------------
pyanaconda/ui/gui/spokes/__init__.py | 20 +-----------
pyanaconda/ui/tui/__init__.py | 4 +++
pyanaconda/ui/tui/categories/Makefile.am | 22 +++++++++++++
pyanaconda/ui/tui/categories/__init__.py | 46 +++++++++++++++++++++++++++
pyanaconda/ui/tui/categories/customization.py | 32 +++++++++++++++++++
pyanaconda/ui/tui/categories/localization.py | 31 ++++++++++++++++++
pyanaconda/ui/tui/categories/software.py | 31 ++++++++++++++++++
pyanaconda/ui/tui/categories/system.py | 32 +++++++++++++++++++
pyanaconda/ui/tui/categories/user_settings.py | 32 +++++++++++++++++++
pyanaconda/ui/tui/hubs/__init__.py | 18 +++++------
pyanaconda/ui/tui/hubs/summary.py | 2 --
pyanaconda/ui/tui/spokes/__init__.py | 24 ++------------
pyanaconda/ui/tui/spokes/askvnc.py | 2 --
pyanaconda/ui/tui/spokes/network.py | 3 +-
pyanaconda/ui/tui/spokes/password.py | 3 +-
pyanaconda/ui/tui/spokes/shell_spoke.py | 3 +-
pyanaconda/ui/tui/spokes/software.py | 3 +-
pyanaconda/ui/tui/spokes/source.py | 11 ++++---
pyanaconda/ui/tui/spokes/storage.py | 7 ++--
pyanaconda/ui/tui/spokes/time_spoke.py | 3 +-
pyanaconda/ui/tui/spokes/user.py | 3 +-
25 files changed, 319 insertions(+), 101 deletions(-)
create mode 100644 pyanaconda/ui/tui/categories/Makefile.am
create mode 100644 pyanaconda/ui/tui/categories/__init__.py
create mode 100644 pyanaconda/ui/tui/categories/customization.py
create mode 100644 pyanaconda/ui/tui/categories/localization.py
create mode 100644 pyanaconda/ui/tui/categories/software.py
create mode 100644 pyanaconda/ui/tui/categories/system.py
create mode 100644 pyanaconda/ui/tui/categories/user_settings.py
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 358534c..e48469e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -62,6 +62,12 @@ pyanaconda/ui/tui/spokes/warnings.py
pyanaconda/ui/tui/spokes/progress.py
pyanaconda/ui/tui/spokes/__init__.py
pyanaconda/ui/tui/__init__.py
+pyanaconda/ui/tui/categories/__init__.py
+pyanaconda/ui/tui/categories/customization.py
+pyanaconda/ui/tui/categories/localization.py
+pyanaconda/ui/tui/categories/software.py
+pyanaconda/ui/tui/categories/system.py
+pyanaconda/ui/tui/categories/user_settings.py
# Graphical interface
pyanaconda/ui/gui/__init__.py
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py
index ddd5016..afca06a 100644
--- a/pyanaconda/ui/common.py
+++ b/pyanaconda/ui/common.py
@@ -673,3 +673,49 @@ def collect(module_pattern, path, pred):
return retval
+def collect_spokes(mask_paths, category):
+ """Return a list of all spoke subclasses that should appear for a given
+ category. Look for them in files imported as module_path % basename(f)
+
+ :param mask_paths: list of mask, path tuples to search for classes
+ :type mask_paths: list of (mask, path)
+
+ :return: list of Spoke classes belonging to category
+ :rtype: list of Spoke classes
+
+ """
+ spokes = []
+ for mask, path in mask_paths:
+ spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category.__name__ == category))
+
+ return spokes
+
+def collect_categories(mask_paths):
+ """Return a list of all category subclasses. Look for them in modules
+ imported as module_mask % basename(f) where f is name of all files in path.
+ """
+ categories = []
+ for mask, path in mask_paths:
+ categories.extend(collect(mask, path, lambda obj: getattr(obj, "displayOnHub", None) != None))
+
+ return categories
+
+def collectCategoriesAndSpokes(paths, klass):
+ """collects categories and spokes to be displayed on this Hub
+
+ :param paths: dictionary mapping categories, spokes, and hubs to their
+ their respective search path(s)
+ :return: dictionary mapping category class to list of spoke classes
+ :rtype: dictionary[category class] -> [ list of spoke classes ]
+ """
+
+ ret = {}
+
+ # Collect all the categories this hub displays, then collect all the
+ # spokes belonging to all those categories.
+ categories = sorted(filter(lambda c: c.displayOnHub == klass, collect_categories(paths["categories"])),
+ key=lambda c: c.sortOrder)
+ for c in categories:
+ ret[c] = collect_spokes(paths["spokes"], c.__name__)
+
+ return ret
diff --git a/pyanaconda/ui/gui/categories/__init__.py b/pyanaconda/ui/gui/categories/__init__.py
index 66bd5d4..06e431a 100644
--- a/pyanaconda/ui/gui/categories/__init__.py
+++ b/pyanaconda/ui/gui/categories/__init__.py
@@ -21,9 +21,8 @@
import os.path
from pyanaconda.i18n import N_
-from pyanaconda.ui.common import collect
-__all__ = ["SpokeCategory", "collect_categories"]
+__all__ = ["SpokeCategory"]
class SpokeCategory(object):
"""A SpokeCategory is an object used to group multiple related Spokes
@@ -45,13 +44,3 @@ class SpokeCategory(object):
displayOnHub = None
sortOrder = 1000
title = N_("DEFAULT TITLE")
-
-def collect_categories(mask_paths):
- """Return a list of all category subclasses. Look for them in modules
- imported as module_mask % basename(f) where f is name of all files in path.
- """
- categories = []
- for mask, path in mask_paths:
- categories.extend(collect(mask, path, lambda obj: getattr(obj, "displayOnHub", None) != None))
-
- return categories
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index 7dcdcdb..f7fc172 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -29,8 +29,7 @@ from pyanaconda.product import distributionText
from pyanaconda.ui import common
from pyanaconda.ui.gui import GUIObject
-from pyanaconda.ui.gui.categories import collect_categories
-from pyanaconda.ui.gui.spokes import StandaloneSpoke, collect_spokes
+from pyanaconda.ui.gui.spokes import StandaloneSpoke
from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup
from pyanaconda.constants import ANACONDA_ENVIRON
@@ -130,29 +129,11 @@ class Hub(GUIObject, common.Hub):
action.exit_logger()
- def _collectCategoriesAndSpokes(self):
- """collects categories and spokes to be displayed on this Hub
-
- :return: dictionary mapping category class to list of spoke classes
- :rtype: dictionary[category class] -> [ list of spoke classes ]
- """
-
- ret = {}
-
- # Collect all the categories this hub displays, then collect all the
- # spokes belonging to all those categories.
- categories = sorted(filter(lambda c: c.displayOnHub == self.__class__, collect_categories(self.paths["categories"])),
- key=lambda c: c.sortOrder)
- for c in categories:
- ret[c] = collect_spokes(self.paths["spokes"], c.__name__)
-
- return ret
-
def _createBox(self):
from gi.repository import Gtk, AnacondaWidgets
from pyanaconda.ui.gui.utils import setViewportBackground
- cats_and_spokes = self._collectCategoriesAndSpokes()
+ cats_and_spokes = common.collectCategoriesAndSpokes(self.paths, self.__class__)
categories = cats_and_spokes.keys()
grid = Gtk.Grid(row_spacing=6, column_spacing=6, column_homogeneous=True,
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index f3fc74f..f9d11e8 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -20,11 +20,10 @@
# Martin Sivak <msivak(a)redhat.com>
from pyanaconda.ui import common
-from pyanaconda.ui.common import collect
from pyanaconda.ui.gui import GUIObject
import os.path
-__all__ = ["StandaloneSpoke", "NormalSpoke", "collect_spokes"]
+__all__ = ["StandaloneSpoke", "NormalSpoke"]
class Spoke(GUIObject):
def __init__(self, data):
@@ -93,20 +92,3 @@ class NormalSpoke(Spoke, common.NormalSpoke):
self.window.hide()
Gtk.main_quit()
-
-def collect_spokes(mask_paths, category):
- """Return a list of all spoke subclasses that should appear for a given
- category. Look for them in files imported as module_path % basename(f)
-
- :param mask_paths: list of mask, path tuples to search for classes
- :type mask_paths: list of (mask, path)
-
- :return: list of Spoke classes belonging to category
- :rtype: list of Spoke classes
-
- """
- spokes = []
- for mask, path in mask_paths:
- spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category.__name__ == category))
-
- return spokes
diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py
index 402d625..4cf3690 100644
--- a/pyanaconda/ui/tui/__init__.py
+++ b/pyanaconda/ui/tui/__init__.py
@@ -105,6 +105,9 @@ class TextUserInterface(ui.UserInterface):
pathlist = set([updatepath, basepath] + sitepackages)
paths = ui.UserInterface.paths + {
+ "categories": [(basemask + ".categories.%s",
+ os.path.join(path, "categories"))
+ for path in pathlist],
"spokes": [(basemask + ".spokes.%s",
os.path.join(path, "spokes"))
for path in pathlist],
@@ -162,6 +165,7 @@ class TextUserInterface(ui.UserInterface):
if hasattr(obj, "set_path"):
obj.set_path("spokes", self.paths["spokes"])
+ obj.set_path("categories", self.paths["categories"])
should_schedule = obj.setup(self.ENVIRONMENT)
diff --git a/pyanaconda/ui/tui/categories/Makefile.am b/pyanaconda/ui/tui/categories/Makefile.am
new file mode 100644
index 0000000..0ad7913
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/Makefile.am
@@ -0,0 +1,22 @@
+# Copyright (C) 2011 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Chris Lumens <clumens(a)redhat.com>
+
+MAINTAINERCLEANFILES = Makefile.in
+
+pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME)
+categoriesdir = $(pkgpyexecdir)/ui/tui/categories
+categories_PYTHON = $(srcdir)/*.py
diff --git a/pyanaconda/ui/tui/categories/__init__.py b/pyanaconda/ui/tui/categories/__init__.py
new file mode 100644
index 0000000..06e431a
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/__init__.py
@@ -0,0 +1,46 @@
+# Base classes for spoke categories.
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+#
+
+import os.path
+from pyanaconda.i18n import N_
+
+__all__ = ["SpokeCategory"]
+
+class SpokeCategory(object):
+ """A SpokeCategory is an object used to group multiple related Spokes
+ together on a hub. It consists of a title displayed above, and then
+ a two-column grid of SpokeSelectors. Each SpokeSelector is associated
+ with a Spoke subclass. A SpokeCategory will only display those Spokes
+ with a matching category attribute.
+
+ Class attributes:
+
+ displayOnHub -- The Hub subclass to display this Category on. If
+ None, this Category will be skipped.
+ sortOrder -- A number indicating the order in which this Category
+ will be displayed. A lower number indicates display
+ higher up in the Hub.
+ title -- The title of this SpokeCategory, to be displayed above
+ the grid.
+ """
+ displayOnHub = None
+ sortOrder = 1000
+ title = N_("DEFAULT TITLE")
diff --git a/pyanaconda/ui/tui/categories/customization.py b/pyanaconda/ui/tui/categories/customization.py
new file mode 100644
index 0000000..b845e39
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/customization.py
@@ -0,0 +1,32 @@
+# Localization category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+# Martin Sivak <msivak(a)redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["CustomizationCategory"]
+
+class CustomizationCategory(SpokeCategory):
+ displayOnHub = SummaryHub
+ sortOrder = 100
+ title = N_("CUSTOMIZATION")
diff --git a/pyanaconda/ui/tui/categories/localization.py b/pyanaconda/ui/tui/categories/localization.py
new file mode 100644
index 0000000..b87ce40
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/localization.py
@@ -0,0 +1,31 @@
+# Localization category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["LocalizationCategory"]
+
+class LocalizationCategory(SpokeCategory):
+ displayOnHub = SummaryHub
+ sortOrder = 100
+ title = N_("LOCALIZATION")
diff --git a/pyanaconda/ui/tui/categories/software.py b/pyanaconda/ui/tui/categories/software.py
new file mode 100644
index 0000000..e15b7a9
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/software.py
@@ -0,0 +1,31 @@
+# Software category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["SoftwareCategory"]
+
+class SoftwareCategory(SpokeCategory):
+ displayOnHub = SummaryHub
+ sortOrder = 200
+ title = N_("SOFTWARE")
diff --git a/pyanaconda/ui/tui/categories/system.py b/pyanaconda/ui/tui/categories/system.py
new file mode 100644
index 0000000..4613c9b
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/system.py
@@ -0,0 +1,32 @@
+# Storage category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+# David Lehman <dlehman(a)redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["SystemCategory"]
+
+class SystemCategory(SpokeCategory):
+ displayOnHub = SummaryHub
+ sortOrder = 300
+ title = N_("SYSTEM")
diff --git a/pyanaconda/ui/tui/categories/user_settings.py b/pyanaconda/ui/tui/categories/user_settings.py
new file mode 100644
index 0000000..0ef3612
--- /dev/null
+++ b/pyanaconda/ui/tui/categories/user_settings.py
@@ -0,0 +1,32 @@
+# User settings category classes
+#
+# Copyright (C) 2011, 2013 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details. You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens(a)redhat.com>
+# Martin Sivak <msivak(a)redhat.com>
+#
+
+from pyanaconda.i18n import N_
+from pyanaconda.ui.tui.categories import SpokeCategory
+from pyanaconda.ui.tui.hubs.summary import SummaryHub
+
+__all__ = ["UserSettingsCategory"]
+
+class UserSettingsCategory(SpokeCategory):
+ displayOnHub = SummaryHub
+ sortOrder = 200
+ title = N_("USER SETTINGS")
diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py
index e886b63..a1ee93e 100644
--- a/pyanaconda/ui/tui/hubs/__init__.py
+++ b/pyanaconda/ui/tui/hubs/__init__.py
@@ -20,7 +20,6 @@
#
from pyanaconda.ui.tui import simpleline as tui
from pyanaconda.ui.tui.tuiobject import TUIObject
-from pyanaconda.ui.tui.spokes import collect_spokes
from pyanaconda.ui import common
from pyanaconda.i18n import _, C_, N_
@@ -50,17 +49,18 @@ class TUIHub(TUIObject, common.Hub):
self._spoke_count = 0
def setup(self, environment="anaconda"):
- # look for spokes having category present in self.categories
- for c in self.categories:
- spokes = collect_spokes(self.paths["spokes"], c)
+ cats_and_spokes = common.collectCategoriesAndSpokes(self.paths, self.__class__)
+ categories = cats_and_spokes.keys()
- # sort them according to their priority
- for s in sorted(spokes, key = lambda s: s.title):
+ for c in sorted(categories, key=lambda c: c.title):
+ obj = c()
+
+ for spokeClass in sorted(cats_and_spokes[c], key=lambda s: s.title):
# Check if this spoke is to be shown in anaconda
- if not s.should_run(environment, self.data):
+ if not spokeClass.should_run(environment, self.data):
continue
- spoke = s(self.app, self.data, self.storage, self.payload, self.instclass)
+ spoke = spokeClass(self.app, self.data, self.storage, self.payload, self.instclass)
spoke.initialize()
if not spoke.showable:
@@ -73,7 +73,7 @@ class TUIHub(TUIObject, common.Hub):
self._spoke_count += 1
self._keys[self._spoke_count] = spoke
- self._spokes[spoke.__class__.__name__] = spoke
+ self._spokes[spokeClass.__name__] = spoke
# only schedule the hub if it has some spokes
return self._spoke_count != 0
diff --git a/pyanaconda/ui/tui/hubs/summary.py b/pyanaconda/ui/tui/hubs/summary.py
index 936812c..3b04c90 100644
--- a/pyanaconda/ui/tui/hubs/summary.py
+++ b/pyanaconda/ui/tui/hubs/summary.py
@@ -32,8 +32,6 @@ log = logging.getLogger("anaconda")
class SummaryHub(TUIHub):
title = N_("Installation")
- ## FIXME: this should be pulling data from somewhere, not just a static list
- categories = ["localization", "software", "system", "user"]
def setup(self, environment="anaconda"):
should_schedule = TUIHub.setup(self, environment=environment)
diff --git a/pyanaconda/ui/tui/spokes/__init__.py b/pyanaconda/ui/tui/spokes/__init__.py
index 57984ba..51eb6d3 100644
--- a/pyanaconda/ui/tui/spokes/__init__.py
+++ b/pyanaconda/ui/tui/spokes/__init__.py
@@ -20,7 +20,7 @@
#
from pyanaconda.ui.tui import simpleline as tui
from pyanaconda.ui.tui.tuiobject import TUIObject, YesNoDialog
-from pyanaconda.ui.common import Spoke, StandaloneSpoke, NormalSpoke, collect
+from pyanaconda.ui.common import Spoke, StandaloneSpoke, NormalSpoke
from pyanaconda.users import validatePassword, cryptPassword
import re
from collections import namedtuple
@@ -29,8 +29,7 @@ from pyanaconda.i18n import N_, _
from pyanaconda.constants import PASSWORD_CONFIRM_ERROR_TUI
__all__ = ["TUISpoke", "EditTUISpoke", "EditTUIDialog", "EditTUISpokeEntry",
- "StandaloneSpoke", "NormalTUISpoke", "collect_spokes",
- "collect_categories"]
+ "StandaloneSpoke", "NormalTUISpoke"]
# Inherit abstract methods from Spoke
# pylint: disable=W0223
@@ -47,7 +46,6 @@ class TUISpoke(TUIObject, tui.Widget, Spoke):
"""
title = N_("Default spoke title")
- category = u""
def __init__(self, app, data, storage, payload, instclass):
if self.__class__ is TUISpoke:
@@ -311,21 +309,3 @@ class EditTUISpoke(NormalTUISpoke):
class StandaloneTUISpoke(TUISpoke, StandaloneSpoke):
pass
-
-def collect_spokes(mask_paths, category):
- """Return a list of all spoke subclasses that should appear for a given
- category.
- """
- spokes = []
- for mask, path in mask_paths:
- spokes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category == category))
-
- return spokes
-
-def collect_categories(mask_paths):
- classes = []
- for mask, path in mask_paths:
- classes.extend(collect(mask, path, lambda obj: hasattr(obj, "category") and obj.category != None and obj.category != ""))
-
- categories = set(c.category for c in classes)
- return categories
diff --git a/pyanaconda/ui/tui/spokes/askvnc.py b/pyanaconda/ui/tui/spokes/askvnc.py
index 19c0d50..dc9273e 100644
--- a/pyanaconda/ui/tui/spokes/askvnc.py
+++ b/pyanaconda/ui/tui/spokes/askvnc.py
@@ -36,7 +36,6 @@ def exception_msg_handler_and_exit(event, data):
class AskVNCSpoke(NormalTUISpoke):
title = N_("VNC")
- category = "vnc"
# This spoke is kinda standalone, not meant to be used with a hub
# We pass in some fake data just to make our parents happy
@@ -109,7 +108,6 @@ class AskVNCSpoke(NormalTUISpoke):
class VNCPassSpoke(NormalTUISpoke):
title = N_("VNC Password")
- category = "vnc"
def __init__(self, app, data, storage, payload, instclass, message=None):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py
index 8ede756..fe74446 100644
--- a/pyanaconda/ui/tui/spokes/network.py
+++ b/pyanaconda/ui/tui/spokes/network.py
@@ -22,6 +22,7 @@
from pyanaconda.flags import can_touch_runtime_system
+from pyanaconda.ui.tui.categories.system import SystemCategory
from pyanaconda.ui.tui.spokes import EditTUISpoke, OneShotEditTUIDialog
from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
@@ -40,7 +41,7 @@ __all__ = ["NetworkSpoke"]
class NetworkSpoke(EditTUISpoke):
""" Spoke used to configure network settings. """
title = N_("Network configuration")
- category = "system"
+ category = SystemCategory
def __init__(self, app, data, storage, payload, instclass):
EditTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/password.py b/pyanaconda/ui/tui/spokes/password.py
index 870fff4..bb98418 100644
--- a/pyanaconda/ui/tui/spokes/password.py
+++ b/pyanaconda/ui/tui/spokes/password.py
@@ -21,6 +21,7 @@
# Chris Lumens <clumens(a)redhat.com>
#
+from pyanaconda.ui.tui.categories.user_settings import UserSettingsCategory
from pyanaconda.ui.tui.spokes import EditTUIDialog, EditTUISpokeEntry
from pyanaconda.ui.common import FirstbootSpokeMixIn
from pyanaconda.ui.tui.simpleline import TextWidget
@@ -29,7 +30,7 @@ from pyanaconda.i18n import N_, _
class PasswordSpoke(FirstbootSpokeMixIn, EditTUIDialog):
title = N_("Root password")
- category = "user"
+ category = UserSettingsCategory
def __init__(self, app, data, storage, payload, instclass):
EditTUIDialog.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/shell_spoke.py b/pyanaconda/ui/tui/spokes/shell_spoke.py
index 4915f98..b355387 100644
--- a/pyanaconda/ui/tui/spokes/shell_spoke.py
+++ b/pyanaconda/ui/tui/spokes/shell_spoke.py
@@ -20,6 +20,7 @@
"""Text mode shell spoke"""
+from pyanaconda.ui.tui.categories.system import SystemCategory
from pyanaconda.ui.tui.spokes import NormalTUISpoke
from pyanaconda.ui.tui.simpleline.widgets import TextWidget
from pyanaconda.i18n import N_, _
@@ -31,7 +32,7 @@ import subprocess
class ShellSpoke(NormalTUISpoke):
title = N_("Shell")
- category = "system"
+ category = SystemCategory
@classmethod
def should_run(cls, environment, data):
diff --git a/pyanaconda/ui/tui/spokes/software.py b/pyanaconda/ui/tui/spokes/software.py
index 2b414d6..e63242f 100644
--- a/pyanaconda/ui/tui/spokes/software.py
+++ b/pyanaconda/ui/tui/spokes/software.py
@@ -20,6 +20,7 @@
#
from pyanaconda.flags import flags
+from pyanaconda.ui.tui.categories.software import SoftwareCategory
from pyanaconda.ui.tui.spokes import NormalTUISpoke
from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget, CheckboxWidget
from pyanaconda.threads import threadMgr, AnacondaThread
@@ -36,7 +37,7 @@ __all__ = ["SoftwareSpoke"]
class SoftwareSpoke(NormalTUISpoke):
""" Spoke used to read new value of text to represent source repo. """
title = N_("Software selection")
- category = "software"
+ category = SoftwareCategory
def __init__(self, app, data, storage, payload, instclass):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/source.py b/pyanaconda/ui/tui/spokes/source.py
index 45381df..0aaf4c2 100644
--- a/pyanaconda/ui/tui/spokes/source.py
+++ b/pyanaconda/ui/tui/spokes/source.py
@@ -20,6 +20,7 @@
#
from pyanaconda.flags import flags
+from pyanaconda.ui.tui.categories.software import SoftwareCategory
from pyanaconda.ui.tui.spokes import EditTUISpoke, NormalTUISpoke
from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
@@ -51,7 +52,7 @@ __all__ = ["SourceSpoke"]
class SourceSpoke(EditTUISpoke, SourceSwitchHandler):
""" Spoke used to customize the install source repo. """
title = N_("Installation source")
- category = "software"
+ category = SoftwareCategory
_protocols = (N_("Closest mirror"), "http://", "https://", "ftp://", "nfs")
@@ -254,7 +255,7 @@ class SourceSpoke(EditTUISpoke, SourceSwitchHandler):
class SpecifyRepoSpoke(EditTUISpoke, SourceSwitchHandler):
""" Specify the repo URL here if closest mirror not selected. """
title = N_("Specify Repo Options")
- category = "software"
+ category = SoftwareCategory
edit_fields = [
Entry(N_("Repo URL"), "url", re.compile(".*$"), True)
@@ -292,7 +293,7 @@ class SpecifyRepoSpoke(EditTUISpoke, SourceSwitchHandler):
class SpecifyNFSRepoSpoke(EditTUISpoke, SourceSwitchHandler):
""" Specify server and mount opts here if NFS selected. """
title = N_("Specify Repo Options")
- category = "software"
+ category = SoftwareCategory
edit_fields = [
Entry(N_("NFS <server>:/<path>"), "server", re.compile(".*$"), True),
@@ -339,7 +340,7 @@ class SpecifyNFSRepoSpoke(EditTUISpoke, SourceSwitchHandler):
class SelectDeviceSpoke(NormalTUISpoke):
""" Select device containing the install source ISO file. """
title = N_("Select device containing the ISO file")
- category = "source"
+ category = SoftwareCategory
def __init__(self, app, data, storage, payload, instclass):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -426,7 +427,7 @@ class SelectDeviceSpoke(NormalTUISpoke):
class SelectISOSpoke(NormalTUISpoke, SourceSwitchHandler):
""" Select an ISO to use as install source. """
title = N_("Select an ISO to use as install source")
- category = "source"
+ category = SoftwareCategory
def __init__(self, app, data, storage, payload, instclass, device):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index cb43d98..9e47f81 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -23,6 +23,7 @@
#
from pyanaconda.ui.lib.disks import getDisks, applyDiskSelection
+from pyanaconda.ui.tui.categories.system import SystemCategory
from pyanaconda.ui.tui.spokes import NormalTUISpoke
from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget
from pyanaconda.ui.tui.tuiobject import YesNoDialog
@@ -66,7 +67,7 @@ class StorageSpoke(NormalTUISpoke):
as disk selection, partitioning, and fs type.
"""
title = N_("Installation Destination")
- category = "system"
+ category = SystemCategory
def __init__(self, app, data, storage, payload, instclass):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -433,7 +434,7 @@ class StorageSpoke(NormalTUISpoke):
class AutoPartSpoke(NormalTUISpoke):
""" Autopartitioning options are presented here. """
title = N_("Autopartitioning Options")
- category = "system"
+ category = SystemCategory
def __init__(self, app, data, storage, payload, instclass):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
@@ -499,7 +500,7 @@ class AutoPartSpoke(NormalTUISpoke):
class PartitionSchemeSpoke(NormalTUISpoke):
""" Spoke to select what partitioning scheme to use on disk(s). """
title = N_("Partition Scheme Options")
- category = "system"
+ category = SystemCategory
# set default FS to LVM, for consistency with graphical behavior
_selection = 1
diff --git a/pyanaconda/ui/tui/spokes/time_spoke.py b/pyanaconda/ui/tui/spokes/time_spoke.py
index 75c1244..ec0f8f4 100644
--- a/pyanaconda/ui/tui/spokes/time_spoke.py
+++ b/pyanaconda/ui/tui/spokes/time_spoke.py
@@ -19,6 +19,7 @@
# Red Hat Author(s): Martin Sivak <msivak(a)redhat.com>
#
+from pyanaconda.ui.tui.categories.localization import LocalizationCategory
from pyanaconda.ui.tui.spokes import NormalTUISpoke
from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
from pyanaconda.ui.common import FirstbootSpokeMixIn
@@ -28,7 +29,7 @@ from pyanaconda.constants_text import INPUT_PROCESSED
class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
title = N_("Timezone settings")
- category = "localization"
+ category = LocalizationCategory
def __init__(self, app, data, storage, payload, instclass):
NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
diff --git a/pyanaconda/ui/tui/spokes/user.py b/pyanaconda/ui/tui/spokes/user.py
index f069eec..4871ab7 100644
--- a/pyanaconda/ui/tui/spokes/user.py
+++ b/pyanaconda/ui/tui/spokes/user.py
@@ -20,6 +20,7 @@
# Chris Lumens <clumens(a)redhat.com>
#
+from pyanaconda.ui.tui.categories.user_settings import UserSettingsCategory
from pyanaconda.ui.tui.spokes import EditTUISpoke
from pyanaconda.ui.tui.spokes import EditTUISpokeEntry as Entry
from pyanaconda.ui.common import FirstbootSpokeMixIn
@@ -34,7 +35,7 @@ __all__ = ["UserSpoke"]
class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
title = N_("User creation")
- category = "user"
+ category = UserSettingsCategory
edit_fields = [
Entry("Create user", "_create", EditTUISpoke.CHECK, True),
--
1.8.3.1
9 years, 11 months
[PATCH 0/6] input validation for dialogs
by David Shea
These patches add a new InputCheckHandler subclass to set error tooltips on
dialog entries in the same way that we currently display the error messages for
LUKS passphrase input. The dialogs for NTP configuration, HTTP proxy and
advanced user settings have been modified to use this subclass.
Aaaaaaand then there's that first patch that combines the nastiness of URL
validation with the Python's unfortunate re syntax. PROXY_URL_PARSE rejected a
lot of actually-valid URLs and lacked IPv6 support, so this change corrects
those deficiencies and expands the parsing to the query and fragment portions
of URLs, which may be useful in the future for handling the URLs elsewhere in
the source spoke.
David Shea (6):
Generalized and improved the proxy URL parsing regex
Add an InputCheckHandler subclass for dialogs.
Add a new InputCheck status for silent failures.
Use GUIDialogInputCheckHandler in the advanced user dialog
Provide feedback for invalid NTP hostnames.
Validate proxy URLs
pyanaconda/iutil.py | 48 +++----
pyanaconda/network.py | 18 +--
pyanaconda/regexes.py | 59 ++++++++-
pyanaconda/ui/gui/spokes/advanced_user.glade | 12 --
pyanaconda/ui/gui/spokes/datetime_spoke.py | 45 +++++--
pyanaconda/ui/gui/spokes/source.py | 55 +++++---
pyanaconda/ui/gui/spokes/user.py | 21 +--
pyanaconda/ui/helpers.py | 39 +++++-
tests/regex_tests/hostname_test.py | 190 +++++++++++++++++++++++++++
tests/regex_tests/proxy_test.py | 119 -----------------
tests/regex_tests/url_test.py | 183 ++++++++++++++++++++++++++
11 files changed, 580 insertions(+), 209 deletions(-)
create mode 100644 tests/regex_tests/hostname_test.py
delete mode 100644 tests/regex_tests/proxy_test.py
create mode 100644 tests/regex_tests/url_test.py
--
1.9.0
10 years
[blivet] More lvm carnage
by David Lehman
Both are fairly self-explanatory, I think. Patch 2 addresses some things
broken by the container actions set and some from the more recent lvm
info gathering refactor.
10 years
[anaconda][initial-setup][rhel7-branch][PATCH] Fix translations in Initial Setup
by Martin Kolman
As can be seen in the de_DE or pt_BR locale, Initial Setup current does not translate
many strings in the GUI even though they are properly translated in the PO files.
Turns out all strings specified in Glade and the spoke title are affected.
It is caused by Initial Setup using Hub and Spoke classes from Anaconda,
which all have the "anaconda" translation domain hardcoded.
Like this translations are looked up in the "anaconda" instead of the
"initial-setup" domain and the strings remain untranslated.
Fortunately, the fix turned out to be pretty simple. :)
The patch for Anaconda adds support for overriding the Hub translation domain
by subclasses and also forwards the spoke domain to gettext when translating spoke title.
The patch for Initial Setup makes sure all Hub and Spoke subclasses
override the translation domain with the "initial-setup" one.
This together makes sure all strings are properly translated.
Martin Kolman (1):
Make it possible to override translation domain in GUIObjects
(#1040240)
pyanaconda/ui/gui/__init__.py | 3 ++-
pyanaconda/ui/gui/hubs/__init__.py | 13 ++++++++++++-
2 files changed, 14 insertions(+), 2 deletions(-)
--
1.8.5.3
10 years
[blivet:master 00/14] Miscellaneous pylint inspired patches
by Anne Mulhern
These are all fairly simple and were all inspired by some pylint error.
mulhern (14):
Change to a semantically equivalent version of FileDevice.path
Change param default [] to None and convert in method
Indent with spaces, not tabs
Move lines beneath __main__ into a main method
Omit compile flag
Keep first parameter "self"
Remove appendiceal assignment
Put docstring in class
Remove unused variable but retain call for its side-effects
Get rid of writeRandomUUID.
Suppress unused variable warning
Do not import logging twice.
Raise exceptions using the new syntax
Rearrange _startEdd to be a little more obvious.
blivet/__init__.py | 2 +-
blivet/arch.py | 3 ---
blivet/devicelibs/lvm.py | 5 ++++-
blivet/devicelibs/mdraid.py | 7 +++++--
blivet/devices.py | 12 +++---------
blivet/fcoe.py | 8 ++------
blivet/formats/fs.py | 19 -------------------
blivet/tsort.py | 7 +++----
scripts/makeupdates | 10 ++++++----
tests/action_test.py | 7 ++++---
tests/devicelibs_test/baseclass.py | 8 ++++----
tests/devicelibs_test/btrfs_test.py | 4 ++--
tests/storagetestcase.py | 14 ++++++--------
13 files changed, 40 insertions(+), 66 deletions(-)
--
1.8.3.1
10 years
[blivet] refactor lvm info gathering
by David Lehman
This slightly improves how we gather information from lvm about
existing devices. It then adds caching to consolidate calls to lvm
utilities on systems with many PVs and/or VGs.
This version does not enable caching for anaconda, but I think I
should remove that piece. Does anyone object? What it means is that
we'll only run pvs and lvs once per call to DeviceTree.populate,
regardless of the number of PVs or VGs present. I think we call
populate after any operation that could add a new PV in anaconda, so
it should be safe.
Jan, if you're reading this, I'd also be open to adding a way to tell
the devicetree not to drop the cache on populate. Let me know if that
sounds useful.
10 years
[blivet:master] Adapt existing tests to changed signature of some methods
by Anne Mulhern
Signed-off-by: mulhern <amulhern(a)redhat.com>
---
tests/devicelibs_test/lvm_test.py | 8 ++++----
tests/devicelibs_test/mdraid_test.py | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/tests/devicelibs_test/lvm_test.py b/tests/devicelibs_test/lvm_test.py
index 91b71f6..655b5ff 100755
--- a/tests/devicelibs_test/lvm_test.py
+++ b/tests/devicelibs_test/lvm_test.py
@@ -57,7 +57,7 @@ class LVMAsRootTestCase(baseclass.DevicelibsTestCase):
try:
lvm.vgdeactivate(self._vg_name)
- lvm.vgreduce(self._vg_name, [], rm=True)
+ lvm.vgreduce(self._vg_name, None, missing=True)
except lvm.LVMError:
pass
@@ -138,11 +138,11 @@ class LVMAsRootTestCase(baseclass.DevicelibsTestCase):
## vgreduce
##
# pass
- self.assertEqual(lvm.vgreduce(self._vg_name, [_LOOP_DEV1]), None)
+ self.assertEqual(lvm.vgreduce(self._vg_name, _LOOP_DEV1), None)
# fail
- self.assertRaises(lvm.LVMError, lvm.vgreduce, "wrong-vg-name", [_LOOP_DEV1])
- self.assertRaises(lvm.LVMError, lvm.vgreduce, self._vg_name, ["/not/existing/device"])
+ self.assertRaises(lvm.LVMError, lvm.vgreduce, "wrong-vg-name", _LOOP_DEV1)
+ self.assertRaises(lvm.LVMError, lvm.vgreduce, self._vg_name, "/not/existing/device")
##
## vgactivate
diff --git a/tests/devicelibs_test/mdraid_test.py b/tests/devicelibs_test/mdraid_test.py
index 9c1584e..1ff710f 100755
--- a/tests/devicelibs_test/mdraid_test.py
+++ b/tests/devicelibs_test/mdraid_test.py
@@ -97,7 +97,7 @@ class MDRaidAsRootTestCase(baseclass.DevicelibsTestCase):
# TODO
# fail
- self.assertRaises(mdraid.MDRaidError, mdraid.mdadd, "/not/existing/device")
+ self.assertRaises(mdraid.MDRaidError, mdraid.mdadd, self._dev_name, "/not/existing/device")
##
## mdactivate
--
1.8.3.1
10 years