From d60ab0788a3115e608480ced9f243241e38e2e97 Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Mon, 5 Feb 2018 17:11:24 +0100 Subject: [PATCH] Python 3 support --- .travis.yml | 2 + CHANGES.rst | 2 +- setup.py | 3 ++ src/shoobx/wfmc/README.txt | 70 +++++++++++++------------ src/shoobx/wfmc/adapter/integration.txt | 24 +++++---- src/shoobx/wfmc/attributeintegration.py | 3 +- src/shoobx/wfmc/deadline.txt | 14 ++--- src/shoobx/wfmc/process.py | 56 +++++++++----------- src/shoobx/wfmc/subflow.txt | 20 +++---- src/shoobx/wfmc/tests.py | 43 ++++++++++----- src/shoobx/wfmc/xpdl-2.1.txt | 62 +++++++++++----------- src/shoobx/wfmc/xpdl.py | 19 ++++--- src/shoobx/wfmc/xpdl.txt | 64 +++++++++++----------- tox.ini | 2 +- 14 files changed, 209 insertions(+), 175 deletions(-) diff --git a/.travis.yml b/.travis.yml index 60dbc4f..d9c0878 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: python sudo: false python: - 2.7 + - 3.5 + - 3.6 install: - pip install tox-travis coveralls script: diff --git a/CHANGES.rst b/CHANGES.rst index 094e912..7ee9116 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ CHANGES 4.0.5 (unreleased) ------------------ -- Nothing changed yet. +- Python 3 support. 4.0.4 (2017-11-01) diff --git a/setup.py b/setup.py index 7b39e6b..13d0095 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,9 @@ def read(*rnames): 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Natural Language :: English', 'Operating System :: OS Independent'], diff --git a/src/shoobx/wfmc/README.txt b/src/shoobx/wfmc/README.txt index bf25136..ff2ab79 100644 --- a/src/shoobx/wfmc/README.txt +++ b/src/shoobx/wfmc/README.txt @@ -64,7 +64,7 @@ the workflow executing by registering a subscriber that logs workflow events: >>> def log_workflow(event): - ... print event + ... print (event) >>> import zope.event >>> zope.event.subscribers.append(log_workflow) @@ -225,10 +225,10 @@ We'll start by defining a simple Participant class: >>> import zope.interface >>> from shoobx.wfmc import interfaces - >>> class Participant(object): + >>> @zope.interface.implementer(interfaces.IParticipant) + ... class Participant(object): ... zope.component.adapts(interfaces.IActivity) - ... zope.interface.implements(interfaces.IParticipant) - ... + ... ... def __init__(self, activity, process): ... self.activity = activity @@ -246,17 +246,17 @@ Now we'll define our work-items. First we'll define some classes: >>> work_list = [] - >>> class ApplicationBase: + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class ApplicationBase: ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) - ... + ... ... def __init__(self, participant, process, activity): ... self.participant = participant ... work_list.append(self) - ... + ... ... def start(self, args): ... pass - ... + ... ... def finish(self): ... self.participant.activity.workItemFinished(self) @@ -267,12 +267,12 @@ Now we'll define our work-items. First we'll define some classes: >>> class Publish(ApplicationBase): ... def start(self, args): - ... print "Published" + ... print ("Published") ... self.finish() >>> class Reject(ApplicationBase): ... def start(self, args): - ... print "Rejected" + ... print ("Rejected") ... self.finish() and then we'll hook them up with the integration object: @@ -769,7 +769,7 @@ them. Finally, we'll create multiple authors and use the selected one: ... def __init__(self, activity, process): ... Participant.__init__(self, activity, process) ... author_name = activity.process.workflowRelevantData.author - ... print "Author `%s` selected" % author_name + ... print(("Author `%s` selected" % author_name)) ... self.user = authors[author_name] In this example, we need to define a separate attribute for each participant: @@ -799,18 +799,18 @@ performers: Now we'll create our applications. Let's start with our author: - >>> class ApplicationBase(object): + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class ApplicationBase(object): ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) - ... + ... ... def __init__(self, participant, process, activity): ... self.participant = participant ... self.activity = participant.activity ... participant.user.work_list.append(self) - ... + ... ... def start(self, args): ... pass - ... + ... ... def finish(self): ... self.participant.activity.workItemFinished(self) @@ -820,13 +820,13 @@ Now we'll create our applications. Let's start with our author: ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') ... if doc: - ... print 'Previous draft:' - ... print doc - ... print 'Changes we need to make:' + ... print ('Previous draft:') + ... print (doc) + ... print ('Changes we need to make:') ... for change in process.workflowRelevantData.tech_changes: - ... print change + ... print (change) ... else: - ... print 'Please write the initial draft' + ... print ('Please write the initial draft') ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc @@ -896,16 +896,16 @@ We'll reuse the `publish` and `reject` application from the previous example. >>> class Final(ApplicationBase): - ... + ... ... def summary(self): ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') - ... print 'Previous draft:' - ... print self.activity.process.applicationRelevantData.doc - ... print 'Changes we need to make:' + ... print ('Previous draft:') + ... print((self.activity.process.applicationRelevantData.doc)) + ... print ('Changes we need to make:') ... for change in process.workflowRelevantData.ed_changes: - ... print change - ... + ... print (change) + ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc ... super(Final, self).finish() @@ -926,9 +926,9 @@ changes. Our process now returns data. When we create a process, we need to supply an object that it can call back to: - >>> class PublicationContext: - ... zope.interface.implements(interfaces.IProcessContext) - ... + >>> @zope.interface.implementer(interfaces.IProcessContext) + ... class PublicationContext: + ... ... def processFinished(self, process, decision): ... self.decision = decision @@ -970,7 +970,7 @@ Notice that we transitioned to *two* activities, `tech1` and Now we'll do a tech review. Let's see what tech1 has: >>> item = tech1.work_list.pop() - >>> print item.getDoc() + >>> print((item.getDoc())) I give my pledge, as an American to save, and faithfully to defend from waste the natural resources of my Country. @@ -1059,7 +1059,7 @@ weren't any technical changes. We're ready to do our editorial review. We'll request an editorial change: >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print((item.getDoc())) I give my pledge, as an Earthling to save, and faithfully to defend from waste the natural resources of my planet. @@ -1099,7 +1099,7 @@ We transition to the activity for reviewing the final edits. We review the document and approve it for publication: >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print((item.getDoc())) I give my pledge, as a Earthling to save, and faithfully to defend from waste the natural resources of my planet. @@ -1132,3 +1132,5 @@ See also --------- http://www.wfmc.org http://www.wfmc.org/standards/standards.htm + + diff --git a/src/shoobx/wfmc/adapter/integration.txt b/src/shoobx/wfmc/adapter/integration.txt index 15f75f8..7c50077 100644 --- a/src/shoobx/wfmc/adapter/integration.txt +++ b/src/shoobx/wfmc/adapter/integration.txt @@ -82,10 +82,10 @@ participant: >>> import zope.interface >>> from shoobx.wfmc import interfaces - >>> class Participant(object): + >>> @zope.interface.implementer(interfaces.IParticipant) + ... class Participant(object): ... zope.component.adapts(interfaces.IActivity) - ... zope.interface.implements(interfaces.IParticipant) - ... + ... ... def __init__(self, activity): ... self.activity = activity @@ -97,17 +97,17 @@ And finally, we can define adapters that implement our application: >>> work_list = [] - >>> class ApplicationBase: + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class ApplicationBase: ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) - ... + ... ... def __init__(self, participant): ... self.participant = participant ... work_list.append(self) - ... + ... ... def start(self, args): ... pass - ... + ... ... def finish(self): ... self.participant.activity.workItemFinished(self) @@ -124,14 +124,14 @@ And finally, we can define adapters that implement our application: >>> class Publish(ApplicationBase): ... def start(self, args): - ... print "Published" + ... print("Published") ... self.finish() >>> zope.component.provideAdapter(Publish, name=".publish") >>> class Reject(ApplicationBase): ... def start(self, args): - ... print "Rejected" + ... print("Rejected") ... self.finish() >>> zope.component.provideAdapter(Reject, name=".reject") @@ -140,7 +140,7 @@ We'll see the workflow executing by registering a subscriber that logs workflow events: >>> def log_workflow(event): - ... print event + ... print(event) >>> import zope.event >>> zope.event.subscribers.append(log_workflow) @@ -192,3 +192,5 @@ published: ActivityFinished(Activity('sample.reject')) ProcessFinished(Process('sample')) WorkItemStarted('reject') + + diff --git a/src/shoobx/wfmc/attributeintegration.py b/src/shoobx/wfmc/attributeintegration.py index 5cd77f3..5ec77a2 100644 --- a/src/shoobx/wfmc/attributeintegration.py +++ b/src/shoobx/wfmc/attributeintegration.py @@ -17,6 +17,7 @@ from zope import interface +@interface.implementer(interfaces.IIntegration) class AttributeIntegration: """Integration component that uses simple attributes @@ -24,8 +25,6 @@ class AttributeIntegration: provide participant and application factories of a given name. """ - interface.implements(interfaces.IIntegration) - def createParticipant(self, activity, proc, performer): factory = getattr(self, performer + 'Participant') return factory(activity, proc) diff --git a/src/shoobx/wfmc/deadline.txt b/src/shoobx/wfmc/deadline.txt index 43a9f0c..6ed1aaa 100644 --- a/src/shoobx/wfmc/deadline.txt +++ b/src/shoobx/wfmc/deadline.txt @@ -25,24 +25,24 @@ Define WorkItems used by the process:: >>> @zope.interface.implementer(interfaces.IWorkItem) ... class OutputWorkItem(object): ... id = None - ... + ... ... def __init__(self, process, activity): ... self.process = process ... self.activity = activity - ... + ... ... def start(self, args): - ... print "START:", args['message'] + ... print(("START: " + args['message'])) >>> @zope.interface.implementer(interfaces.IWorkItem) ... class InputWorkItem(object): ... id = None - ... + ... ... def __init__(self, process, activity): ... self.process = process ... self.activity = activity - ... + ... ... def start(self, args): - ... print "START:", args['message'] + ... print(("START: " + args['message'])) Define integration object:: @@ -133,3 +133,5 @@ Do it again without the wait >>> proc.activities.getActive()[0].finish() >>> proc.isFinished True + + diff --git a/src/shoobx/wfmc/process.py b/src/shoobx/wfmc/process.py index 44874bf..8a26d62 100644 --- a/src/shoobx/wfmc/process.py +++ b/src/shoobx/wfmc/process.py @@ -51,8 +51,8 @@ def defaultDeadlineCanceller(process, deadline): deadline.deadlineTimer.cancel() +@interface.implementer(interfaces.IProcessDefinitionFactory) class StaticProcessDefinitionFactory(object): - interface.implements(interfaces.IProcessDefinitionFactory) def __init__(self): self.definitions = {} @@ -66,10 +66,9 @@ def register(self, pd): self.definitions[pd.id] = pd +@interface.implementer(interfaces.ITransitionDefinition) class TransitionDefinition(object): - interface.implements(interfaces.ITransitionDefinition) - def __init__(self, from_, to, condition=always_true, id=None, __name__=None, otherwise=False): self.id = id @@ -88,10 +87,9 @@ def __repr__(self): return "TransitionDefinition(from=%r, to=%r)" % (self.from_, self.to) +@interface.implementer(interfaces.IProcessDefinition) class ProcessDefinition(object): - interface.implements(interfaces.IProcessDefinition) - TransitionDefinitionFactory = TransitionDefinition def __init__(self, id, integration=None): @@ -207,10 +205,9 @@ def _dirty(self): pass -class ActivityDefinition(object): - - interface.implements(interfaces.IActivityDefinition, +@interface.implementer(interfaces.IActivityDefinition, interfaces.IExtendedAttributesContainer) +class ActivityDefinition(object): performer = '' process = None @@ -287,8 +284,8 @@ def __init__(self, activity, deadline_time, deadlinedef): 'are supported at this point.') +@interface.implementer(interfaces.IActivity) class Activity(persistent.Persistent): - interface.implements(interfaces.IActivity) DeadlineFactory = Deadline incoming = () @@ -448,7 +445,10 @@ def start(self, transition): if self.workitems: evaluator = getEvaluator(self.process) - for workitem, app, formal, actual in self.workitems.values(): + workitems = list(self.workitems.values()) + # We need the list() here to make a copy to + # loop over, as we modify self.workitems in the loop. + for workitem, app, formal, actual in list(self.workitems.values()): __traceback_info__ = ( workitem, self.activity_definition_identifier) @@ -536,8 +536,9 @@ def abort(self, cancelDeadlineTimer=True): self.active = False - # Abort all workitems. - for workitem, app, formal, actual in self.workitems.values(): + # Abort all workitems. We need the list() here to make a copy to + # loop over, as we modify self.workitems in the loop. + for workitem, app, formal, actual in list(self.workitems.values()): if interfaces.IAbortWorkItem.providedBy(workitem): workitem.abort() zope.event.notify(WorkItemAborted(workitem, app, actual)) @@ -607,8 +608,8 @@ def current(self): return self.counter +@interface.implementer(interfaces.IActivityContainer) class ActivityContainer(dict): - interface.implements(interfaces.IActivityContainer) def getActive(self): return [a for a in self.values() if a.active] @@ -617,10 +618,9 @@ def getFinished(self): return [a for a in self.values() if not a.active] +@interface.implementer(interfaces.IProcess) class Process(persistent.Persistent): - interface.implements(interfaces.IProcess) - ActivityFactory = Activity WorkflowDataFactory = WorkflowData @@ -844,8 +844,8 @@ def deadlinePassedHandler(self, deadline): activity.process.transition(activity, transitions) +@interface.implementer(interfaces.IProcessStarted) class ProcessStarted: - interface.implements(interfaces.IProcessStarted) def __init__(self, process): self.process = process @@ -854,8 +854,8 @@ def __repr__(self): return "ProcessStarted(%r)" % self.process +@interface.implementer(interfaces.IProcessFinished) class ProcessFinished: - interface.implements(interfaces.IProcessFinished) def __init__(self, process): self.process = process @@ -864,8 +864,8 @@ def __repr__(self): return "ProcessFinished(%r)" % self.process +@interface.implementer(interfaces.IProcessAborted) class ProcessAborted: - interface.implements(interfaces.IProcessAborted) def __init__(self, process): self.process = process @@ -1125,10 +1125,9 @@ def __repr__(self): return "ActivityStarted(%r)" % self.activity +@interface.implementer(interfaces.IParameterDefinition) class Parameter(object): - interface.implements(interfaces.IParameterDefinition) - input = output = False initialValue = None @@ -1150,10 +1149,9 @@ class InputOutputParameter(InputParameter, OutputParameter): pass -class Application: - - interface.implements(interfaces.IApplicationDefinition, +@interface.implementer(interfaces.IApplicationDefinition, interfaces.IExtendedAttributesContainer) +class Application: def __init__(self, *parameters): self.parameters = parameters @@ -1170,10 +1168,9 @@ def __repr__(self): return " (%s)>" % (self.id, input, output) -class Participant: - - interface.implements(interfaces.IParticipantDefinition, +@interface.implementer(interfaces.IParticipantDefinition, interfaces.IExtendedAttributesContainer) +class Participant: def __init__(self, name=None, type=None): self.__name__ = name @@ -1185,10 +1182,9 @@ def __repr__(self): return "Participant(%r, %r)" % (self.__name__, self.type) +@interface.implementer(interfaces.IDataFieldDefinition) class DataField: - interface.implements(interfaces.IDataFieldDefinition) - def __init__(self, name=None, title=None, initialValue=None): self.__name__ = name self.title = title @@ -1204,13 +1200,13 @@ def __repr__(self): if k in ALLOWED_BUILTIN_NAMES} +@interface.implementer(interfaces.IPythonExpressionEvaluator) class PythonExpressionEvaluator(object): """Simple Python Expression Evaluator. This evaluator only produces a limited namespace and does not use a safe Python engine. """ - interface.implements(interfaces.IPythonExpressionEvaluator) component.adapts(interfaces.IProcess) def __init__(self, process): @@ -1232,7 +1228,7 @@ def execute(self, code, locals={}): ns.update(locals) ns.update(ALLOWED_BUILTINS) result = {} - exec code in ALLOWED_BUILTINS, result + exec(code, ALLOWED_BUILTINS, result) for name, value in result: pass diff --git a/src/shoobx/wfmc/subflow.txt b/src/shoobx/wfmc/subflow.txt index f69cdb0..fb9b000 100644 --- a/src/shoobx/wfmc/subflow.txt +++ b/src/shoobx/wfmc/subflow.txt @@ -25,25 +25,25 @@ Define WorkItems used by the process:: >>> @zope.interface.implementer(interfaces.IWorkItem) ... class OutputWorkItem(object): ... id = None - ... + ... ... def __init__(self, process, activity): ... self.process = process ... self.activity = activity - ... + ... ... def start(self, args): - ... print "OUTPUT:", args['message'] + ... print(("OUTPUT: " + args['message'])) ... self.activity.workItemFinished(self) >>> @zope.interface.implementer(interfaces.IWorkItem) ... class InputWorkItem(object): ... id = None - ... + ... ... def __init__(self, process, activity): ... self.process = process ... self.activity = activity - ... + ... ... def start(self, args): - ... print "INPUT:", args['message'] + ... print(("INPUT: " + args['message'])) Define integration object:: @@ -89,7 +89,7 @@ Now we can create and execute our process:: >>> lastactivity.activity_definition_identifier 'innerflow_first' - >>> wi, _, _, _ = lastactivity.workitems.values()[0] + >>> wi, _, _, _ = list((lastactivity.workitems.values()))[0] >>> lastactivity.workItemFinished(wi, {'result': "from-the-deep"}) INPUT: Hello Parameter @@ -97,12 +97,12 @@ Now we can create and execute our process:: >>> lastactivity.activity_definition_identifier 'subflow_second' - >>> wi, _, _, _ = lastactivity.workitems.values()[0] + >>> wi, _, _, _ = list((lastactivity.workitems.values()))[0] >>> lastactivity.workItemFinished(wi, {'result': "completed"}) INPUT: Inner subflow activity >>> lastactivity = proc.activities[max(proc.activities.keys())] - >>> wi, _, _, _ = lastactivity.workitems.values()[0] + >>> wi, _, _, _ = list((lastactivity.workitems.values()))[0] >>> lastactivity.workItemFinished(wi, {'result': "from-the-deep-2"}) OUTPUT: Second mainflow activity @@ -127,3 +127,5 @@ Subflow output variable, however, is written to main flow context: >>> proc.workflowRelevantData.subflow_result 'completed' + + diff --git a/src/shoobx/wfmc/tests.py b/src/shoobx/wfmc/tests.py index d0707e4..64b4b7f 100644 --- a/src/shoobx/wfmc/tests.py +++ b/src/shoobx/wfmc/tests.py @@ -13,14 +13,30 @@ ############################################################################## """Test hookup """ +from __future__ import print_function import os +import re +import sys import unittest import zope.event import zope.interface + from zope.component import testing, provideAdapter from zope.testing import doctest - from shoobx.wfmc import interfaces, process +from doctest import OutputChecker, DocTestSuite + + +class Py23DocChecker(OutputChecker): + def check_output(self, want, got, optionflags): + if sys.version_info[0] == 3: + # if running on py2, attempt to prefix all the strings + # with "u" to signify that they're unicode literals + want = re.sub("u'(.*?)'", "'\\1'", want) + want = re.sub('u"(.*?)"', '"\\1"', want) + else: + want = re.sub('shoobx.wfmc.xpdl.HandlerError', 'HandlerError', want) + return OutputChecker.check_output(self, want, got, optionflags) def tearDown(test): @@ -46,16 +62,16 @@ def __init__(self, participant, process, activity): def start(self, args): self.args = args - print 'Workitem %i for activity %r started.' % ( - self.id, self.activity.definition.id) + print('Workitem %i for activity %r started.' % ( + self.id, self.activity.definition.id)) def abort(self): - print 'Workitem %i for activity %r aborted.' % ( - self.id, self.activity.definition.id) + print('Workitem %i for activity %r aborted.' % ( + self.id, self.activity.definition.id)) def revert(self): - print 'Workitem %i for activity %r reverted.' % ( - self.id, self.activity.definition.id) + print('Workitem %i for activity %r reverted.' % ( + self.id, self.activity.definition.id)) def test_multiple_input_parameters(): @@ -94,9 +110,9 @@ def test_multiple_input_parameters(): >>> from shoobx.wfmc import interfaces - >>> class Participant(object): + >>> @zope.interface.implementer(interfaces.IParticipant) + ... class Participant(object): ... zope.component.adapts(interfaces.IActivity) - ... zope.interface.implements(interfaces.IParticipant) ... ... def __init__(self, activity, process): ... self.activity = activity @@ -108,16 +124,16 @@ def test_multiple_input_parameters(): >>> integration.Participant = Participant - >>> class Eek: + >>> @interface.implementer(interfaces.IWorkItem) + ... class Eek: ... component.adapts(interfaces.IParticipant) - ... interface.implements(interfaces.IWorkItem) ... ... def __init__(self, participant, process, activity): ... self.participant = participant ... ... def start(self, args): ... x = args['x']; y=args['y'] - ... print x, y + ... print(x, y) >>> integration.eekWorkItem = Eek @@ -364,7 +380,10 @@ def test_suite(): suite.addTest(doctest.DocFileSuite( doctestfile, setUp=setUp, tearDown=tearDown, + checker=Py23DocChecker(), optionflags=doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF)) suite.addTest(doctest.DocTestSuite( setUp=setUp, tearDown=testing.tearDown)) return suite + + diff --git a/src/shoobx/wfmc/xpdl-2.1.txt b/src/shoobx/wfmc/xpdl-2.1.txt index e0a872a..42d43d9 100644 --- a/src/shoobx/wfmc/xpdl-2.1.txt +++ b/src/shoobx/wfmc/xpdl-2.1.txt @@ -27,7 +27,7 @@ before (in "README.txt"). As before, we'll create an event subscriber so that we can see what's going on: >>> def log_workflow(event): - ... print event + ... print (event) >>> import zope.event >>> zope.event.subscribers.append(log_workflow) @@ -45,9 +45,9 @@ and we'll define and register participant and application adapters: >>> import zope.interface >>> from shoobx.wfmc import interfaces - >>> class Participant(object): + >>> @zope.interface.implementer(interfaces.IParticipant) + ... class Participant(object): ... zope.component.adapts(interfaces.IActivity) - ... zope.interface.implements(interfaces.IParticipant) ... ... def __init__(self, activity, process): ... self.activity = activity @@ -66,7 +66,7 @@ and we'll define and register participant and application adapters: ... def __init__(self, activity, process): ... Participant.__init__(self, activity, process) ... author_name = activity.process.workflowRelevantData.author - ... print "Author `%s` selected" % author_name + ... print("Author `%s` selected" % author_name) ... self.user = authors[author_name] >>> integration.authorParticipant = Author @@ -85,9 +85,9 @@ and we'll define and register participant and application adapters: >>> integration.SystemParticipant = Participant - >>> class ApplicationBase(object): + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class ApplicationBase(object): ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) ... ... def __init__(self, participant, process, activity): ... self.participant = participant @@ -106,13 +106,13 @@ and we'll define and register participant and application adapters: ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') ... if doc: - ... print 'Previous draft:' - ... print doc - ... print 'Changes we need to make:' + ... print('Previous draft:') + ... print(doc) + ... print('Changes we need to make:') ... for change in process.workflowRelevantData.tech_changes: - ... print change + ... print(change) ... else: - ... print 'Please write the initial draft' + ... print('Please write the initial draft') ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc @@ -165,11 +165,11 @@ and we'll define and register participant and application adapters: ... def summary(self): ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') - ... print 'Previous draft:' - ... print self.activity.process.applicationRelevantData.doc - ... print 'Changes we need to make:' + ... print('Previous draft:') + ... print(self.activity.process.applicationRelevantData.doc) + ... print('Changes we need to make:') ... for change in process.workflowRelevantData.ed_changes: - ... print change + ... print(change) ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc @@ -186,15 +186,15 @@ and we'll define and register participant and application adapters: >>> integration.rfinalWorkItem = ReviewFinal - >>> class Publish: + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class Publish: ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) ... ... def __init__(self, participant, process, activity): ... self.participant = participant ... ... def start(self, args): - ... print "Published" + ... print("Published") ... self.finish() ... ... def finish(self): @@ -205,15 +205,16 @@ and we'll define and register participant and application adapters: >>> class Reject(Publish): ... def start(self): - ... print "Rejected" + ... print("Rejected") ... self.finish() >>> integration.rejectWorkItem = Reject and a process context, so we can pass parameters: - >>> class PublicationContext: - ... zope.interface.implements(interfaces.IProcessContext) + >>> @zope.interface.implementer(interfaces.IProcessContext) + ... class PublicationContext: + ... pass Now, let's try out our process. We'll follow the same steps we did in @@ -254,7 +255,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'tech_review') >>> item = tech1.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as an American to save, and faithfully to defend from waste the natural resources of my Country. @@ -339,7 +340,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'ed_review') >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as an human to save, and faithfully to defend from waste the natural resources of my planet. @@ -378,7 +379,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'rfinal') >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as a human to save, and faithfully to defend from waste the natural resources of my planet. @@ -443,10 +444,9 @@ Most process elements can have names and descriptions. u'Transition', u'Transition', u'Transition to Tech Review 1', u'Transition to Tech Review 2'] - >>> sorted([item.description for item in pd.transitions]) - [None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, - u'Use this transition if there are editorial changes required.'] + >>> descriptions = [item.description for item in pd.transitions if item.description] + >>> u'Use this transition if there are editorial changes required.' in descriptions + True Pools and Lanes @@ -510,7 +510,7 @@ Having two ExtendedAttribute elements is nonsense. >>> package = xpdl.read(open(os.path.join(this_directory, fname))) # doctest: +ELLIPSIS Traceback (most recent call last): ... - HandlerError: u"The Name 'ParticipantID' is already used, uniqueness violated, value: 'system' see: Publication." + shoobx.wfmc.xpdl.HandlerError: u"The Name 'ParticipantID' is already used, uniqueness violated, value: 'system' see: Publication." File ".../shoobx/wfmc/publication-2.1-duplicate-ExtendedAttribute.xpdl", line 240. in ExtendedAttribute Sometimes we can't always raise an exception when this happens so instead @@ -519,4 +519,6 @@ we can record the parse error. >>> package = xpdl.read(open(os.path.join(this_directory, fname))) >>> package.parseErrors [u"The Name 'ParticipantID' is already used, uniqueness violated, value: 'system' see: Publication."] - + + + diff --git a/src/shoobx/wfmc/xpdl.py b/src/shoobx/wfmc/xpdl.py index cbbdbb7..85e4b96 100644 --- a/src/shoobx/wfmc/xpdl.py +++ b/src/shoobx/wfmc/xpdl.py @@ -13,6 +13,7 @@ ############################################################################## """XPDL reader for process definitions """ +from __future__ import absolute_import import logging import sys import xml.sax @@ -22,6 +23,7 @@ import shoobx.wfmc.process from zope import interface from shoobx.wfmc import interfaces +import six xpdlns10 = "http://www.wfmc.org/2002/XPDL1.0" xpdlns21 = "http://www.wfmc.org/2008/XPDL2.1" @@ -47,6 +49,9 @@ def __str__(self): return ('%s\nFile "%s", line %s. in %s' % (self.orig, self.xml, self.line, self.tag)) + def __call__(self): + return self + class Package(dict): @@ -76,10 +81,9 @@ def addScript(self, script): self.script = script +@interface.implementer(interfaces.IPoolDefinition) class PoolDefinition: - interface.implements(interfaces.IPoolDefinition) - def __init__(self, name=None, process_def=None): self.__name__ = name self.process_def = process_def @@ -94,10 +98,9 @@ def __repr__(self): return "Pool(%r, %r)" % (self.__name__, self.process_def) +@interface.implementer(interfaces.ILaneDefinition) class LaneDefinition: - interface.implements(interfaces.ILaneDefinition) - def __init__(self, name=None): self.__name__ = name self.performers = () @@ -158,8 +161,8 @@ def startElementNS(self, name, qname, attrs): try: result = handler(self, attrs) except: - raise HandlerError(sys.exc_info()[1], name[1], self.locator - ), None, sys.exc_info()[2] + six.reraise(HandlerError(sys.exc_info()[1], name[1], self.locator + ), None, sys.exc_info()[2]) else: result = None @@ -177,8 +180,8 @@ def endElementNS(self, name, qname): try: handler(self, last) except: - raise HandlerError(sys.exc_info()[1], name[1], self.locator - ), None, sys.exc_info()[2] + six.reraise(HandlerError(sys.exc_info()[1], name[1], self.locator + ), None, sys.exc_info()[2]) self.textstack.pop() diff --git a/src/shoobx/wfmc/xpdl.txt b/src/shoobx/wfmc/xpdl.txt index 6fc82e8..e8aad1a 100644 --- a/src/shoobx/wfmc/xpdl.txt +++ b/src/shoobx/wfmc/xpdl.txt @@ -31,7 +31,7 @@ before (in "README.txt"). As before, we'll create an event subscriber so that we can see what's going on: >>> def log_workflow(event): - ... print event + ... print(event) >>> import zope.event >>> zope.event.subscribers.append(log_workflow) @@ -49,9 +49,9 @@ and we'll define and register participant and application adapters: >>> import zope.interface >>> from shoobx.wfmc import interfaces - >>> class Participant(object): + >>> @zope.interface.implementer(interfaces.IParticipant) + ... class Participant(object): ... zope.component.adapts(interfaces.IActivity) - ... zope.interface.implements(interfaces.IParticipant) ... ... def __init__(self, activity, process): ... self.activity = activity @@ -70,7 +70,7 @@ and we'll define and register participant and application adapters: ... def __init__(self, activity, process): ... Participant.__init__(self, activity, process) ... author_name = activity.process.workflowRelevantData.author - ... print "Author `%s` selected" % author_name + ... print("Author `%s` selected" % author_name) ... self.user = authors[author_name] >>> integration.authorParticipant = Author @@ -89,9 +89,9 @@ and we'll define and register participant and application adapters: >>> integration.SystemParticipant = Participant - >>> class ApplicationBase(object): + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class ApplicationBase(object): ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) ... ... def __init__(self, participant, process, activity): ... self.participant = participant @@ -110,13 +110,13 @@ and we'll define and register participant and application adapters: ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') ... if doc: - ... print 'Previous draft:' - ... print doc - ... print 'Changes we need to make:' + ... print('Previous draft:') + ... print(doc) + ... print('Changes we need to make:') ... for change in process.workflowRelevantData.tech_changes: - ... print change + ... print(change) ... else: - ... print 'Please write the initial draft' + ... print('Please write the initial draft') ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc @@ -143,16 +143,16 @@ and we'll define and register participant and application adapters: ... changes1 = args['tech_changes1'] ... changes2 = args['tech_changes2'] ... if not (publish1 and publish2): - ... output = {'publish': False, - ... 'tech_changes': changes1 + changes2, + ... output = {'publish': False, + ... 'tech_changes': changes1 + changes2, ... 'ed_changes': ()} ... # Reject if either tech reviewer rejects ... self.activity.workItemFinished( ... self, output) ... ... if changes1 or changes2: - ... output = {'publish': True, - ... 'tech_changes': changes1 + changes2, + ... output = {'publish': True, + ... 'tech_changes': changes1 + changes2, ... 'ed_changes': ()} ... # we won't do anyting if there are tech changes ... self.activity.workItemFinished( @@ -169,11 +169,11 @@ and we'll define and register participant and application adapters: ... def summary(self): ... process = self.activity.process ... doc = getattr(process.applicationRelevantData, 'doc', '') - ... print 'Previous draft:' - ... print self.activity.process.applicationRelevantData.doc - ... print 'Changes we need to make:' + ... print('Previous draft:') + ... print(self.activity.process.applicationRelevantData.doc) + ... print('Changes we need to make:') ... for change in process.workflowRelevantData.ed_changes: - ... print change + ... print(change) ... ... def finish(self, doc): ... self.activity.process.applicationRelevantData.doc = doc @@ -190,15 +190,15 @@ and we'll define and register participant and application adapters: >>> integration.rfinalWorkItem = ReviewFinal - >>> class Publish: + >>> @zope.interface.implementer(interfaces.IWorkItem) + ... class Publish: ... zope.component.adapts(interfaces.IParticipant) - ... zope.interface.implements(interfaces.IWorkItem) ... ... def __init__(self, participant, process, activity): ... self.participant = participant ... ... def start(self, args): - ... print "Published" + ... print("Published") ... self.finish() ... ... def finish(self): @@ -209,15 +209,15 @@ and we'll define and register participant and application adapters: >>> class Reject(Publish): ... def start(self, args): - ... print "Rejected" + ... print("Rejected") ... self.finish() >>> integration.rejectWorkItem = Reject and a process context, so we can pass parameters: - >>> class PublicationContext: - ... zope.interface.implements(interfaces.IProcessContext) + >>> @zope.interface.implementer(interfaces.IProcessContext) + ... class PublicationContext: ... ... def processFinished(self, process, decision): ... self.decision = decision @@ -257,7 +257,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'tech_review') >>> item = tech1.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as an American to save, and faithfully to defend from waste the natural resources of my Country. @@ -330,7 +330,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'ed_review') >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as an human to save, and faithfully to defend from waste the natural resources of my planet. @@ -366,7 +366,7 @@ Now, let's try out our process. We'll follow the same steps we did in WorkItemStarted(u'rfinal') >>> item = reviewer.work_list.pop() - >>> print item.getDoc() + >>> print(item.getDoc()) I give my pledge, as a human to save, and faithfully to defend from waste the natural resources of my planet. @@ -423,6 +423,8 @@ Most process elements can have names and descriptions. u'Transition', u'Transition', u'Transition to Tech Review 1', u'Transition to Tech Review 2'] - >>> sorted([item.description for item in pd.transitions]) - [None, None, None, None, None, None, None, None, None, None, None, - u'Use this transition if there are editorial changes required.'] + >>> descriptions = [item.description for item in pd.transitions if item.description] + >>> u'Use this transition if there are editorial changes required.' in descriptions + True + + diff --git a/tox.ini b/tox.ini index 08373fe..3412fcd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27 +envlist = py27, py35, py36 [testenv] commands =