Zope 2 as WSGI application

I held a tutorial on PyCon DE in Leipzig how to run Zope 2 as a WSGI application. I post it here, too, so you can try the tutorial yourself.

ZopeEditManager for Mac OS X Lion

The current binary distribution of ZopeEditManager no longer works on Mac OS X Lion (10.7): It is PowerPC code which is no longer supported. (The package included in MacPorts, does not build either.)

To built ZopeEditManager from the sources I did the following:

  1. Installed the needed libraries using MacPorts:
    sudo port install py27-py2app py27-pyobjc py27-pyobjc-cocoa
  2. Downloaded ZopeEditManager sources.
  3. Extracted them and patched them using the Lion-Patch:
    patch -p1 < ZopeEditManager-Lion.patch
  4. Compiled the application:
    /opt/local/bin/python2.7 setup.py py2app
  5. Ran ZopeEditManager from the dist-directory:
    Bildschirmfoto_2011-08-16_um_09

I built a ZopeEditManger-0.9.8.dmg containing the result, so you do not have to do it yourself :)

LaTeX: Same footnote number for more than one footnote

To reuse the current footnote number in LaTeX use:

\footnotemark[\value{footnote}]

Example:

Lorem ipsum\footnote{See somewere.} dolor sit amet, consectetur, adipisci 
velit\footnotemark[\value{footnote}]

Result:

Bildschirmfoto_2010-09-04_um_12

Tagged LaTeX footnote

Test an in-out-widget of z3c.form using zope.testbrowser

z3c.form uses a widget for sorted collections (like lists) which is implemented using JavaScript. (See the example image.) But this widget is testable using zope.testbrowser, too.

Bildschirmfoto-2010-07-26-um-20

Example in-out-widget of a list

As the widget in the image is named "columns" (title and name of the schema element are the same in the examle), the left select control is named form.widgets.columns.from. The right select control is named form.widgets.columns.to. On submit the values of the items in the right control get stored in a hidden fields named form.widgets.columns:list. To select the "person -- birthdate" control additionally to the ones in the right select control, you can use the following code:

>>> def in_out_select(form, name, control_name):
...     form.mech_form.new_control(
...         type='hidden',
...         name=name,
...         attrs=dict(value=browser.getControl(control_name).optionValue))
>>> form = browser.getForm()
>>> in_out_select(form, 'form.widgets.columns:list', 'person -- birth date'))
>>> in_out_select(form, 'form.widgets.columns:list', 'person -- first name'))
>>> in_out_select(form, 'form.widgets.columns:list', 'person -- last name'))

Get package version

To get the version of a currently installed package you can use:

import pkg_resources
pkg_resources.get_distribution('my.package').version

Getting rid of zope.app.testing

The zope.app.package has many dependencies but there are "lighter" packages which do the same.

zope.app.wsgi

To get rid of a zope.app.testing test dependency by using zope.app.wsgi (version 3.7 or newer) two things are necessary:

1) Use zope.app.wsgi.testlayer to set up the test layer

Instead of:

import os
from zope.app.testing.functional import ZCMLLayer

MyLayer = ZCMLLayer(
    os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
    __name__, 'MyLayer', allow_teardown=True)

do:

import zope.app.wsgi.testlayer
import my.package

MyLayer = zope.app.wsgi.testlayer.BrowserLayer(my.package)

2) Use zope.app.wsgi.testlayer.Browser for browser tests

In browser tests use instead of:

>>> from zope.testbrowser.testing import Browser

the following one:

>>> from zope.app.wsgi.testlayer import Browser

unittest resp. doctest

1) Use unittest.TestCase instead of zope.app.testing.functional.FunctionalTestCase

No longer subclass zope.app.testing.functional.FunctionalTestCase but use:

class FunctionalTestCase(unittest.TestCase):
    """Base class for functional tests."""

    layer = MyLayer

MyLayer is the one defined in zope.app.wsgi 1).

Inside such functional unittests use

self.layer.getRootFolder()

instead of:

self.getRootFolder()

2) Use doctest.DocFileSuite instead of zope.app.testing.functional.FunctionalDocFileSuite

You need an extra step in test_suite() function now:

def test_suits():
    suite = doctest.DocFileSuite('my_doctest.txt')
    suite.layer = MyLayer

MyLayer is the one defined in zope.app.wsgi 1).

3) getRootFolder is no longer per default in the globals

When you need getRootFolder in a doctest you have to put layer.getRootFolder into globs keyword argument of the DocFileSuite of the test.

zope.testing

To get rid of:

import zope.app.testing.setup

def setUp(test):
    zope.app.testing.setup.setUpTestAsModule(test, 'my.package.README')

def tearDown(test):
    zope.app.testing.setup.tearDownTestAsModule(test)

use:

import zope.testing.module

def setUp(test):
    zope.testing.module.setUp(test, 'my.package.README')

def tearDown(test):
    zope.testing.module.tearDown(test, 'my.package.README')

zope.component

To get rid of:

from zope.app.testing.placelesssetup import PlacelessSetup
from zope.app.testing.placelesssetup import setUp, tearDown

it is enough to use (at least in the majority of cases):

from zope.component.testing import PlacelessSetup
from zope.component.testing import setUp, tearDown

 

"unevolve" zope.app.generations

To set the zope.app.generations counter to a previous value generate an evolve step like this:

import transaction

MY_KEY = u'uc/dgb.internet'
BACK_TO = 38

def evolve(context):
    context.connection.root()['zope.app.generations'][MY_KEY] = BACK_TO
    transaction.commit()
    transaction.doom()

MY_KEY is the key used for your application. (I'm currently not sure how it is computed.) BACK_TO is the target generation. This generation fails because of the transaction.doom() but it saved the value of the key before.