[master 03/15] Throw away the dispatcher 'skipList' and give Step a state.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In particular, 'scheduled' will run if not explicitly skipped, 'skipped'
means 'is never going to be run' and 'requested' means 'will run and can
not be skipped'.
---
 pyanaconda/dispatch.py                 |  103 +++++++++++++++++-------
 pyanaconda/errors.py                   |    3 +
 pyanaconda/installclass.py             |    2 +-
 pyanaconda/upgrade.py                  |    2 +-
 tests/pyanaconda_test/dispatch_test.py |  133 ++++++++++++++++++++++++++++++++
 5 files changed, 211 insertions(+), 32 deletions(-)
 create mode 100644 tests/pyanaconda_test/dispatch_test.py

diff --git a/pyanaconda/dispatch.py b/pyanaconda/dispatch.py
index ee15ce2..0f4499a 100644
--- a/pyanaconda/dispatch.py
+++ b/pyanaconda/dispatch.py
@@ -24,6 +24,7 @@ import string
 from types import *
 
 import indexed_dict
+import errors
 from constants import *
 from packages import writeKSConfiguration, turnOnFilesystems
 from packages import doPostAction
@@ -55,11 +56,21 @@ from packages import doReIPL
 import logging
 log = logging.getLogger("anaconda")
 
-SCHED_SKIPPED = -2
-SCHED_SKIPPED_PERMANENTLY = -1
-SCHED_SCHEDULED = 1
-
 class Step(object):
+    SCHED_UNSCHEDULED = 0
+    SCHED_SCHEDULED = 1 # will execute if not explicitly skipped
+    SCHED_SKIPPED = 2   # is never going to execute
+    SCHED_REQUESTED = 3 # will execute and can not be skipped
+    SCHED_DONE = 4      # done is a final state
+
+    sched_state_machine = [
+        # unscheduled       # scheduled      # skipped      # requested      # done
+        [SCHED_UNSCHEDULED, SCHED_SCHEDULED, SCHED_SKIPPED, SCHED_REQUESTED, SCHED_DONE], # unscheduled
+        [None             , SCHED_SCHEDULED, SCHED_SKIPPED, SCHED_REQUESTED, SCHED_DONE], # scheduled
+        [None             , None           , SCHED_SKIPPED, None           , None],       # skipped
+        [None             , None           , None         , SCHED_REQUESTED, SCHED_DONE], # requested
+        [None             , None           , None         , None           , SCHED_DONE]] # done
+
     def __init__(self, name, target = None):
         """ Dispatcher step object.
 
@@ -70,22 +81,48 @@ class Step(object):
         """
         self.name = name
         self.target = target # None for dynamic target (e.g. gui view)
-        self._sched = SCHED_SKIPPED
+        self._sched = self.SCHED_UNSCHEDULED
 
-    def reschedule(self, new_sched):
-        if self._sched == SCHED_SKIPPED_PERMANENTLY:
-            # do not allow "upgrading" of permanently skipped steps
-            return
+    def _reschedule(self, to_sched):
+        new_sched = self.sched_state_machine[self._sched][to_sched]
+        if new_sched is None:
+            raise errors.DispatchError(
+                "Can not reschedule step '%s' from '%s' to '%s'" %
+                (self.name,
+                 self.namesched(self._sched),
+                 self.namesched(to_sched)))
         self._sched = new_sched
 
     @property
     def direct(self):
         return self.target is not None
 
+    def done(self):
+        self._reschedule(self.SCHED_DONE)
+
+    def request(self):
+        self._reschedule(self.SCHED_REQUESTED)
+
+    def namesched(self, sched):
+        return {
+            self.SCHED_UNSCHEDULED : "unscheduled",
+            self.SCHED_SCHEDULED   : "scheduled",
+            self.SCHED_SKIPPED     : "skipped",
+            self.SCHED_REQUESTED   : "requested",
+            self.SCHED_DONE        : "done"
+            }[sched]
+
     @property
     def sched(self):
         return self._sched
 
+    def schedule(self):
+        self._reschedule(self.SCHED_SCHEDULED)
+
+    def skip(self):
+        self._reschedule(self.SCHED_SKIPPED)
+
+
 class Dispatcher(object):
 
     def __init__(self, anaconda):
@@ -158,6 +195,9 @@ class Dispatcher(object):
     def _step_index(self):
         return self.steps.index(self.step)
 
+    def done_steps(self, *steps):
+        map(lambda s: self.steps[s].done(), steps)
+
     def go_back(self):
         """
         The caller should make sure canGoBack() is True before calling this
@@ -170,42 +210,43 @@ class Dispatcher(object):
         self._setDir(DISPATCH_FORWARD)
         self.dispatch()
 
-    def canGoBack(self):
+    def can_go_back(self):
         # Begin with the step before this one. If all steps are skipped,
         # we can not go backwards from this one.
         i = self._step_index() - 1
         while i >= 0:
             sname = self.steps[i].name
-            if not (self.stepIsDirect(sname) or self.stepInSkipList(sname)):
+            if not self.step_is_direct(sname) and self.step_enabled(sname):
                 return True
             i -= 1
         return False
 
+    def request_step(self, *steps):
+        map(lambda s: self.steps[s].request(), steps)
+
     def run(self):
         self.anaconda.intf.run(self.anaconda)
         log.info("dispatch: finished.")
 
-    def setStepList(self, *new_steps):
-        for (name, step) in self.steps.items():
-            if name in new_steps:
-                step.reschedule(SCHED_SCHEDULED)
-            else:
-                step.reschedule(SCHED_SKIPPED)
+    def schedule_steps(self, *steps):
+        map(lambda s: self.steps[s].schedule(), steps)
 
-    def stepInSkipList(self, step):
-        return self.steps[step].sched in [SCHED_SKIPPED_PERMANENTLY,
-                                          SCHED_SKIPPED]
+    def step_disabled(self, step):
+        """ True if step is not yet scheduled to be run or will never be run
+            (i.e. is skipped).
+        """
+        return not self.step_enabled(step)
 
-    def skipStep(self, step, skip = 1, permanent = 0):
-        new_sched = SCHED_SCHEDULED
-        if skip:
-            if permanent:
-                new_sched = SCHED_SKIPPED_PERMANENTLY
-            else:
-                new_sched = SCHED_SKIPPED
-        self.steps[step].reschedule(new_sched)
+    def step_enabled(self, step):
+        """ True if step is scheduled to be run or have been run already. """
+        return self.steps[step].sched in [Step.SCHED_SCHEDULED,
+                                          Step.SCHED_REQUESTED,
+                                          Step.SCHED_DONE]
+
+    def skipStep(self, *steps):
+        map(lambda s: self.steps[s].skip(), steps)
 
-    def stepIsDirect(self, step):
+    def step_is_direct(self, step):
         return self.steps[step].direct
 
     def dispatch(self):
@@ -216,6 +257,7 @@ class Dispatcher(object):
         else:
             log.info("dispatch: leaving (%d) step %s" %
                      (self.dir, self.step))
+            self.done_steps(self.step)
             self._advance_step()
 
         while True:
@@ -223,7 +265,7 @@ class Dispatcher(object):
                 # installation has proceeded beyond the last step: finished
                 self.anaconda.intf.shutdown()
                 return
-            if self.stepInSkipList(self.step):
+            if self.step_disabled(self.step):
                 self._advance_step()
                 continue
             log.info("dispatch: moving (%d) to step %s" %
@@ -248,6 +290,7 @@ class Dispatcher(object):
                     self.dir = rc
             log.info("dispatch: leaving (%d) step %s" %
                      (self.dir, self.step))
+            self.done_steps(self.step)
             self._advance_step()
 
     def _getDir(self):
diff --git a/pyanaconda/errors.py b/pyanaconda/errors.py
index 9733733..0bb6237 100644
--- a/pyanaconda/errors.py
+++ b/pyanaconda/errors.py
@@ -157,3 +157,6 @@ class NoSuchGroup(Exception):
 
     def __str__ (self):
         return self.value
+
+class DispatchError(RuntimeError):
+    pass
diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py
index 8092e94..7815051 100644
--- a/pyanaconda/installclass.py
+++ b/pyanaconda/installclass.py
@@ -83,7 +83,7 @@ class BaseInstallClass(object):
 
     def setSteps(self, anaconda):
         dispatch = anaconda.dispatch
-	dispatch.setStepList(
+	dispatch.schedule_steps(
 		 "sshd",
 		 "language",
 		 "keyboard",
diff --git a/pyanaconda/upgrade.py b/pyanaconda/upgrade.py
index 6a16169..2b96eb5 100644
--- a/pyanaconda/upgrade.py
+++ b/pyanaconda/upgrade.py
@@ -288,7 +288,7 @@ def upgradeMountFilesystems(anaconda):
 
 def setSteps(anaconda):
     dispatch = anaconda.dispatch
-    dispatch.setStepList(
+    dispatch.schedule_steps(
                 "language",
                 "keyboard",
                 "filtertype",
diff --git a/tests/pyanaconda_test/dispatch_test.py b/tests/pyanaconda_test/dispatch_test.py
new file mode 100644
index 0000000..485ddde
--- /dev/null
+++ b/tests/pyanaconda_test/dispatch_test.py
@@ -0,0 +1,133 @@
+import mock
+
+class StepTest(mock.TestCase):
+    def setUp(self):
+        self.setupModules(
+            ['logging', 'pyanaconda.anaconda_log', 'block'])
+
+        import pyanaconda
+        pyanaconda.anaconda_log = mock.Mock()
+
+    def tearDown(self):
+        self.tearDownModules()
+
+    def done_test(self):
+        from pyanaconda.dispatch import Step
+        from pyanaconda.errors import DispatchError
+        s = Step("a_step")
+        s.schedule()
+        s.request()
+        s.done()
+        self.assertEquals(s.sched, Step.SCHED_DONE)
+        self.assertRaises(DispatchError, s.skip)
+
+    def instantiation_test(self):
+        from pyanaconda.dispatch import Step
+        s = Step("yeah")
+        self.assertIsInstance(s, Step)
+        # name
+        self.assertEqual(s.name, "yeah")
+        # default scheduling
+        self.assertEqual(s.sched, Step.SCHED_UNSCHEDULED)
+
+    def namesched_test(self):
+        from pyanaconda.dispatch import Step
+        s = Step("a_step")
+        self.assertEqual(s.namesched(Step.SCHED_UNSCHEDULED), "unscheduled")
+
+    def reschedule_test(self):
+        from pyanaconda.dispatch import Step
+        from pyanaconda.errors import DispatchError
+        s = Step("a_step")
+        s._reschedule(Step.SCHED_UNSCHEDULED)
+        self.assertEqual(s.sched, Step.SCHED_UNSCHEDULED)
+        s._reschedule(Step.SCHED_SCHEDULED)
+        self.assertEqual(s.sched, Step.SCHED_SCHEDULED)
+        s._reschedule(Step.SCHED_REQUESTED)
+        self.assertEqual(s.sched, Step.SCHED_REQUESTED)
+        self.assertRaises(DispatchError, s._reschedule, Step.SCHED_SKIPPED)
+        self.assertRaises(DispatchError, s._reschedule, Step.SCHED_UNSCHEDULED)
+        s._reschedule(Step.SCHED_DONE)
+        self.assertEqual(s.sched, Step.SCHED_DONE)
+
+        s = Step("another_step")
+        s._reschedule(Step.SCHED_SKIPPED)
+        self.assertEqual(s.sched, Step.SCHED_SKIPPED)
+        self.assertRaises(DispatchError, s._reschedule, Step.SCHED_SCHEDULED)
+        self.assertRaises(DispatchError, s._reschedule, Step.SCHED_REQUESTED)
+        self.assertRaises(DispatchError, s._reschedule, Step.SCHED_DONE)
+
+    def request_test(self):
+        from pyanaconda.dispatch import Step
+        from pyanaconda.errors import DispatchError
+        s = Step("a_step")
+        s.request()
+        self.assertRaises(DispatchError, s.skip)
+
+    def schedule_test(self):
+        from pyanaconda.dispatch import Step
+        s = Step("a_step")
+        s.schedule()
+        self.assertEquals(s.sched, Step.SCHED_SCHEDULED)
+
+    def skip_test(self):
+        from pyanaconda.dispatch import Step
+        from pyanaconda.errors import DispatchError
+        s = Step("a_step")
+        s.skip()
+        self.assertEquals(s.sched, Step.SCHED_SKIPPED)
+        self.assertRaises(DispatchError, s.done)
+        self.assertRaises(DispatchError, s.request)
+
+class DispatchTest(mock.TestCase):
+    def setUp(self):
+        self.setupModules(
+            ['logging', 'pyanaconda.anaconda_log', 'block'])
+
+        import pyanaconda
+        pyanaconda.anaconda_log = mock.Mock()
+
+    def tearDown(self):
+        self.tearDownModules()
+
+    def _getDispatcher(self):
+        from pyanaconda.dispatch import Dispatcher
+        self.anaconda_obj = mock.Mock()
+        return Dispatcher(self.anaconda_obj)
+
+    def done_test(self):
+        from pyanaconda.dispatch import Dispatcher
+        from pyanaconda.dispatch import Step
+        from pyanaconda.errors import DispatchError
+
+        d = self._getDispatcher()
+        self.assertFalse(d.step_enabled("betanag"))
+        d.schedule_steps("betanag")
+        d.done_steps("betanag")
+        self.assertTrue(d.step_enabled("betanag"))
+        self.assertTrue(d.steps["betanag"], Step.SCHED_DONE)
+        self.assertRaises(DispatchError, d.skipStep, "betanag")
+
+    def instantiation_test(self):
+        from pyanaconda.dispatch import Dispatcher
+        d = self._getDispatcher()
+        self.assertIsInstance(d, Dispatcher)
+
+    def schedule_test(self):
+        from pyanaconda.dispatch import Step
+        d = self._getDispatcher()
+        d.schedule_steps("betanag", "complete")
+        self.assertEqual(d.steps["betanag"].sched, Step.SCHED_SCHEDULED)
+        self.assertEqual(d.steps["complete"].sched, Step.SCHED_SCHEDULED)
+        self.assertEqual(d.steps["bootloader"].sched, Step.SCHED_UNSCHEDULED)
+        # impossible to reach nonexistent steps:
+        self.assertRaises(KeyError, d.steps.__getitem__, "nonexistent")
+
+    def skip_test(self):
+        d = self._getDispatcher()
+        d.schedule_steps("betanag", "filtertype", "complete")
+        self.assertTrue(d.step_enabled("betanag"))
+        d.skipStep("betanag", "complete")
+        self.assertFalse(d.step_enabled("betanag"))
+        self.assertFalse(d.step_enabled("complete"))
+        self.assertTrue(d.step_enabled("filtertype"))
-- 
1.7.3.3

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list


[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux