Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PLIP: Ship Plone with plone.restapi #2177

Closed
tisto opened this issue Oct 17, 2017 · 41 comments
Closed

PLIP: Ship Plone with plone.restapi #2177

tisto opened this issue Oct 17, 2017 · 41 comments

Comments

@tisto
Copy link
Member

tisto commented Oct 17, 2017

PLIP: Ship Plone with plone.restapi

Responsible Persons

Proposer: Timo Stollenwerk tisto@plone.org

Seconder: @hvelarde

Abstract

plone.restapi is a RESTful hypermedia API for Plone. Companies such as 4teamwork, code syntax, kitconcept, VNC and others use plone.restapi in production since many years already. Adding it to Plone core will allow us to further enhance our headless CMS efforts.

Motivation

Allow developers and integrators to build modern JavaScript front-ends on top of Plone.

Assumptions

The Plone community wants to go ahead with adopting modern JavaScript solutions.

Proposal & Implementation

plone.restapi 1.0 will be released this year and is considered to be a stable and mature piece of software.

Deliverables

  • plone.rest 1.0
  • plone.restapi 1.0

Risks

There is a small risk that plone.rest could break existing functionality of browser views that use application/json as HTTP accept header. We encountered a single issue since plone.restapi has been created. We do not really expect any problems, but they might occur.

Participants

  • Timo
@jensens jensens added this to the Plone 5.2 milestone Nov 1, 2017
@jensens jensens mentioned this issue Nov 14, 2017
@ebrehault
Copy link
Member

Review by Eric Bréhault (ebrehault@gmail.com, barbichu@irc.freenode.net)

The PLIP was reviewed on Ubuntu 16.04 using python 2.7.8 (https://github.com/collective/buildout.python) and Chromium 49.0.2623.108.

December 10, 2017

Review steps

  • Set up the buildout using the plone.restapi config:
  $ ./bin/buildout -c plone-5.1.x.cfg

and for plone.test:

  $ ./bin/buildout -c plone-5.0.x.cfg
  • Ran tests::
  $ bin/test
  • Reviewed code

  • Manual testing

Notes and observations

Automated testing

  • Tests for plone.restapi:

    All tests pass. The amount of tests and their quality are very satisfying.

  • Tests for Products.CMFPlone:

    Not applicable

  • Missing tests:

    I could not identify any missing test.

  • Test coverage:

    Excellent.

    Note: the coveralls.io badge for plone.rest seems out-of-date.

Manual testing

Tested directly with Postman, and tested from various Javascript frontend apps.

Tested in anonymous mode.

Tested in authenticated mode.

Code review

Implementation is clean.

plone.api is only used in tests.

Archetypes support is provided through zcml:condition.
It might be easily removed in a future version of Plone.

Very good documentation.

Conclusion

plone.rest and plone.restapi are ready to be shipped with Plone.

We need a pull request on Products.CMFPlone to add plone.restapi as a dependency.

@tisto
Copy link
Member Author

tisto commented Jan 19, 2018

plone.restapi 1.0.0 released: https://pypi.python.org/pypi/plone.restapi/1.0.0
plone.rest 1.0.0 released: https://pypi.python.org/pypi/plone.rest/1.0.0

@tisto
Copy link
Member Author

tisto commented Jan 19, 2018

Pull Request: #2263

@tisto
Copy link
Member Author

tisto commented Jan 23, 2018

It seems Plone 5.2 removed Products.Five.metaclass:

http://jenkins.plone.org/job/pull-request-5.2/160/console

$ bin/test -s plone.restapi
Running plone.restapi.testing.PloneRestApiATLayer:Functional tests:
  Set up plone.testing.zca.LayerCleanup in 0.001 seconds.
  Set up plone.testing.z2.Startup in 0.158 seconds.
  Set up plone.app.testing.layers.PloneFixture in 15.287 seconds.
  Set up plone.restapi.testing.PloneRestApiATLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 160, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.restapi.testing.PloneRestApiATLayer:Integration tests:
  Set up plone.restapi.testing.PloneRestApiATLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 160, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.restapi.testing.PloneRestApiDXLayer:Functional tests:
  Set up plone.app.event.testing.PAEventLayer in 1.105 seconds.
  Set up plone.app.contenttypes.testing.PloneAppContenttypes in 1.043 seconds.
  Set up plone.restapi.testing.PloneRestApiDXLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 66, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.restapi.testing.PloneRestApiDXLayer:Integration tests:
  Set up plone.restapi.testing.PloneRestApiDXLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 66, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.restapi.testing.PloneRestApiDXPAMLayer:Functional tests:
  Set up plone.restapi.testing.PloneRestApiDXPAMLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 109, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.restapi.testing.PloneRestApiDXPAMLayer:Integration tests:
  Set up plone.restapi.testing.PloneRestApiDXPAMLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/testing.py", line 109, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 53, in includeDependenciesDirective
    includeZCMLGroup(_context, info, 'meta.zcml')
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/z3c.autoinclude-0.3.7-py2.7.egg/z3c/autoinclude/zcml.py", line 30, in includeZCMLGroup
    include(_context, filename, includable_package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.restapi-1.0.0-py2.7.egg/plone/restapi/configure.zcml", line 8.2-8.37
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running zope.testrunner.layer.UnitTests tests:
  Tear down plone.app.contenttypes.testing.PloneAppContenttypes in 0.515 seconds.
  Tear down plone.app.event.testing.PAEventLayer in 0.005 seconds.
  Tear down plone.app.testing.layers.PloneFixture in 0.136 seconds.
  Tear down plone.testing.z2.Startup in 0.018 seconds.
  Tear down plone.testing.zca.LayerCleanup in 0.001 seconds.
  Set up zope.testrunner.layer.UnitTests in 0.000 seconds.
  Running:

  Ran 58 tests with 0 failures, 0 errors, 0 skipped in 0.025 seconds.
Tearing down left over layers:
  Tear down zope.testrunner.layer.UnitTests in 0.000 seconds.
Total: 58 tests, 0 failures, 6 errors, 0 skipped in 21.201 seconds.
Timos-MBP:buildout.coredev-5.2 timo$ bin/test -s plone.rest
Running plone.rest.testing.PloneRestLayer:Functional tests:
  Set up plone.testing.zca.LayerCleanup in 0.000 seconds.
  Set up plone.testing.z2.Startup in 0.160 seconds.
  Set up plone.app.testing.layers.PloneFixture in 12.720 seconds.
  Set up plone.app.event.testing.PAEventLayer in 0.677 seconds.
  Set up plone.app.contenttypes.testing.PloneAppContenttypes in 0.690 seconds.
  Set up plone.rest.testing.PloneRestLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/testing.py", line 21, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/configure.zcml", line 7.2-7.29
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running plone.rest.testing.PloneRestLayer:Integration tests:
  Set up plone.rest.testing.PloneRestLayer Traceback (most recent call last):
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 400, in run_layer
    setup_layer(options, layer, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 708, in setup_layer
    setup_layer(options, base, setup_layers)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.testrunner-4.4.4-py2.7.egg/zope/testrunner/runner.py", line 713, in setup_layer
    layer.setUp()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/src/plone.app.testing/plone/app/testing/helpers.py", line 371, in setUp
    self.setUpZope(portal.getPhysicalRoot(), configurationContext)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/testing.py", line 21, in setUpZope
    context=configurationContext
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 498, in file
    include(context, name, package)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 453, in finish
    actions = self.handler(context, **args)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 402, in include
    processxmlfile(f, context)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 295, in processxmlfile
    parser.parse(src)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 281, in endElementNS
    None, sys.exc_info()[2])
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/xmlconfig.py", line 272, in endElementNS
    self.context.end()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 345, in end
    self.stack.pop().finish()
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 452, in finish
    args = toargs(context, *self.argdata)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 794, in toargs
    args[str(name)] = field.fromUnicode(s)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/fields.py", line 73, in fromUnicode
    value = self.context.resolve(name)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/zope.configuration-4.1.0-py2.7.egg/zope/configuration/config.py", line 151, in resolve
    mod = __import__(mname, *_import_chickens)
  File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/zcml.py", line 6, in <module>
    from Products.Five.metaclass import makeClass
ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/configure.zcml", line 7.2-7.29
    ZopeXMLConfigurationError: File "/Users/timo/workspace/plone/buildout.coredev-5.2/eggs/plone.rest-1.0.0-py2.7.egg/plone/rest/meta.zcml", line 5.4-9.10
    ImportError: No module named metaclass

Running zope.testrunner.layer.UnitTests tests:
  Tear down plone.app.contenttypes.testing.PloneAppContenttypes in 0.377 seconds.
  Tear down plone.app.event.testing.PAEventLayer in 0.005 seconds.
  Tear down plone.app.testing.layers.PloneFixture in 0.078 seconds.
  Tear down plone.testing.z2.Startup in 0.007 seconds.
  Tear down plone.testing.zca.LayerCleanup in 0.002 seconds.
  Set up zope.testrunner.layer.UnitTests in 0.000 seconds.
  Running:

  Ran 14 tests with 0 failures, 0 errors, 0 skipped in 0.002 seconds.
Tearing down left over layers:
  Tear down zope.testrunner.layer.UnitTests in 0.000 seconds.
Total: 14 tests, 0 failures, 2 errors, 0 skipped in 15.086 seconds.

It there an upgrade guide for Plone 5.2 with a list of removed imports?

@tisto
Copy link
Member Author

tisto commented Jan 23, 2018

@jensens five.pt has been removed in Plone 5.2? Is there an upgrade guide for Plone 5.2?

@tisto
Copy link
Member Author

tisto commented Jan 23, 2018

Ok. Found the change:

zopefoundation/Zope@b4d37a3

@tisto
Copy link
Member Author

tisto commented Jan 23, 2018

I fixed the import failure with plone/plone.rest#71. Though, there are quite a few regressions in both plone.rest and plone.restapi. @plone/framework-team Is there any upgrade guide for Zope 4 / Plone 5.2?

@pbauer
Copy link
Member

pbauer commented Jan 24, 2018

@tisto there is a ticket #1940 but there is no finished upgrade-guide yet. Additions are very welcome.

@tisto
Copy link
Member Author

tisto commented Jan 24, 2018

@pbauer thanks for the link! Though, this is still pretty far away from an integrators-ready upgrade guide. :)

@plone/framework-team When I wrote the PLIP, I really expected this to be a 30 minutes job. If this becomes more a days/week-like challenge, because of the Zope 4 changes, I might have to drop the PLIP.

I really want to focus on pushing things on the front end (plone-react, pastanaga, etc) and I think it makes more sense for the Plone community if I spend my time there.

If anybody wants to help out or take over this PLIP, I am happy to do what I can to help...

@jensens
Copy link
Member

jensens commented Jan 24, 2018

@tisto whats the stopper exactly? May we help you getting this done, like adding it as a sprint topic?

@tisto
Copy link
Member Author

tisto commented Jan 24, 2018

@jensens I will set up a PLIP jenkins job to show the current test failures. It seems something in plone.rest is broken that leads to additional test failures in plone.restapi. I fixed the obvious import problems. Though, as said, I have no idea about the changes in Zope 4, so I am lost.

Would be great if someone at the sprint could look into this!

@gforcada
Copy link
Member

The jenkins job that @tisto created is here http://jenkins.plone.org/view/PLIPs/job/plip-plone-restapi/ for everyone's pleasure to run it 🙂

@tisto
Copy link
Member Author

tisto commented Jan 24, 2018

As a starting point, I would recommend looking into why plone.rest returns a 404 instead of a 401:

http://jenkins.plone.org/view/PLIPs/job/plip-plone-restapi/1/testReport/plone.rest.tests.test_dispatching/TestDispatchingDexterity/test_private_dx_folder_invalid_creds/

@davisagli
Copy link
Member

It doesn't surprise me if it takes some work to make plone.rest work on Zope 4, since plone.rest added adding a feature to the Zope publisher, and Zope 4 involves a pretty big change to use the WSGI publisher.

@thet
Copy link
Member

thet commented Jan 30, 2018

I've added some fixes:
#2282
plone/plone.testing#45

And updated the PLIP buildout:
plone/buildout.coredev@b371391

plone.rest goes green. i don't know about plone.restapi yet.

@djay
Copy link
Member

djay commented Jan 30, 2018

@thet there is a current bug plone.rest and the plone theme editor. Believe its still current. We are trying to add a test for it now to show the fix works.
plone/plone.rest#61

@djay
Copy link
Member

djay commented Feb 5, 2018

We've now added a test for the plone.rest issue that caused a regression in the theme editor in plone. plone/plone.rest#61. If someone could review it, we can get it merged

@jensens
Copy link
Member

jensens commented Feb 16, 2018

We had a look at the TUS implementation.
https://github.com/plone/plone.restapi/blob/master/src/plone/restapi/services/content/tus.py#L286

It seems it stores the uploads in tmp-dirs. How is it supposed to scale up over more than one machine?

@djay
Copy link
Member

djay commented Feb 18, 2018

@jensens there is a TUS implementation in plone 5 and this issue was got around by turning it off by default. Not ideal but I don't see any choice but to do the same with restapi.
It's a shame that ZEO doesn't have an api to allow for merging blobs or that tempoary storage doesn't support blobs, otherwise you could store the parts in the ZODB. I guess you could still store the parts unmerged in the ZODB if you have a namedfile object that had references to many blobs, but then that would break zope streaming and xsendfile.

@jensens
Copy link
Member

jensens commented Feb 19, 2018

I would store the parts as blobs in ZODB and do a manual merge after the next part arrived and is committed. Or do i miss something?

@jaroel
Copy link
Member

jaroel commented Feb 19, 2018

I think one could set up a nfs share and point TUS_TMP_FILE_DIR to it, alike sharing blobs between instances?

@tomgross
Copy link
Member

I'd love to see an analysis and roadmap of how current Plone core components (like folder contents and relation widget) will use the REST API together with this PLIP. This will make it easier for new developers to understand. Otherwise this is yat (Yet another thing 😉 ) in Plone.

@jensens
Copy link
Member

jensens commented Feb 20, 2018

I personally hope with restapi all those distributed wild endpoints will die as the next step. But not together with this PLIP, that would broaden the scope too much.

@tisto
Copy link
Member Author

tisto commented Feb 21, 2018

@tomgross +1, we need to write down some best practices for add-on developers how to implement a proper plone.rest-based and plone.restapi compliant API as well as start to move core components that do not need to be server-side rendered.

Though, I fully agree with @jensens that this is beyond the scope of this PLIP. The first step is to ship with plone.restapi. Then we can start with migrating. Not sure if we even need a PLIP, if both React and plone.restapi have been approved in the core.

Ideally, we can just make components from plone-react re-usable and integrate them into standard Plone.

Personally, I plan to fully focus on plone-react in the future. Though, the plone-react team is more than happy to assist if anybody is interested in using components outside of plone-react. :)

cc @robgietema

@jensens
Copy link
Member

jensens commented May 8, 2018

plone.rest need love, it does not work with Zope 4 WSGI publisher. @tisto can not contribute to that, this task needs help!

This is a blocker for the PLIP!

@tisto
Copy link
Member Author

tisto commented Jun 27, 2018

@plone/framework-team thanks to @davisagli and @sunew we are very close to a green build. Just one test isolation issue is left. Can we merge the PLIP right away on a green build? I see the risk that we have to spend way more work on this if we don't merge it as soon as it is green since the Plone 5.2 branch is under heavy development right now. :)

@frapell
Copy link
Member

frapell commented Jun 27, 2018

Huge +1 from me

@jensens
Copy link
Member

jensens commented Jun 27, 2018

Yes, it is approved and reviewed. Merge it after green build.

@tisto
Copy link
Member Author

tisto commented Jun 27, 2018

Thanks for the swift reply! We are very close. Just one flickering robot test prevents the merge. :)

@sunew
Copy link
Contributor

sunew commented Jun 27, 2018

@tisto The flickering test is apparently more or less normal:
Products.CMFPlone/Products/CMFPlone/tests/robot/test_querystring.robot

Scenario: Location query
[Tags] unstable
[Documentation] This sometimes fails with: Element locator 'jquery=:focus' did not match any elements.
... This sometimes fails with: Element locator 'jquery=.pattern-relateditems-tree-select' did not match any elements.

@sunew
Copy link
Contributor

sunew commented Jun 27, 2018

@ebrehault
Copy link
Member

@tisto @sunew @gforcada in such cases, re-playing failing tests and merging the results is a pretty good strategy, see http://laurent.bristiel.com/re-executing-failed-test-cases-and-merging-outputs-with-robot-framework/

@tisto
Copy link
Member Author

tisto commented Jun 27, 2018

Merged: plone/buildout.coredev#494

@tisto
Copy link
Member Author

tisto commented Jun 27, 2018

Plone 5.2 build is green: https://jenkins.plone.org/job/plone-5.2-python-2.7/1254/

@tisto tisto closed this as completed Jun 27, 2018
@tisto
Copy link
Member Author

tisto commented Jun 29, 2018

@gforcada it seems plone.rest and plone.restapi do not have the post-push hooks on Jenkins to trigger a 5.2 coredev build on changes. Could you please run our script to update that?

@gforcada
Copy link
Member

@tisto sure

@gforcada
Copy link
Member

@tisto ... and done! 🎉 let me know if it does not work

@tisto
Copy link
Member Author

tisto commented Jun 29, 2018

Thank you Gil!

@jensens jensens reopened this Nov 2, 2018
@jensens
Copy link
Member

jensens commented Nov 2, 2018

I am pretty sure the Plip is not finished integration wise.

  • coredev testing tests it
  • theres no dependency anywhere yet, so w/o adding it to your eggs section restapi would not get pulled in (or do I miss somehting?).
  • Ship with plone.restapi. #2265 want to pull it in Products.CMFPlone. As stated there, this would (one time more) give us an up-side-down dependency chain. This, b/c ploe.restapi depends on Products.CMFPlone itself and sits ontop of it and not vice versa.

@jensens
Copy link
Member

jensens commented Feb 4, 2019

This was resolved by closing #2265 and merging plone/Plone#13
thanks for the good work!

@jensens jensens closed this as completed Feb 4, 2019
gforcada referenced this issue in plone/plone.restapi Feb 9, 2019
@stevepiercy stevepiercy moved this to Merged in PLIPs (core) Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests