[PATCH 1/2] Unravel the Hub and Spoke classes.

David Shea dshea at redhat.com
Mon Jul 7 20:45:41 UTC 2014


Make common.Hub and common.Spoke abstract base classes with an abstract
'data' property to remove the need for everything to have two paths to
UIObject. Remove gui.Spoke entirely because it didn't do anything. Make
common.StandaloneSpoke inherit from common.Spoke instead of
common.NormalSpoke because that didn't make any sense.

Remove the abstract class checks in __init__ from common since they the
ABCMeta metaclass takes care of it. Remove the abstract class check from
gui.NormalSpoke.__init__ for consistency with
gui.StandaloneSpoke.__init__
---
 pyanaconda/ui/common.py              | 53 ++++++++++++++++++++----------------
 pyanaconda/ui/gui/hubs/__init__.py   |  2 +-
 pyanaconda/ui/gui/spokes/__init__.py | 52 +++++------------------------------
 pyanaconda/ui/tui/hubs/__init__.py   |  2 +-
 pyanaconda/ui/tui/spokes/__init__.py |  2 +-
 5 files changed, 40 insertions(+), 71 deletions(-)

diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py
index f0ac602..5c1caf2 100644
--- a/pyanaconda/ui/common.py
+++ b/pyanaconda/ui/common.py
@@ -26,6 +26,8 @@ import inspect
 import sys
 import types
 
+from abc import ABCMeta, abstractproperty
+
 from pyanaconda.constants import ANACONDA_ENVIRON, FIRSTBOOT_ENVIRON
 from pyanaconda.errors import RemovedModuleError
 from pykickstart.constants import FIRSTBOOT_RECONFIG, DISPLAY_MODE_TEXT
@@ -164,7 +166,7 @@ class FirstbootOnlySpokeMixIn(object):
         else:
             return False
 
-class Spoke(UIObject):
+class Spoke(object):
     """A Spoke is a single configuration screen.  There are several different
        places where a Spoke can be displayed, each of which will have its own
        unique class.  A Spoke is typically used when an element in the Hub is
@@ -191,11 +193,14 @@ class Spoke(UIObject):
                      corresponding to this Spoke instance.  If no title is
                      given, the default from SpokeSelector will be used.
     """
+
+    __metaclass__ = ABCMeta
+
     category = None
     icon = None
     title = None
 
-    def __init__(self, data, storage, payload, instclass):
+    def __init__(self, storage, payload, instclass):
         """Create a new Spoke instance.
 
            The arguments this base class accepts defines the API that spokes
@@ -205,7 +210,9 @@ class Spoke(UIObject):
 
            data         -- An instance of a pykickstart Handler object.  The
                            Spoke uses this to populate its UI with defaults
-                           and to pass results back after it has run.
+                           and to pass results back after it has run. The data
+                           property must be implemented by classes inherting
+                           from Spoke.
            storage      -- An instance of storage.Storage.  This is useful for
                            determining what storage devices are present and how
                            they are configured.
@@ -217,10 +224,6 @@ class Spoke(UIObject):
                            installation information like default package
                            selections and default partitioning.
         """
-        if self.__class__ is Spoke:
-            raise TypeError("Spoke is an abstract class")
-
-        UIObject.__init__(self, data)
         self._storage = storage
         self.payload = payload
         self.instclass = instclass
@@ -228,6 +231,10 @@ class Spoke(UIObject):
 
         self.visitedSinceApplied = True
 
+    @abstractproperty
+    def data(self):
+        pass
+
     @property
     def storage(self):
         return self._storage
@@ -363,12 +370,9 @@ class NormalSpoke(Spoke):
        installing, how to get back to the Hub) at the top of the screen.
     """
 
-    def __init__(self, data, storage, payload, instclass):
+    def __init__(self, storage, payload, instclass):
         """Create a NormalSpoke instance."""
-        if self.__class__ is NormalSpoke:
-            raise TypeError("NormalSpoke is an abstract class")
-
-        Spoke.__init__(self, data, storage, payload, instclass)
+        Spoke.__init__(self, storage, payload, instclass)
         self.selector = None
 
     @property
@@ -403,7 +407,7 @@ class NormalSpoke(Spoke):
 
 # Inherit abstract methods from NormalSpoke
 # pylint: disable=abstract-method
-class StandaloneSpoke(NormalSpoke):
+class StandaloneSpoke(Spoke):
     """A StandaloneSpoke is a Spoke subclass that is displayed apart from any
        Hub.  It is suitable to be used as a Welcome screen.
 
@@ -429,15 +433,12 @@ class StandaloneSpoke(NormalSpoke):
     preForHub = None
     postForHub = None
 
-    def __init__(self, data, storage, payload, instclass):
+    def __init__(self, storage, payload, instclass):
         """Create a StandaloneSpoke instance."""
-        if self.__class__ is StandaloneSpoke:
-            raise TypeError("StandaloneSpoke is an abstract class")
-
         if self.preForHub and self.postForHub:
             raise AttributeError("StandaloneSpoke instance %s may not have both preForHub and postForHub set" % self)
 
-        NormalSpoke.__init__(self, data, storage, payload, instclass)
+        Spoke.__init__(self, storage, payload, instclass)
 
     # Standalone spokes are not part of a hub, and thus have no status.
     # Provide a concrete implementation of status here so that subclasses
@@ -446,7 +447,7 @@ class StandaloneSpoke(NormalSpoke):
     def status(self):
         return None
 
-class Hub(UIObject):
+class Hub(object):
     """A Hub is an overview UI screen.  A Hub consists of one or more grids of
        configuration options that the user may choose from.  Each grid is
        provided by a SpokeCategory, and each option is provided by a Spoke.
@@ -468,7 +469,9 @@ class Hub(UIObject):
        additional standalone screens either before or after them.
     """
 
-    def __init__(self, data, storage, payload, instclass):
+    __metaclass__ = ABCMeta
+
+    def __init__(self, storage, payload, instclass):
         """Create a new Hub instance.
 
            The arguments this base class accepts defines the API that Hubs
@@ -478,7 +481,9 @@ class Hub(UIObject):
 
            data         -- An instance of a pykickstart Handler object.  The
                            Hub uses this to populate its UI with defaults
-                           and to pass results back after it has run.
+                           and to pass results back after it has run. The data
+                           property must be implemented by classes inheriting
+                           from Hub.
            storage      -- An instance of storage.Storage.  This is useful for
                            determining what storage devices are present and how
                            they are configured.
@@ -490,8 +495,6 @@ class Hub(UIObject):
                            installation information like default package
                            selections and default partitioning.
         """
-        UIObject.__init__(self, data)
-
         self._storage = storage
         self.payload = payload
         self.instclass = instclass
@@ -502,6 +505,10 @@ class Hub(UIObject):
         # spokes for which environments this hub should collect?
         self._environs = [ANACONDA_ENVIRON]
 
+    @abstractproperty
+    def data(self):
+        pass
+
     @property
     def storage(self):
         return self._storage
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index b89e596..e2cb44a 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -81,7 +81,7 @@ class Hub(GUIObject, common.Hub):
                            selections and default partitioning.
         """
         GUIObject.__init__(self, data)
-        common.Hub.__init__(self, data, storage, payload, instclass)
+        common.Hub.__init__(self, storage, payload, instclass)
 
         # enable the autoContinue feature if we are in kickstart
         # mode, but if the user interacts with the hub, it will be
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index 86706a4..53989c3 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -25,47 +25,12 @@ import os.path
 
 __all__ = ["StandaloneSpoke", "NormalSpoke"]
 
-class Spoke(GUIObject):
-    def __init__(self, data):
-        GUIObject.__init__(self, data)
-
-    def apply(self):
-        """Apply the selections made on this Spoke to the object's preset
-           data object.  This method must be provided by every subclass.
-        """
-        raise NotImplementedError
-
-    @property
-    def completed(self):
-        """Has this spoke been visited and completed?  If not, a special warning
-           icon will be shown on the Hub beside the spoke, and a highlighted
-           message will be shown at the bottom of the Hub.  Installation will not
-           be allowed to proceed until all spokes are complete.
-
-           WARNING: This can be called before the spoke is finished initializing
-           if the spoke starts a thread. It should make sure it doesn't access
-           things until they are completely setup.
-        """
-        return False
-
-    def execute(self):
-        """Cause the data object to take effect on the target system.  This will
-           usually be as simple as calling one or more of the execute methods on
-           the data object.  This method does not need to be provided by all
-           subclasses.
-
-           This method will be called in two different places:  (1) Immediately
-           after initialize on kickstart installs.  (2) Immediately after apply
-           in all cases.
-        """
-        pass
-
-# Inherit abstract methods from Spoke and common.StandaloneSpoke
+# Inherit abstract methods from common.StandaloneSpoke
 # pylint: disable=abstract-method
-class StandaloneSpoke(Spoke, common.StandaloneSpoke):
+class StandaloneSpoke(GUIObject, common.StandaloneSpoke):
     def __init__(self, data, storage, payload, instclass):
-        Spoke.__init__(self, data)
-        common.StandaloneSpoke.__init__(self, data, storage, payload, instclass)
+        GUIObject.__init__(self, data)
+        common.StandaloneSpoke.__init__(self, storage, payload, instclass)
 
         # Add a continue-clicked handler to save the data before leaving the window
         self.window.connect("continue-clicked", self._on_continue_clicked)
@@ -75,13 +40,10 @@ class StandaloneSpoke(Spoke, common.StandaloneSpoke):
 
 # Inherit abstract methods from common.NormalSpoke
 # pylint: disable=abstract-method
-class NormalSpoke(Spoke, common.NormalSpoke):
+class NormalSpoke(GUIObject, common.NormalSpoke):
     def __init__(self, data, storage, payload, instclass):
-        if self.__class__ is NormalSpoke:
-            raise TypeError("NormalSpoke is an abstract class")
-
-        Spoke.__init__(self, data)
-        common.NormalSpoke.__init__(self, data, storage, payload, instclass)
+        GUIObject.__init__(self, data)
+        common.NormalSpoke.__init__(self, storage, payload, instclass)
 
     def on_back_clicked(self, window):
         # Notify the hub that we're finished.
diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py
index 24489f6..e89bf71 100644
--- a/pyanaconda/ui/tui/hubs/__init__.py
+++ b/pyanaconda/ui/tui/hubs/__init__.py
@@ -42,7 +42,7 @@ class TUIHub(TUIObject, common.Hub):
 
     def __init__(self, app, data, storage, payload, instclass):
         TUIObject.__init__(self, app, data)
-        common.Hub.__init__(self, data, storage, payload, instclass)
+        common.Hub.__init__(self, storage, payload, instclass)
 
         self._spokes = {}     # holds spokes referenced by their class name
         self._keys = {}       # holds spokes referenced by their user input key
diff --git a/pyanaconda/ui/tui/spokes/__init__.py b/pyanaconda/ui/tui/spokes/__init__.py
index 4c4d4d8..9b5a98e 100644
--- a/pyanaconda/ui/tui/spokes/__init__.py
+++ b/pyanaconda/ui/tui/spokes/__init__.py
@@ -53,7 +53,7 @@ class TUISpoke(TUIObject, tui.Widget, Spoke):
 
         TUIObject.__init__(self, app, data)
         tui.Widget.__init__(self)
-        Spoke.__init__(self, data, storage, payload, instclass)
+        Spoke.__init__(self, storage, payload, instclass)
 
     @property
     def status(self):
-- 
2.0.0



More information about the anaconda-patches mailing list