[PATCH 1/2] Make sure to return bool if bool is expected in spokes

Martin Kolman mkolman at redhat.com
Wed Oct 23 15:33:39 UTC 2013


The ready, completed and mandatory properties are expected
to return a boolean. If a non-bool is returned, it might
for example cause issues with GI, which won't take None as false
but will raise an exception instead.
Therefore the returns_bool decoratro has been added to iutil and
al relevant methods have been decorated.

Signed-off-by: Martin Kolman <mkolman at redhat.com>
---
 pyanaconda/iutil.py                        | 26 ++++++++++++++++++++++++++
 pyanaconda/ui/common.py                    |  4 ++++
 pyanaconda/ui/gui/spokes/__init__.py       |  2 ++
 pyanaconda/ui/gui/spokes/custom.py         |  1 +
 pyanaconda/ui/gui/spokes/datetime_spoke.py |  3 +++
 pyanaconda/ui/gui/spokes/keyboard.py       |  2 ++
 pyanaconda/ui/gui/spokes/langsupport.py    |  3 +++
 pyanaconda/ui/gui/spokes/network.py        |  3 +++
 pyanaconda/ui/gui/spokes/password.py       |  5 ++++-
 pyanaconda/ui/gui/spokes/software.py       |  4 ++++
 pyanaconda/ui/gui/spokes/source.py         |  4 ++++
 pyanaconda/ui/gui/spokes/storage.py        |  3 +++
 pyanaconda/ui/gui/spokes/user.py           |  3 +++
 pyanaconda/ui/gui/spokes/welcome.py        |  2 ++
 pyanaconda/ui/tui/__init__.py              |  1 +
 pyanaconda/ui/tui/simpleline/widgets.py    |  2 ++
 pyanaconda/ui/tui/spokes/__init__.py       |  2 ++
 pyanaconda/ui/tui/spokes/askvnc.py         |  2 ++
 pyanaconda/ui/tui/spokes/network.py        |  2 ++
 pyanaconda/ui/tui/spokes/password.py       |  3 +++
 pyanaconda/ui/tui/spokes/progress.py       |  2 ++
 pyanaconda/ui/tui/spokes/shell_spoke.py    |  2 ++
 pyanaconda/ui/tui/spokes/software.py       |  3 +++
 pyanaconda/ui/tui/spokes/source.py         |  3 +++
 pyanaconda/ui/tui/spokes/storage.py        |  4 ++++
 pyanaconda/ui/tui/spokes/time_spoke.py     |  5 ++++-
 pyanaconda/ui/tui/spokes/user.py           |  3 +++
 pyanaconda/ui/tui/spokes/warnings.py       |  2 ++
 28 files changed, 99 insertions(+), 2 deletions(-)

diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index ff7d32b..9cc96aa 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -758,3 +758,29 @@ def upcase_first_letter(text):
         return text.upper()
     else:
         return text[0].upper() + text[1:]
+
+def returns_bool(func):
+    """Make sure the decorated function always returns a boolean.
+    Please note that if the decorated function is also using the
+    @property decorator, the returns_bool decorator should be placed
+    below the property one.
+    Example:
+
+    @property
+    @returns_bool
+    def foo():
+        return None
+
+    This ensures the function is first decorated with returns_bool
+    and then with the property decorator.
+
+    :param func: a function to be decorated
+    :type func: a callable
+    :return: True of False
+    :rtype: boolean
+    """
+
+    def wrapper(*args, **kwargs):
+        return bool(func(*args, **kwargs))
+
+    return wrapper
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py
index dfff1f6..ab8b911 100644
--- a/pyanaconda/ui/common.py
+++ b/pyanaconda/ui/common.py
@@ -29,6 +29,7 @@ import types
 
 from pyanaconda.constants import ANACONDA_ENVIRON, FIRSTBOOT_ENVIRON
 from pyanaconda.errors import RemovedModuleError
+from pyanaconda import iutil
 from pykickstart.constants import FIRSTBOOT_RECONFIG
 
 import logging
@@ -277,6 +278,7 @@ class Spoke(UIObject):
         return ["%s.%s" % (self.__class__.__module__, self.__class__.__name__)]
 
     @property
+    @iutil.returns_bool
     def completed(self):
         """Has this spoke been visited and completed?  If not and the spoke is
            mandatory, a special warning icon will be shown on the Hub beside the
@@ -287,6 +289,7 @@ class Spoke(UIObject):
         return False
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         """Mark this spoke as mandatory. Installation will not be allowed
            to proceed until all mandatory spokes are complete.
@@ -381,6 +384,7 @@ class NormalSpoke(Spoke):
         return False
 
     @property
+    @iutil.returns_bool
     def ready(self):
         """Returns True if the Spoke has all the information required to be
            displayed.  Almost all spokes should keep the default value here.
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index d0e56c5..8ed7458 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -22,6 +22,7 @@
 from pyanaconda.ui import common
 from pyanaconda.ui.common import collect
 from pyanaconda.ui.gui import GUIObject
+from pyanaconda import iutil
 import os.path
 
 __all__ = ["StandaloneSpoke", "NormalSpoke", "PersonalizationSpoke",
@@ -38,6 +39,7 @@ class Spoke(GUIObject):
         raise NotImplementedError
 
     @property
+    @iutil.returns_bool
     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
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 20c1777..f7a8c1d 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -40,6 +40,7 @@ from pyanaconda.threads import AnacondaThread, threadMgr
 from pyanaconda.constants import THREAD_EXECUTE_STORAGE, THREAD_STORAGE, THREAD_CUSTOM_STORAGE_INIT
 from pyanaconda.iutil import lowerASCII
 from pyanaconda.bootloader import BootLoaderError
+from pyanaconda import iutil
 
 from blivet import devicefactory
 from blivet.formats import device_formats
diff --git a/pyanaconda/ui/gui/spokes/datetime_spoke.py b/pyanaconda/ui/gui/spokes/datetime_spoke.py
index 04c62b8..9912fd3 100644
--- a/pyanaconda/ui/gui/spokes/datetime_spoke.py
+++ b/pyanaconda/ui/gui/spokes/datetime_spoke.py
@@ -430,10 +430,12 @@ class DatetimeSpoke(FirstbootSpokeMixIn, NormalSpoke):
         self.data.timezone.setup(self.data)
 
     @property
+    @iutil.returns_bool
     def ready(self):
         return not threadMgr.get("AnaDateTimeThread")
 
     @property
+    @iutil.returns_bool
     def completed(self):
         if self._kickstarted and not self.data.timezone.seen:
             # taking values from kickstart, but not specified
@@ -442,6 +444,7 @@ class DatetimeSpoke(FirstbootSpokeMixIn, NormalSpoke):
             return is_valid_timezone(self.data.timezone.timezone)
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return True
 
diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py
index a30aac5..dcd7df5 100644
--- a/pyanaconda/ui/gui/spokes/keyboard.py
+++ b/pyanaconda/ui/gui/spokes/keyboard.py
@@ -28,6 +28,7 @@ from pyanaconda.ui.gui.categories.localization import LocalizationCategory
 from pyanaconda.ui.gui.utils import enlightbox, gtk_call_once, escape_markup
 from pyanaconda import keyboard
 from pyanaconda import flags
+from pyanaconda import iutil
 from pyanaconda.i18n import _, N_, CN_
 from pyanaconda.constants import DEFAULT_KEYBOARD
 
@@ -291,6 +292,7 @@ class KeyboardSpoke(NormalSpoke):
             self.data.keyboard.x_layouts.append(row[0])
 
     @property
+    @iutil.returns_bool
     def completed(self):
         if flags.flags.automatedInstall and not self.data.keyboard.seen:
             return False
diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py
index a5b5117..f633822 100644
--- a/pyanaconda/ui/gui/spokes/langsupport.py
+++ b/pyanaconda/ui/gui/spokes/langsupport.py
@@ -28,6 +28,7 @@ from pyanaconda.ui.gui.utils import escape_markup
 from pyanaconda.ui.gui.categories.localization import LocalizationCategory
 from pyanaconda.ui.gui.spokes.lib.lang_locale_handler import LangLocaleHandler
 from pyanaconda import localization
+from pyanaconda import iutil
 
 import re
 
@@ -101,10 +102,12 @@ class LangsupportSpoke(LangLocaleHandler, NormalSpoke):
                          for locale in self._installed_langsupports)
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return False
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return True
 
diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py
index c5bbd93..8af6585 100644
--- a/pyanaconda/ui/gui/spokes/network.py
+++ b/pyanaconda/ui/gui/spokes/network.py
@@ -44,6 +44,7 @@ from pyanaconda.ui.common import FirstbootSpokeMixIn
 
 from pyanaconda import network
 from pyanaconda import nm
+from pyanaconda import iutil
 
 from gi.repository import GLib, GObject, Pango, Gio, NetworkManager, NMClient
 import dbus
@@ -1221,6 +1222,7 @@ class NetworkSpoke(FirstbootSpokeMixIn, NormalSpoke):
                 or nm.nm_activated_devices())
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return self.data.method.method in ("url", "nfs")
 
@@ -1327,6 +1329,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke):
         network.set_hostname(self.data.network.hostname)
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return (not can_touch_runtime_system("require network connection")
                 or nm.nm_activated_devices())
diff --git a/pyanaconda/ui/gui/spokes/password.py b/pyanaconda/ui/gui/spokes/password.py
index fa5ef85..34b2ae1 100644
--- a/pyanaconda/ui/gui/spokes/password.py
+++ b/pyanaconda/ui/gui/spokes/password.py
@@ -21,6 +21,7 @@
 
 from pyanaconda.i18n import _, CN_
 from pyanaconda.users import cryptPassword, validatePassword
+from pyanaconda import iutil
 
 from pyanaconda.ui.gui import GUICheck
 from pyanaconda.ui.gui.spokes import NormalSpoke
@@ -116,6 +117,7 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
             return _("Root password is not set")
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return not any(user for user in self.data.user.userList
                             if "wheel" in user.groups)
@@ -137,8 +139,9 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
         self.confirm.set_placeholder_text("")
 
     @property
+    @iutil.returns_bool
     def completed(self):
-        return bool(self.data.rootpw.password or self.data.rootpw.lock)
+        return self.data.rootpw.password or self.data.rootpw.lock
 
     def _checkPasswordEmpty(self, editable, data):
         """Check whether a password has been specified at all."""
diff --git a/pyanaconda/ui/gui/spokes/software.py b/pyanaconda/ui/gui/spokes/software.py
index 89624c5..11a8f90 100644
--- a/pyanaconda/ui/gui/spokes/software.py
+++ b/pyanaconda/ui/gui/spokes/software.py
@@ -26,6 +26,7 @@ from pyanaconda.i18n import _, C_, CN_
 from pyanaconda.packaging import MetadataError
 from pyanaconda.threads import threadMgr, AnacondaThread
 from pyanaconda import constants
+from pyanaconda import iutil
 
 from pyanaconda.ui.communication import hubQ
 from pyanaconda.ui.gui.spokes import NormalSpoke
@@ -114,6 +115,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
             hubQ.send_ready("SourceSpoke", False)
 
     @property
+    @iutil.returns_bool
     def completed(self):
         processingDone = not threadMgr.get(constants.THREAD_CHECK_SOFTWARE) and \
                          not self._errorMsgs and self.txid_valid
@@ -139,10 +141,12 @@ class SoftwareSelectionSpoke(NormalSpoke):
         return True
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return True
 
     @property
+    @iutil.returns_bool
     def ready(self):
         # By default, the software selection spoke is not ready.  We have to
         # wait until the installation source spoke is completed.  This could be
diff --git a/pyanaconda/ui/gui/spokes/source.py b/pyanaconda/ui/gui/spokes/source.py
index 68bb931..40cd2b3 100644
--- a/pyanaconda/ui/gui/spokes/source.py
+++ b/pyanaconda/ui/gui/spokes/source.py
@@ -42,6 +42,7 @@ from pyanaconda.ui.gui.utils import gtk_call_once, really_hide, really_show
 from pyanaconda.threads import threadMgr, AnacondaThread
 from pyanaconda.packaging import PayloadError, MetadataError
 from pyanaconda import constants
+from pyanaconda import iutil
 
 from blivet.util import get_mount_paths
 
@@ -479,6 +480,7 @@ class SourceSpoke(NormalSpoke):
         return method_changed or update_payload_repos
 
     @property
+    @iutil.returns_bool
     def completed(self):
         if flags.automatedInstall and (not self.data.method.method or not self.payload.baseRepo):
             return False
@@ -486,10 +488,12 @@ class SourceSpoke(NormalSpoke):
             return not self._error and self.ready and (self.data.method.method or self.payload.baseRepo)
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return True
 
     @property
+    @iutil.returns_bool
     def ready(self):
         return (self._ready and
                 not threadMgr.get(constants.THREAD_PAYLOAD_MD) and
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index f3313a8..989570d 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -63,6 +63,7 @@ from pyanaconda.product import productName
 from pyanaconda.flags import flags
 from pyanaconda.i18n import _, C_, CN_, P_
 from pyanaconda import constants
+from pyanaconda import iutil
 from pyanaconda.bootloader import BootLoaderError
 
 from pykickstart.constants import CLEARPART_TYPE_NONE, AUTOPART_TYPE_LVM
@@ -457,6 +458,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             hubQ.send_ready(self.__class__.__name__, True)
 
     @property
+    @iutil.returns_bool
     def completed(self):
         retval = (threadMgr.get(constants.THREAD_EXECUTE_STORAGE) is None and
                   threadMgr.get(constants.THREAD_CHECK_STORAGE) is None and
@@ -469,6 +471,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             return retval
 
     @property
+    @iutil.returns_bool
     def ready(self):
         # By default, the storage spoke is not ready.  We have to wait until
         # storageInitialize is done.
diff --git a/pyanaconda/ui/gui/spokes/user.py b/pyanaconda/ui/gui/spokes/user.py
index 21d0388..a48fcff 100644
--- a/pyanaconda/ui/gui/spokes/user.py
+++ b/pyanaconda/ui/gui/spokes/user.py
@@ -34,6 +34,7 @@ from pyanaconda.constants import ANACONDA_ENVIRON, FIRSTBOOT_ENVIRON,\
         PASSWORD_WEAK, PASSWORD_WEAK_WITH_ERROR, PASSWORD_WEAK_CONFIRM,\
         PASSWORD_WEAK_CONFIRM_WITH_ERROR
 from pyanaconda.regexes import GECOS_VALID, USERNAME_VALID, GROUPNAME_VALID, GROUPLIST_FANCY_PARSE
+from pyanaconda import iutil
 
 __all__ = ["UserSpoke", "AdvancedUserDialog"]
 
@@ -337,6 +338,7 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke):
             return _("User %s will be created") % self.data.user.userList[0].name
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         # mandatory only if root account is disabled
         return (not self.data.rootpw.password) or self.data.rootpw.lock
@@ -392,6 +394,7 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke):
             self.data.user.userList.remove(self._user)
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return len(self.data.user.userList) > 0
 
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index a986b73..85d4285 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -36,6 +36,7 @@ from pyanaconda import flags
 from pyanaconda import geoloc
 from pyanaconda.i18n import _
 from pyanaconda.iutil import is_unsupported_hw
+from pyanaconda import iutil
 from pyanaconda.constants import DEFAULT_LANG, DEFAULT_KEYBOARD
 
 import logging
@@ -138,6 +139,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
                 self._xklwrapper.activate_default_layout()
 
     @property
+    @iutil.returns_bool
     def completed(self):
         if flags.flags.automatedInstall:
             return self.data.lang.lang and self.data.lang.lang != ""
diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py
index 1b12fb9..24ad926 100644
--- a/pyanaconda/ui/tui/__init__.py
+++ b/pyanaconda/ui/tui/__init__.py
@@ -20,6 +20,7 @@
 #
 
 from pyanaconda import ui
+from pyanaconda import iutil
 from pyanaconda.ui.communication import hubQ
 from pyanaconda.flags import flags
 from pyanaconda.threads import threadMgr
diff --git a/pyanaconda/ui/tui/simpleline/widgets.py b/pyanaconda/ui/tui/simpleline/widgets.py
index 438a35f..b7141e4 100644
--- a/pyanaconda/ui/tui/simpleline/widgets.py
+++ b/pyanaconda/ui/tui/simpleline/widgets.py
@@ -24,6 +24,7 @@
 __all__ = ["TextWidget", "ColumnWidget", "CheckboxWidget", "CenterWidget"]
 
 from pyanaconda.ui.tui.simpleline import base
+from pyanaconda import iutil
 
 class TextWidget(base.Widget):
     """Class to handle wrapped text output."""
@@ -186,6 +187,7 @@ class CheckboxWidget(base.Widget):
         return self._title
 
     @property
+    @iutil.returns_bool
     def completed(self):
         """Returns the state of the checkbox, checked is True."""
         return self._completed
diff --git a/pyanaconda/ui/tui/spokes/__init__.py b/pyanaconda/ui/tui/spokes/__init__.py
index 8ffe5d1..6bd3fbb 100644
--- a/pyanaconda/ui/tui/spokes/__init__.py
+++ b/pyanaconda/ui/tui/spokes/__init__.py
@@ -25,6 +25,7 @@ from pyanaconda.users import validatePassword, cryptPassword
 import re
 from collections import namedtuple
 from pyanaconda.iutil import setdeepattr, getdeepattr
+from pyanaconda import iutil
 from pyanaconda.i18n import _
 from pyanaconda.constants import PASSWORD_CONFIRM_ERROR_TUI
 
@@ -56,6 +57,7 @@ class TUISpoke(TUIObject, tui.Widget, Spoke):
         return _("testing status...")
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return True
 
diff --git a/pyanaconda/ui/tui/spokes/askvnc.py b/pyanaconda/ui/tui/spokes/askvnc.py
index d97763e..e1eee31 100644
--- a/pyanaconda/ui/tui/spokes/askvnc.py
+++ b/pyanaconda/ui/tui/spokes/askvnc.py
@@ -26,6 +26,7 @@ from pyanaconda.constants_text import INPUT_PROCESSED
 from pyanaconda.i18n import _
 from pyanaconda.ui.communication import hubQ
 from pyanaconda.ui.tui import exception_msg_handler
+from pyanaconda import iutil
 import getpass
 import sys
 
@@ -125,6 +126,7 @@ class VNCPassSpoke(NormalTUISpoke):
         return True
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return True # We're always complete
 
diff --git a/pyanaconda/ui/tui/spokes/network.py b/pyanaconda/ui/tui/spokes/network.py
index f4b99dc..f423df5 100644
--- a/pyanaconda/ui/tui/spokes/network.py
+++ b/pyanaconda/ui/tui/spokes/network.py
@@ -28,6 +28,7 @@ from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
 from pyanaconda.i18n import _
 from pyanaconda import network
 from pyanaconda import nm
+from pyanaconda import iutil
 
 from pyanaconda.regexes import IPV4_PATTERN_WITHOUT_ANCHORS
 from pyanaconda.constants_text import INPUT_PROCESSED
@@ -62,6 +63,7 @@ class NetworkSpoke(EditTUISpoke):
             self._update_network_data()
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return (not can_touch_runtime_system("require network connection")
                 or nm.nm_activated_devices())
diff --git a/pyanaconda/ui/tui/spokes/password.py b/pyanaconda/ui/tui/spokes/password.py
index 89bc7a7..b018011 100644
--- a/pyanaconda/ui/tui/spokes/password.py
+++ b/pyanaconda/ui/tui/spokes/password.py
@@ -24,6 +24,7 @@ from pyanaconda.ui.tui.spokes import EditTUIDialog, EditTUISpokeEntry
 from pyanaconda.ui.common import FirstbootSpokeMixIn
 from pyanaconda.ui.tui.simpleline import TextWidget
 from pyanaconda.i18n import _
+from pyanaconda import iutil
 
 class PasswordSpoke(FirstbootSpokeMixIn, EditTUIDialog):
     title = _("Set root password")
@@ -34,10 +35,12 @@ class PasswordSpoke(FirstbootSpokeMixIn, EditTUIDialog):
         self._password = None
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return bool(self.data.rootpw.password or self.data.rootpw.lock)
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return not any(user for user in self.data.user.userList
                        if "wheel" in user.groups)
diff --git a/pyanaconda/ui/tui/spokes/progress.py b/pyanaconda/ui/tui/spokes/progress.py
index 2638d8f..180577c 100644
--- a/pyanaconda/ui/tui/spokes/progress.py
+++ b/pyanaconda/ui/tui/spokes/progress.py
@@ -29,6 +29,7 @@ from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
 from pyanaconda.ui.tui.spokes import StandaloneTUISpoke
 from pyanaconda.ui.tui.hubs.summary import SummaryHub
 from pyanaconda.ui.tui.simpleline.base import ExitAllMainLoops
+from pyanaconda import iutil
 
 __all__ = ["ProgressSpoke"]
 
@@ -43,6 +44,7 @@ class ProgressSpoke(StandaloneTUISpoke):
         self._stepped = False
 
     @property
+    @iutil.returns_bool
     def completed(self):
         # this spoke is never completed, initially
         return False
diff --git a/pyanaconda/ui/tui/spokes/shell_spoke.py b/pyanaconda/ui/tui/spokes/shell_spoke.py
index 0f34f17..aa83b12 100644
--- a/pyanaconda/ui/tui/spokes/shell_spoke.py
+++ b/pyanaconda/ui/tui/spokes/shell_spoke.py
@@ -24,6 +24,7 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline.widgets import TextWidget
 from pyanaconda.i18n import _
 from pyanaconda.constants import ANACONDA_ENVIRON
+from pyanaconda import iutil
 from blivet import arch
 
 import subprocess
@@ -38,6 +39,7 @@ class ShellSpoke(NormalTUISpoke):
         return environment == ANACONDA_ENVIRON and arch.isS390()
 
     @property
+    @iutil.returns_bool
     def completed(self):
         # always completed
         return True
diff --git a/pyanaconda/ui/tui/spokes/software.py b/pyanaconda/ui/tui/spokes/software.py
index 9b37f55..b11acda 100644
--- a/pyanaconda/ui/tui/spokes/software.py
+++ b/pyanaconda/ui/tui/spokes/software.py
@@ -29,6 +29,7 @@ from pyanaconda.i18n import _
 from pyanaconda.constants import THREAD_PAYLOAD, THREAD_PAYLOAD_MD
 from pyanaconda.constants import THREAD_CHECK_SOFTWARE, THREAD_SOFTWARE_WATCHER
 from pyanaconda.constants_text import INPUT_PROCESSED
+from pyanaconda import iutil
 
 __all__ = ["SoftwareSpoke"]
 
@@ -101,6 +102,7 @@ class SoftwareSpoke(NormalTUISpoke):
         return self.payload.environmentDescription(self.environment)[0]
 
     @property
+    @iutil.returns_bool
     def completed(self):
         """ Make sure our threads are done running and vars are set. """
         processingDone = not threadMgr.get(THREAD_CHECK_SOFTWARE) and \
@@ -156,6 +158,7 @@ class SoftwareSpoke(NormalTUISpoke):
         return INPUT_PROCESSED
 
     @property
+    @iutil.returns_bool
     def ready(self):
         """ If we're ready to move on. """
         return (not threadMgr.get(THREAD_SOFTWARE_WATCHER) and
diff --git a/pyanaconda/ui/tui/spokes/source.py b/pyanaconda/ui/tui/spokes/source.py
index c505bd1..9a0c27a 100644
--- a/pyanaconda/ui/tui/spokes/source.py
+++ b/pyanaconda/ui/tui/spokes/source.py
@@ -31,6 +31,7 @@ from pyanaconda.image import opticalInstallMedia
 from pyanaconda.constants import THREAD_SOURCE_WATCHER, THREAD_SOFTWARE_WATCHER, THREAD_PAYLOAD
 from pyanaconda.constants import THREAD_PAYLOAD_MD, THREAD_STORAGE, THREAD_CHECK_SOFTWARE
 from pyanaconda.constants_text import INPUT_PROCESSED
+from pyanaconda import iutil
 
 import re
 
@@ -109,6 +110,7 @@ class SourceSpoke(EditTUISpoke):
         return summary
 
     @property
+    @iutil.returns_bool
     def completed(self):
         if flags.automatedInstall and (not self.data.method.method or not self.payload.baseRepo):
             return False
@@ -207,6 +209,7 @@ class SourceSpoke(EditTUISpoke):
                     self.errors.append(_("No installation source available"))
 
     @property
+    @iutil.returns_bool
     def ready(self):
         """ Check if the spoke is ready. """
         return (self._ready and
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index 2fbbd7b..8fdb2ca 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -36,6 +36,7 @@ from pyanaconda.constants import THREAD_STORAGE, THREAD_STORAGE_WATCHER
 from pyanaconda.constants_text import INPUT_PROCESSED
 from pyanaconda.i18n import _, P_
 from pyanaconda.bootloader import BootLoaderError
+from pyanaconda import iutil
 
 from pykickstart.constants import CLEARPART_TYPE_ALL, CLEARPART_TYPE_LINUX, CLEARPART_TYPE_NONE
 from pykickstart.errors import KickstartValueError
@@ -84,6 +85,7 @@ class StorageSpoke(NormalTUISpoke):
             self.data.autopart.autopart = True
 
     @property
+    @iutil.returns_bool
     def completed(self):
         retval = bool(self.storage.rootDevice and not self.errors)
 
@@ -93,12 +95,14 @@ class StorageSpoke(NormalTUISpoke):
             return retval
 
     @property
+    @iutil.returns_bool
     def ready(self):
         # By default, the storage spoke is not ready.  We have to wait until
         # storageInitialize is done.
         return self._ready and not threadMgr.get(THREAD_STORAGE_WATCHER)
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return True
 
diff --git a/pyanaconda/ui/tui/spokes/time_spoke.py b/pyanaconda/ui/tui/spokes/time_spoke.py
index fc9ff36..ee6d238 100644
--- a/pyanaconda/ui/tui/spokes/time_spoke.py
+++ b/pyanaconda/ui/tui/spokes/time_spoke.py
@@ -23,6 +23,7 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
 from pyanaconda.ui.common import FirstbootSpokeMixIn
 from pyanaconda import timezone
+from pyanaconda import iutil
 from pyanaconda.i18n import _
 from pyanaconda.constants_text import INPUT_PROCESSED
 
@@ -44,10 +45,12 @@ class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
         self._selection = ""
 
     @property
+    @iutil.returns_bool
     def completed(self):
-        return bool(self.data.timezone.timezone)
+        return self.data.timezone.timezone
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         return True
 
diff --git a/pyanaconda/ui/tui/spokes/user.py b/pyanaconda/ui/tui/spokes/user.py
index c2ff4dd..5fd48d9 100644
--- a/pyanaconda/ui/tui/spokes/user.py
+++ b/pyanaconda/ui/tui/spokes/user.py
@@ -27,6 +27,7 @@ from pyanaconda.i18n import _
 from pykickstart.constants import FIRSTBOOT_RECONFIG
 from pyanaconda.constants import ANACONDA_ENVIRON, FIRSTBOOT_ENVIRON
 from pyanaconda.regexes import GECOS_VALID, USERNAME_VALID, GROUPLIST_SIMPLE_VALID
+from pyanaconda import iutil
 
 __all__ = ["UserSpoke"]
 
@@ -84,6 +85,7 @@ class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
         return EditTUISpoke.refresh(self, args)
 
     @property
+    @iutil.returns_bool
     def completed(self):
         """ Verify a user is created; verify pw is set if option checked. """
         if len(self.data.user.userList) > 0:
@@ -95,6 +97,7 @@ class UserSpoke(FirstbootSpokeMixIn, EditTUISpoke):
             return False
 
     @property
+    @iutil.returns_bool
     def mandatory(self):
         """ Only mandatory if root account is disabled. """
         return not bool(self.data.rootpw.password) or self.data.rootpw.lock
diff --git a/pyanaconda/ui/tui/spokes/warnings.py b/pyanaconda/ui/tui/spokes/warnings.py
index c844183..4500126 100644
--- a/pyanaconda/ui/tui/spokes/warnings.py
+++ b/pyanaconda/ui/tui/spokes/warnings.py
@@ -25,6 +25,7 @@ from pyanaconda.ui.tui.hubs.summary import SummaryHub
 from pyanaconda.i18n import _
 
 from pyanaconda.iutil import is_unsupported_hw
+from pyanaconda import iutil
 from pyanaconda.product import productName
 
 import logging
@@ -51,6 +52,7 @@ class WarningsSpoke(StandaloneTUISpoke):
                             not self.data.unsupportedhardware.unsupported_hardware
 
     @property
+    @iutil.returns_bool
     def completed(self):
         return not self._unsupported
 
-- 
1.8.3.1



More information about the anaconda-patches mailing list