diff options
| author | J08nY | 2017-06-29 23:51:47 +0200 |
|---|---|---|
| committer | J08nY | 2017-08-30 13:18:10 +0200 |
| commit | f2cf2d0c96c0cf47e6dfa80137bb705ec4e8321b (patch) | |
| tree | 05afab72170dca745dad0d9976b28ce5878956d0 | |
| parent | c1060c9dfec4776ab6d714bea6e678a7d708396e (diff) | |
| download | mailman-f2cf2d0c96c0cf47e6dfa80137bb705ec4e8321b.tar.gz mailman-f2cf2d0c96c0cf47e6dfa80137bb705ec4e8321b.tar.zst mailman-f2cf2d0c96c0cf47e6dfa80137bb705ec4e8321b.zip | |
| -rw-r--r-- | src/mailman/app/subscriptions.py | 4 | ||||
| -rw-r--r-- | src/mailman/app/tests/test_workflow.py | 12 | ||||
| -rw-r--r-- | src/mailman/config/configure.zcml | 4 | ||||
| -rw-r--r-- | src/mailman/interfaces/workflows.py (renamed from src/mailman/interfaces/workflow.py) | 63 | ||||
| -rw-r--r-- | src/mailman/model/tests/test_workflow.py | 2 | ||||
| -rw-r--r-- | src/mailman/model/workflows.py (renamed from src/mailman/model/workflow.py) | 2 | ||||
| -rw-r--r-- | src/mailman/workflows/__init__.py | 0 | ||||
| -rw-r--r-- | src/mailman/workflows/base.py (renamed from src/mailman/app/workflow.py) | 56 |
8 files changed, 98 insertions, 45 deletions
diff --git a/src/mailman/app/subscriptions.py b/src/mailman/app/subscriptions.py index 0d8176ffb..dace9ccb6 100644 --- a/src/mailman/app/subscriptions.py +++ b/src/mailman/app/subscriptions.py @@ -24,7 +24,6 @@ from datetime import timedelta from email.utils import formataddr from enum import Enum from mailman.app.membership import delete_member -from mailman.app.workflow import Workflow from mailman.core.i18n import _ from mailman.database.transaction import flush from mailman.email.message import UserNotification @@ -43,9 +42,10 @@ from mailman.interfaces.subscriptions import ( from mailman.interfaces.template import ITemplateLoader from mailman.interfaces.user import IUser from mailman.interfaces.usermanager import IUserManager -from mailman.interfaces.workflow import IWorkflowStateManager +from mailman.interfaces.workflows import IWorkflowStateManager from mailman.utilities.datetime import now from mailman.utilities.string import expand, wrap +from mailman.workflows.base import Workflow from public import public from zope.component import getUtility from zope.event import notify diff --git a/src/mailman/app/tests/test_workflow.py b/src/mailman/app/tests/test_workflow.py index e82001540..f242a73ec 100644 --- a/src/mailman/app/tests/test_workflow.py +++ b/src/mailman/app/tests/test_workflow.py @@ -20,15 +20,15 @@ import json import unittest -from mailman.app.workflow import Workflow -from mailman.interfaces.workflow import IWorkflowStateManager +from mailman.interfaces.workflows import IWorkflowStateManager from mailman.testing.layers import ConfigLayer +from mailman.workflows.base import Workflow from zope.component import getUtility class MyWorkflow(Workflow): - INITIAL_STATE = 'first' - SAVE_ATTRIBUTES = ('ant', 'bee', 'cat') + initial_state = 'first' + save_attributes = ('ant', 'bee', 'cat') def __init__(self): super().__init__() @@ -51,7 +51,7 @@ class MyWorkflow(Workflow): class DependentWorkflow(MyWorkflow): - SAVE_ATTRIBUTES = ('ant', 'bee', 'cat', 'elf') + save_attributes = ('ant', 'bee', 'cat', 'elf') def __init__(self): super().__init__() @@ -136,7 +136,7 @@ class TestWorkflow(unittest.TestCase): def test_save_and_restore_dependant_attributes(self): # Attributes must be restored in the order they are declared in - # SAVE_ATTRIBUTES. + # save_attributes. workflow = iter(DependentWorkflow()) workflow.elf = 6 workflow.save() diff --git a/src/mailman/config/configure.zcml b/src/mailman/config/configure.zcml index 2fd0c8788..da4eb36b6 100644 --- a/src/mailman/config/configure.zcml +++ b/src/mailman/config/configure.zcml @@ -135,8 +135,8 @@ /> <utility - provides="mailman.interfaces.workflow.IWorkflowStateManager" - factory="mailman.model.workflow.WorkflowStateManager" + provides="mailman.interfaces.workflows.IWorkflowStateManager" + factory="mailman.model.workflows.WorkflowStateManager" /> </configure> diff --git a/src/mailman/interfaces/workflow.py b/src/mailman/interfaces/workflows.py index 5b3582b58..b110a481e 100644 --- a/src/mailman/interfaces/workflow.py +++ b/src/mailman/interfaces/workflows.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Interfaces describing the state of a workflow.""" +"""Interfaces describing the a workflow, it's state and it's state manager.""" from public import public from zope.interface import Attribute, Interface @@ -65,3 +65,64 @@ class IWorkflowStateManager(Interface): """ count = Attribute('The number of saved workflows in the database.') + + +@public +class IWorkflow(Interface): + """A workflow.""" + + name = Attribute('The name of the workflow, must be unique.') + description = Attribute('A brief description of the workflow.') + initial_state = Attribute('The state in which the workflow starts.') + save_attributes = Attribute('The sequence of attributes of the workflow, ' + 'which are saved.') + + def __iter__(): + """Return an iterator over the steps.""" + + def __next__(): + """Run the next step from the queue. + + :return: The result of the step run. + """ + + def push(step): + """Push a step to this workflows queue.""" + + def run_thru(stop_after): + """Run the state machine through and including the given step. + + :param stop_after: Name of method, sans prefix to run the + state machine through. In other words, the state machine runs + until the named method completes. + """ + + def run_until(stop_before): + """Run the state machine until (not including) the given step. + + :param stop_before: Name of method, sans prefix that the + state machine is run until the method is reached. Unlike + `run_thru()` the named method is not run. + """ + + def save(): + """Save the workflow in it's current state. + + Needs to have the `token` attribute set. + """ + + def restore(): + """Restore the workflow from the database. + + Needs to have the `token` attribute set. + """ + + +@public +class ISubscriptionWorkflow(IWorkflow): + """A workflow used for subscription.""" + + +@public +class IUnsubscriptionWorkflow(IWorkflow): + """A workflow used for unsubscription.""" diff --git a/src/mailman/model/tests/test_workflow.py b/src/mailman/model/tests/test_workflow.py index 9cc5446b7..41c00efc0 100644 --- a/src/mailman/model/tests/test_workflow.py +++ b/src/mailman/model/tests/test_workflow.py @@ -19,7 +19,7 @@ import unittest -from mailman.interfaces.workflow import IWorkflowStateManager +from mailman.interfaces.workflows import IWorkflowStateManager from mailman.testing.layers import ConfigLayer from zope.component import getUtility diff --git a/src/mailman/model/workflow.py b/src/mailman/model/workflows.py index 3ca3412a6..587d3375e 100644 --- a/src/mailman/model/workflow.py +++ b/src/mailman/model/workflows.py @@ -20,7 +20,7 @@ from mailman.database.model import Model from mailman.database.transaction import dbconnection from mailman.database.types import SAUnicode -from mailman.interfaces.workflow import IWorkflowState, IWorkflowStateManager +from mailman.interfaces.workflows import IWorkflowState, IWorkflowStateManager from public import public from sqlalchemy import Column from zope.interface import implementer diff --git a/src/mailman/workflows/__init__.py b/src/mailman/workflows/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/mailman/workflows/__init__.py diff --git a/src/mailman/app/workflow.py b/src/mailman/workflows/base.py index 92b78c184..5988dfa43 100644 --- a/src/mailman/app/workflow.py +++ b/src/mailman/workflows/base.py @@ -22,7 +22,8 @@ import json import logging from collections import deque -from mailman.interfaces.workflow import IWorkflowStateManager + +from mailman.interfaces.workflows import IWorkflowStateManager from public import public from zope.component import getUtility @@ -35,24 +36,33 @@ log = logging.getLogger('mailman.error') class Workflow: """Generic workflow.""" - SAVE_ATTRIBUTES = () - INITIAL_STATE = None + initial_state = None + save_attributes = () def __init__(self): self.token = None self._next = deque() - self.push(self.INITIAL_STATE) + self.push(self.initial_state) self.debug = False self._count = 0 - @property - def name(self): - return self.__class__.__name__ - def __iter__(self): + """See `IWorkflow`.""" return self + def __next__(self): + """See `IWorkflow`.""" + try: + name, step = self._pop() + return step() + except IndexError: + raise StopIteration + except: + log.exception('deque: {}'.format(COMMASPACE.join(self._next))) + raise + def push(self, step): + """See `IWorkflow`.""" self._next.append(step) def _pop(self): @@ -63,23 +73,8 @@ class Workflow: print('[{:02d}] -> {}'.format(self._count, name), file=sys.stderr) return name, step - def __next__(self): - try: - name, step = self._pop() - return step() - except IndexError: - raise StopIteration - except: - log.exception('deque: {}'.format(COMMASPACE.join(self._next))) - raise - def run_thru(self, stop_after): - """Run the state machine through and including the given step. - - :param stop_after: Name of method, sans prefix to run the - state machine through. In other words, the state machine runs - until the named method completes. - """ + """See `IWorkflow`.""" results = [] while True: try: @@ -93,12 +88,7 @@ class Workflow: return results def run_until(self, stop_before): - """Trun the state machine until (not including) the given step. - - :param stop_before: Name of method, sans prefix that the - state machine is run until the method is reached. Unlike - `run_thru()` the named method is not run. - """ + """See `IWorkflow`.""" results = [] while True: try: @@ -116,9 +106,10 @@ class Workflow: return results def save(self): + """See `IWorkflow`.""" assert self.token, 'Workflow token must be set' state_manager = getUtility(IWorkflowStateManager) - data = {attr: getattr(self, attr) for attr in self.SAVE_ATTRIBUTES} + data = {attr: getattr(self, attr) for attr in self.save_attributes} # Note: only the next step is saved, not the whole stack. This is not # an issue in practice, since there's never more than a single step in # the queue anyway. If we want to support more than a single step in @@ -135,6 +126,7 @@ class Workflow: state_manager.save(self.token, step, json.dumps(data)) def restore(self): + """See `IWorkflow`.""" state_manager = getUtility(IWorkflowStateManager) state = state_manager.restore(self.token) if state is None: @@ -144,7 +136,7 @@ class Workflow: if state.step: self._next.append(state.step) data = json.loads(state.data) - for attr in self.SAVE_ATTRIBUTES: + for attr in self.save_attributes: try: setattr(self, attr, data[attr]) except KeyError: |
