From 11218c34938d4922966a6a58eb4028006d6a1cb9 Mon Sep 17 00:00:00 2001 From: pbauer Date: Fri, 21 Sep 2018 09:55:38 +0200 Subject: [PATCH] [fc] Repository: plone.contentrules Branch: refs/heads/master Date: 2018-05-25T11:29:50+02:00 Author: Philip Bauer (pbauer) Commit: https://github.com/plone/plone.contentrules/commit/a1b17d5aec2471c70b5ad47eeff34edb98c639ae fix tests in py3 Files changed: M plone/contentrules/README.rst M plone/contentrules/tests.py M plone/contentrules/zcml.rst Repository: plone.contentrules Branch: refs/heads/master Date: 2018-09-18T20:10:03+02:00 Author: Philip Bauer (pbauer) Commit: https://github.com/plone/plone.contentrules/commit/dc12de8f2e60c9bc73c264fb551c69bf6fc716fb add changelog and classifiers Files changed: M CHANGES.rst M setup.py Repository: plone.contentrules Branch: refs/heads/master Date: 2018-09-21T09:55:38+02:00 Author: Philip Bauer (pbauer) Commit: https://github.com/plone/plone.contentrules/commit/0bd184dddafc06b12d120216dbdc49e44bf4edf7 Merge pull request #8 from plone/python3 fix tests in py3 Files changed: M CHANGES.rst M plone/contentrules/README.rst M plone/contentrules/tests.py M plone/contentrules/zcml.rst M setup.py --- last_commit.txt | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index ea7fbdad80..ca1bbed11a 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,52 +1,54 @@ -Repository: plone.registry +Repository: plone.contentrules Branch: refs/heads/master -Date: 2018-09-20T22:12:48+02:00 +Date: 2018-05-25T11:29:50+02:00 Author: Philip Bauer (pbauer) -Commit: https://github.com/plone/plone.registry/commit/a25d8fe05b88746f3e5815447fcc403b8dea8b82 +Commit: https://github.com/plone/plone.contentrules/commit/a1b17d5aec2471c70b5ad47eeff34edb98c639ae -fix test for changed object field in zope4 +fix tests in py3 Files changed: -M plone/registry/registry.rst -M plone/registry/tests.py +M plone/contentrules/README.rst +M plone/contentrules/tests.py +M plone/contentrules/zcml.rst -b"diff --git a/plone/registry/registry.rst b/plone/registry/registry.rst\nindex d6a1f39..5952d69 100644\n--- a/plone/registry/registry.rst\n+++ b/plone/registry/registry.rst\n@@ -142,7 +142,7 @@ Again, values are validated::\n Traceback (most recent call last):\n ...\n WrongType: ('Joomla', ...)\n- \n+\n >>> registry['plone.registry.tests.cms'] = b'Joomla' # doctest: +SKIP_PYTHON_2\n Traceback (most recent call last):\n ...\n@@ -215,7 +215,7 @@ If we have a field for which there is no ``IPersistentField`` adapter, we will g\n \n >>> from plone.registry.tests import IMailPreferences\n >>> IMailPreferences['settings']\n- \n+ \n \n >>> registry.registerInterface(IMailPreferences)\n Traceback (most recent call last):\ndiff --git a/plone/registry/tests.py b/plone/registry/tests.py\nindex 626e726..d89ab62 100644\n--- a/plone/registry/tests.py\n+++ b/plone/registry/tests.py\n@@ -21,6 +21,11 @@\n \n class PolyglotOutputChecker(doctest.OutputChecker):\n def check_output(self, want, got, optionflags):\n+ # fix changed objectfield class in zope4\n+ got = re.sub(\n+ 'zope.schema._field.Object',\n+ 'zope.schema._bootstrapfields.Object', got)\n+\n if optionflags & SKIP_PYTHON_3 and sys.version_info >= (3,):\n return True\n elif optionflags & SKIP_PYTHON_2:\n" +b'diff --git a/plone/contentrules/README.rst b/plone/contentrules/README.rst\nindex 291a98c..d2db5f0 100644\n--- a/plone/contentrules/README.rst\n+++ b/plone/contentrules/README.rst\n@@ -30,8 +30,8 @@ First, we create some rule elements.\n \n Lets start with some basic imports:\n \n- >>> from zope.interface import Interface, implements\n- >>> from zope.component import adapts\n+ >>> from zope.interface import Interface, implementer\n+ >>> from zope.component import adapter\n >>> from zope.component import getUtility, getAllUtilitiesRegisteredFor\n >>> from zope import schema\n \n@@ -56,8 +56,8 @@ and summary properties come from IRuleElementData and are used by the\n user interface to discover the edit view and present a title and summery\n to the user:\n \n- >>> class MoveToFolderAction(Persistent):\n- ... implements(IMoveToFolderAction, IRuleElementData)\n+ >>> @implementer(IMoveToFolderAction, IRuleElementData)\n+ ... class MoveToFolderAction(Persistent):\n ... targetFolder = \'\'\n ... element = "test.moveToFolder"\n ... @property\n@@ -71,15 +71,15 @@ adaptable to IExecutable. This should be a multi-adapter from\n >>> from plone.contentrules.rule.interfaces import IExecutable\n >>> from zope.component.interfaces import IObjectEvent\n \n- >>> class MoveToFolderExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IMoveToFolderAction, IObjectEvent)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IMoveToFolderAction, IObjectEvent)\n+ ... class MoveToFolderExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n ... self.event = event\n ... def __call__(self):\n- ... print "Tried to execute MoveToFolderExecutor, but not implemented"\n+ ... print("Tried to execute MoveToFolderExecutor, but not implemented")\n ... return True\n \n >>> provideAdapter(MoveToFolderExecutor)\n@@ -127,8 +127,8 @@ Again, we have to define an interface for the logger action:\n \n A factory class holding configuration data:\n \n- >>> class LoggerAction(Persistent):\n- ... implements(ILoggerAction, IRuleElementData)\n+ >>> @implementer(ILoggerAction, IRuleElementData)\n+ ... class LoggerAction(Persistent):\n ... loggingLevel = \'\'\n ... targetLogger = \'\'\n ... message = \'\'\n@@ -138,9 +138,9 @@ A factory class holding configuration data:\n As well as the executor that does the actual logging, capable of being adapted\n to IExecutable. In this case, it will adapt any context and any event.\n \n- >>> class LoggerActionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, ILoggerAction, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, ILoggerAction, Interface)\n+ ... class LoggerActionExecutor(object):\n ...\n ... def __init__(self, context, element, event):\n ... self.context = context\n@@ -182,17 +182,17 @@ a given interface.\n >>> class IInterfaceCondition(Interface):\n ... iface = Attribute(u\'the interface\')\n \n- >>> class InterfaceCondition(object):\n- ... implements (IInterfaceCondition, IRuleElementData)\n+ >>> @implementer (IInterfaceCondition, IRuleElementData)\n+ ... class InterfaceCondition(object):\n ... iface = None\n ... element = "test.interface"\n ... @property\n ... def summary(self):\n ... return "Check for interface " + self.iface.__identifier__\n \n- >>> class InterfaceConditionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IInterfaceCondition, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IInterfaceCondition, Interface)\n+ ... class InterfaceConditionExecutor(object):\n ...\n ... def __init__(self, context, element, event):\n ... self.context = context\n@@ -226,14 +226,14 @@ present:\n >>> class IHaltExecutionAction(Interface):\n ... pass\n \n- >>> class HaltExecutionAction(Persistent):\n- ... implements (IHaltExecutionAction, IRuleElementData)\n+ >>> @implementer (IHaltExecutionAction, IRuleElementData)\n+ ... class HaltExecutionAction(Persistent):\n ... element = "test.halt"\n ... summary = "Halt!"\n \n- >>> class HaltExecutionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IHaltExecutionAction, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IHaltExecutionAction, Interface)\n+ ... class HaltExecutionExecutor(object):\n ... # Above: the second "Interface" causes this\n ... # element to be available for every event\n ... def __init__(self, context, element, event):\n@@ -241,7 +241,7 @@ present:\n ... self.element = element\n ... self.event = event\n ... def __call__(self):\n- ... print "Rule Execution aborted at HaltAction"\n+ ... print("Rule Execution aborted at HaltAction")\n ... return False # False = Stop Execution! This is the payload.\n \n >>> provideAdapter(HaltExecutionExecutor)\n@@ -273,8 +273,9 @@ itself implies IAttributeAnnotatable.\n >>> from plone.contentrules.engine.interfaces import IRuleAssignable\n >>> class IMyContent(IRuleAssignable):\n ... pass\n- >>> class MyContent(object):\n- ... implements(IMyContent)\n+ >>> @implementer(IMyContent)\n+ ... class MyContent(object):\n+ ... pass\n \n >>> context = MyContent()\n \n@@ -459,8 +460,9 @@ higher up. Rules that are assigned not to bubble will not be executed.\n \n Now consider what would happen if the interface condition failed:\n \n- >>> class OtherContent(object):\n- ... implements(IRuleAssignable)\n+ >>> @implementer(IRuleAssignable)\n+ ... class OtherContent(object):\n+ ... pass\n >>> otherContext = OtherContent()\n \n >>> otherManager = IRuleAssignmentManager(otherContext)\n@@ -522,11 +524,12 @@ An element for IObjectCreatedEvent:\n \n >>> class IObjectCreatedSpecificAction(Interface):\n ... pass\n- >>> class ObjectCreatedSpecificAction(Persistent):\n- ... implements (IObjectCreatedSpecificAction)\n- >>> class ObjectCreatedExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IObjectCreatedSpecificAction, IObjectCreatedEvent) #!\n+ >>> @implementer (IObjectCreatedSpecificAction)\n+ ... class ObjectCreatedSpecificAction(Persistent):\n+ ... pass\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IObjectCreatedSpecificAction, IObjectCreatedEvent) #!\n+ ... class ObjectCreatedExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n@@ -552,11 +555,12 @@ An element for IObjectCopiedEvent:\n \n >>> class IObjectCopiedSpecificAction(Interface):\n ... pass\n- >>> class ObjectCopiedSpecificAction(Persistent):\n- ... implements (IObjectCopiedSpecificAction)\n- >>> class ObjectCopiedExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IObjectCopiedSpecificAction, IObjectCopiedEvent) #!\n+ >>> @implementer (IObjectCopiedSpecificAction)\n+ ... class ObjectCopiedSpecificAction(Persistent):\n+ ... pass\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IObjectCopiedSpecificAction, IObjectCopiedEvent) #!\n+ ... class ObjectCopiedExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n@@ -579,15 +583,15 @@ An element for IObjectCopiedEvent:\n \n All elements so far, applicable for object events:\n \n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\']\n \n For a more specific event, we may get more elements (i.e. those that also\n apply to more general events):\n \n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectCopiedEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectCopiedEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\', \'Object Created specific action\', \'Object Copied Specific Action\']\n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectCreatedEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectCreatedEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\', \'Object Created specific action\']\n \n Filtering for specific events:\ndiff --git a/plone/contentrules/tests.py b/plone/contentrules/tests.py\nindex 599f04f..b31d21b 100644\n--- a/plone/contentrules/tests.py\n+++ b/plone/contentrules/tests.py\n@@ -1,4 +1,6 @@\n import doctest\n+import re\n+import six\n import unittest\n \n from zope.component.testing import PlacelessSetup as CAPlacelessSetup\n@@ -8,6 +10,14 @@\n optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS\n \n \n+class Py23DocChecker(doctest.OutputChecker):\n+\n+ def check_output(self, want, got, optionflags):\n+ if not six.PY2:\n+ want = re.sub("u\'(.*?)\'", "\'\\\\1\'", want)\n+ return doctest.OutputChecker.check_output(self, want, got, optionflags)\n+\n+\n class PlacelessSetup(CAPlacelessSetup, ContainerPlacelessSetup):\n \n def setUp(self, doctesttest=None):\n@@ -36,10 +46,14 @@ def test_suite():\n \'README.rst\',\n setUp=configurationSetUp,\n tearDown=configurationTearDown,\n- optionflags=optionflags),\n+ optionflags=optionflags,\n+ checker=Py23DocChecker(),\n+ ),\n doctest.DocFileSuite(\n \'zcml.rst\',\n setUp=configurationSetUp,\n tearDown=configurationTearDown,\n- optionflags=optionflags),\n+ optionflags=optionflags,\n+ checker=Py23DocChecker(),\n+ ),\n ))\ndiff --git a/plone/contentrules/zcml.rst b/plone/contentrules/zcml.rst\nindex b42d936..2f47597 100644\n--- a/plone/contentrules/zcml.rst\n+++ b/plone/contentrules/zcml.rst\n@@ -46,7 +46,8 @@ Here is how we would register these in ZCML:\n ... """\n \n >>> from zope.configuration.xmlconfig import xmlconfig\n- >>> from StringIO import StringIO\n+ >>> import six\n+ >>> from six import StringIO\n \n First, we need to make sure the ZCML directives are defined:\n \n' -Repository: plone.registry +Repository: plone.contentrules Branch: refs/heads/master -Date: 2018-09-20T22:16:13+02:00 +Date: 2018-09-18T20:10:03+02:00 Author: Philip Bauer (pbauer) -Commit: https://github.com/plone/plone.registry/commit/37a93b508fe0fa6ac06da7eeebd2e78531ba8f29 +Commit: https://github.com/plone/plone.contentrules/commit/dc12de8f2e60c9bc73c264fb551c69bf6fc716fb -add docstring and classifiers +add changelog and classifiers Files changed: M CHANGES.rst M setup.py -b'diff --git a/CHANGES.rst b/CHANGES.rst\nindex 421fb1a..b6027eb 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -14,7 +14,8 @@ New features:\n \n Bug fixes:\n \n-- *add item here*\n+- Adapt test to changed object field in zope4\n+ [pbauer]\n \n \n 1.1.3 (2018-06-22)\ndiff --git a/setup.py b/setup.py\nindex 24ec8da..ae05a88 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -32,11 +32,14 @@ def read(*rnames):\n classifiers=[\n "Framework :: Plone",\n "Framework :: Plone :: 5.1",\n+ "Framework :: Plone :: 5.2",\n "License :: OSI Approved :: GNU General Public License (GPL)",\n "Programming Language :: Python :: 2",\n "Programming Language :: Python :: 2.7",\n "Programming Language :: Python :: 3",\n "Programming Language :: Python :: 3.5",\n+ "Programming Language :: Python :: 3.6",\n+ "Programming Language :: Python :: 3.7",\n "Topic :: Software Development :: Libraries :: Python Modules",\n ],\n keywords=\'configuration registry\',\n' +b'diff --git a/CHANGES.rst b/CHANGES.rst\nindex 90444a6..c88f3d7 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -14,7 +14,8 @@ New features:\n \n Bug fixes:\n \n-- *add item here*\n+- Fix tests in py3.\n+ [pbauer]\n \n \n 2.0.7 (2018-01-30)\ndiff --git a/setup.py b/setup.py\nindex 51775a2..cff8dfb 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -14,12 +14,15 @@\n "Framework :: Plone :: 4.3",\n "Framework :: Plone :: 5.0",\n "Framework :: Plone :: 5.1",\n+ "Framework :: Plone :: 5.2",\n "Framework :: Zope2",\n "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",\n "Operating System :: OS Independent",\n "Programming Language :: Python",\n "Programming Language :: Python :: 2.6",\n "Programming Language :: Python :: 2.7",\n+ "Programming Language :: Python :: 3.6",\n+ "Programming Language :: Python :: 3.7",\n ],\n keywords=\'Plone content rules events\',\n author=\'Plone Foundation\',\n' -Repository: plone.registry +Repository: plone.contentrules Branch: refs/heads/master -Date: 2018-09-21T09:41:01+02:00 +Date: 2018-09-21T09:55:38+02:00 Author: Philip Bauer (pbauer) -Commit: https://github.com/plone/plone.registry/commit/462ddd121d97a370d7e398538660615e246759a5 +Commit: https://github.com/plone/plone.contentrules/commit/0bd184dddafc06b12d120216dbdc49e44bf4edf7 -Merge pull request #19 from plone/zope4 +Merge pull request #8 from plone/python3 -fix test for changed object field in zope4 +fix tests in py3 Files changed: M CHANGES.rst -M plone/registry/registry.rst -M plone/registry/tests.py +M plone/contentrules/README.rst +M plone/contentrules/tests.py +M plone/contentrules/zcml.rst M setup.py -b'diff --git a/CHANGES.rst b/CHANGES.rst\nindex 421fb1a..b6027eb 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -14,7 +14,8 @@ New features:\n \n Bug fixes:\n \n-- *add item here*\n+- Adapt test to changed object field in zope4\n+ [pbauer]\n \n \n 1.1.3 (2018-06-22)\ndiff --git a/plone/registry/registry.rst b/plone/registry/registry.rst\nindex d6a1f39..5952d69 100644\n--- a/plone/registry/registry.rst\n+++ b/plone/registry/registry.rst\n@@ -142,7 +142,7 @@ Again, values are validated::\n Traceback (most recent call last):\n ...\n WrongType: (\'Joomla\', ...)\n- \n+\n >>> registry[\'plone.registry.tests.cms\'] = b\'Joomla\' # doctest: +SKIP_PYTHON_2\n Traceback (most recent call last):\n ...\n@@ -215,7 +215,7 @@ If we have a field for which there is no ``IPersistentField`` adapter, we will g\n \n >>> from plone.registry.tests import IMailPreferences\n >>> IMailPreferences[\'settings\']\n- \n+ \n \n >>> registry.registerInterface(IMailPreferences)\n Traceback (most recent call last):\ndiff --git a/plone/registry/tests.py b/plone/registry/tests.py\nindex 626e726..d89ab62 100644\n--- a/plone/registry/tests.py\n+++ b/plone/registry/tests.py\n@@ -21,6 +21,11 @@\n \n class PolyglotOutputChecker(doctest.OutputChecker):\n def check_output(self, want, got, optionflags):\n+ # fix changed objectfield class in zope4\n+ got = re.sub(\n+ \'zope.schema._field.Object\',\n+ \'zope.schema._bootstrapfields.Object\', got)\n+\n if optionflags & SKIP_PYTHON_3 and sys.version_info >= (3,):\n return True\n elif optionflags & SKIP_PYTHON_2:\ndiff --git a/setup.py b/setup.py\nindex 24ec8da..ae05a88 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -32,11 +32,14 @@ def read(*rnames):\n classifiers=[\n "Framework :: Plone",\n "Framework :: Plone :: 5.1",\n+ "Framework :: Plone :: 5.2",\n "License :: OSI Approved :: GNU General Public License (GPL)",\n "Programming Language :: Python :: 2",\n "Programming Language :: Python :: 2.7",\n "Programming Language :: Python :: 3",\n "Programming Language :: Python :: 3.5",\n+ "Programming Language :: Python :: 3.6",\n+ "Programming Language :: Python :: 3.7",\n "Topic :: Software Development :: Libraries :: Python Modules",\n ],\n keywords=\'configuration registry\',\n' +b'diff --git a/CHANGES.rst b/CHANGES.rst\nindex 90444a6..c88f3d7 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -14,7 +14,8 @@ New features:\n \n Bug fixes:\n \n-- *add item here*\n+- Fix tests in py3.\n+ [pbauer]\n \n \n 2.0.7 (2018-01-30)\ndiff --git a/plone/contentrules/README.rst b/plone/contentrules/README.rst\nindex 291a98c..d2db5f0 100644\n--- a/plone/contentrules/README.rst\n+++ b/plone/contentrules/README.rst\n@@ -30,8 +30,8 @@ First, we create some rule elements.\n \n Lets start with some basic imports:\n \n- >>> from zope.interface import Interface, implements\n- >>> from zope.component import adapts\n+ >>> from zope.interface import Interface, implementer\n+ >>> from zope.component import adapter\n >>> from zope.component import getUtility, getAllUtilitiesRegisteredFor\n >>> from zope import schema\n \n@@ -56,8 +56,8 @@ and summary properties come from IRuleElementData and are used by the\n user interface to discover the edit view and present a title and summery\n to the user:\n \n- >>> class MoveToFolderAction(Persistent):\n- ... implements(IMoveToFolderAction, IRuleElementData)\n+ >>> @implementer(IMoveToFolderAction, IRuleElementData)\n+ ... class MoveToFolderAction(Persistent):\n ... targetFolder = \'\'\n ... element = "test.moveToFolder"\n ... @property\n@@ -71,15 +71,15 @@ adaptable to IExecutable. This should be a multi-adapter from\n >>> from plone.contentrules.rule.interfaces import IExecutable\n >>> from zope.component.interfaces import IObjectEvent\n \n- >>> class MoveToFolderExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IMoveToFolderAction, IObjectEvent)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IMoveToFolderAction, IObjectEvent)\n+ ... class MoveToFolderExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n ... self.event = event\n ... def __call__(self):\n- ... print "Tried to execute MoveToFolderExecutor, but not implemented"\n+ ... print("Tried to execute MoveToFolderExecutor, but not implemented")\n ... return True\n \n >>> provideAdapter(MoveToFolderExecutor)\n@@ -127,8 +127,8 @@ Again, we have to define an interface for the logger action:\n \n A factory class holding configuration data:\n \n- >>> class LoggerAction(Persistent):\n- ... implements(ILoggerAction, IRuleElementData)\n+ >>> @implementer(ILoggerAction, IRuleElementData)\n+ ... class LoggerAction(Persistent):\n ... loggingLevel = \'\'\n ... targetLogger = \'\'\n ... message = \'\'\n@@ -138,9 +138,9 @@ A factory class holding configuration data:\n As well as the executor that does the actual logging, capable of being adapted\n to IExecutable. In this case, it will adapt any context and any event.\n \n- >>> class LoggerActionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, ILoggerAction, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, ILoggerAction, Interface)\n+ ... class LoggerActionExecutor(object):\n ...\n ... def __init__(self, context, element, event):\n ... self.context = context\n@@ -182,17 +182,17 @@ a given interface.\n >>> class IInterfaceCondition(Interface):\n ... iface = Attribute(u\'the interface\')\n \n- >>> class InterfaceCondition(object):\n- ... implements (IInterfaceCondition, IRuleElementData)\n+ >>> @implementer (IInterfaceCondition, IRuleElementData)\n+ ... class InterfaceCondition(object):\n ... iface = None\n ... element = "test.interface"\n ... @property\n ... def summary(self):\n ... return "Check for interface " + self.iface.__identifier__\n \n- >>> class InterfaceConditionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IInterfaceCondition, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IInterfaceCondition, Interface)\n+ ... class InterfaceConditionExecutor(object):\n ...\n ... def __init__(self, context, element, event):\n ... self.context = context\n@@ -226,14 +226,14 @@ present:\n >>> class IHaltExecutionAction(Interface):\n ... pass\n \n- >>> class HaltExecutionAction(Persistent):\n- ... implements (IHaltExecutionAction, IRuleElementData)\n+ >>> @implementer (IHaltExecutionAction, IRuleElementData)\n+ ... class HaltExecutionAction(Persistent):\n ... element = "test.halt"\n ... summary = "Halt!"\n \n- >>> class HaltExecutionExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IHaltExecutionAction, Interface)\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IHaltExecutionAction, Interface)\n+ ... class HaltExecutionExecutor(object):\n ... # Above: the second "Interface" causes this\n ... # element to be available for every event\n ... def __init__(self, context, element, event):\n@@ -241,7 +241,7 @@ present:\n ... self.element = element\n ... self.event = event\n ... def __call__(self):\n- ... print "Rule Execution aborted at HaltAction"\n+ ... print("Rule Execution aborted at HaltAction")\n ... return False # False = Stop Execution! This is the payload.\n \n >>> provideAdapter(HaltExecutionExecutor)\n@@ -273,8 +273,9 @@ itself implies IAttributeAnnotatable.\n >>> from plone.contentrules.engine.interfaces import IRuleAssignable\n >>> class IMyContent(IRuleAssignable):\n ... pass\n- >>> class MyContent(object):\n- ... implements(IMyContent)\n+ >>> @implementer(IMyContent)\n+ ... class MyContent(object):\n+ ... pass\n \n >>> context = MyContent()\n \n@@ -459,8 +460,9 @@ higher up. Rules that are assigned not to bubble will not be executed.\n \n Now consider what would happen if the interface condition failed:\n \n- >>> class OtherContent(object):\n- ... implements(IRuleAssignable)\n+ >>> @implementer(IRuleAssignable)\n+ ... class OtherContent(object):\n+ ... pass\n >>> otherContext = OtherContent()\n \n >>> otherManager = IRuleAssignmentManager(otherContext)\n@@ -522,11 +524,12 @@ An element for IObjectCreatedEvent:\n \n >>> class IObjectCreatedSpecificAction(Interface):\n ... pass\n- >>> class ObjectCreatedSpecificAction(Persistent):\n- ... implements (IObjectCreatedSpecificAction)\n- >>> class ObjectCreatedExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IObjectCreatedSpecificAction, IObjectCreatedEvent) #!\n+ >>> @implementer (IObjectCreatedSpecificAction)\n+ ... class ObjectCreatedSpecificAction(Persistent):\n+ ... pass\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IObjectCreatedSpecificAction, IObjectCreatedEvent) #!\n+ ... class ObjectCreatedExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n@@ -552,11 +555,12 @@ An element for IObjectCopiedEvent:\n \n >>> class IObjectCopiedSpecificAction(Interface):\n ... pass\n- >>> class ObjectCopiedSpecificAction(Persistent):\n- ... implements (IObjectCopiedSpecificAction)\n- >>> class ObjectCopiedExecutor(object):\n- ... implements(IExecutable)\n- ... adapts(Interface, IObjectCopiedSpecificAction, IObjectCopiedEvent) #!\n+ >>> @implementer (IObjectCopiedSpecificAction)\n+ ... class ObjectCopiedSpecificAction(Persistent):\n+ ... pass\n+ >>> @implementer(IExecutable)\n+ ... @adapter(Interface, IObjectCopiedSpecificAction, IObjectCopiedEvent) #!\n+ ... class ObjectCopiedExecutor(object):\n ... def __init__(self, context, element, event):\n ... self.context = context\n ... self.element = element\n@@ -579,15 +583,15 @@ An element for IObjectCopiedEvent:\n \n All elements so far, applicable for object events:\n \n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\']\n \n For a more specific event, we may get more elements (i.e. those that also\n apply to more general events):\n \n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectCopiedEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectCopiedEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\', \'Object Created specific action\', \'Object Copied Specific Action\']\n- >>> map(lambda x: x.title, utils.allAvailableActions(IObjectCreatedEvent))\n+ >>> list(map(lambda x: x.title, utils.allAvailableActions(IObjectCreatedEvent)))\n [\'Move To Folder\', \'Log Event\', \'Halt Rule Execution\', \'Object Created specific action\']\n \n Filtering for specific events:\ndiff --git a/plone/contentrules/tests.py b/plone/contentrules/tests.py\nindex 599f04f..b31d21b 100644\n--- a/plone/contentrules/tests.py\n+++ b/plone/contentrules/tests.py\n@@ -1,4 +1,6 @@\n import doctest\n+import re\n+import six\n import unittest\n \n from zope.component.testing import PlacelessSetup as CAPlacelessSetup\n@@ -8,6 +10,14 @@\n optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS\n \n \n+class Py23DocChecker(doctest.OutputChecker):\n+\n+ def check_output(self, want, got, optionflags):\n+ if not six.PY2:\n+ want = re.sub("u\'(.*?)\'", "\'\\\\1\'", want)\n+ return doctest.OutputChecker.check_output(self, want, got, optionflags)\n+\n+\n class PlacelessSetup(CAPlacelessSetup, ContainerPlacelessSetup):\n \n def setUp(self, doctesttest=None):\n@@ -36,10 +46,14 @@ def test_suite():\n \'README.rst\',\n setUp=configurationSetUp,\n tearDown=configurationTearDown,\n- optionflags=optionflags),\n+ optionflags=optionflags,\n+ checker=Py23DocChecker(),\n+ ),\n doctest.DocFileSuite(\n \'zcml.rst\',\n setUp=configurationSetUp,\n tearDown=configurationTearDown,\n- optionflags=optionflags),\n+ optionflags=optionflags,\n+ checker=Py23DocChecker(),\n+ ),\n ))\ndiff --git a/plone/contentrules/zcml.rst b/plone/contentrules/zcml.rst\nindex b42d936..2f47597 100644\n--- a/plone/contentrules/zcml.rst\n+++ b/plone/contentrules/zcml.rst\n@@ -46,7 +46,8 @@ Here is how we would register these in ZCML:\n ... """\n \n >>> from zope.configuration.xmlconfig import xmlconfig\n- >>> from StringIO import StringIO\n+ >>> import six\n+ >>> from six import StringIO\n \n First, we need to make sure the ZCML directives are defined:\n \ndiff --git a/setup.py b/setup.py\nindex 51775a2..cff8dfb 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -14,12 +14,15 @@\n "Framework :: Plone :: 4.3",\n "Framework :: Plone :: 5.0",\n "Framework :: Plone :: 5.1",\n+ "Framework :: Plone :: 5.2",\n "Framework :: Zope2",\n "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",\n "Operating System :: OS Independent",\n "Programming Language :: Python",\n "Programming Language :: Python :: 2.6",\n "Programming Language :: Python :: 2.7",\n+ "Programming Language :: Python :: 3.6",\n+ "Programming Language :: Python :: 3.7",\n ],\n keywords=\'Plone content rules events\',\n author=\'Plone Foundation\',\n'