Lain and Sakura


”One byte to rule them all’ – dtmf – old school

Functional tests – Windmill impresionante!


Windmill is a web testing framework intended for complete automation of user interface testing, with strong test debugging capabilities.

The first goal of Windmill is to make test writing easier, portable and sustainable. Test writing and debugging is an interactive process and requires an architecture that allows for large amounts of flexibility and back-and-forth communication.

The second goal of Windmill is to keep the project open and transparent, encouraging as much outside contribution as possible. This goal is shared by every project started at OSAF and we have maintained a reputation of accomplishing transparency in Windmill.

Extraido de :

Es un framework que nos permite realizar tests funcionales al estilo selenium de una forma muy sencilla. Tiene algunas particularidades a tener en cuenta, y aun no lo he visto con detalle, pero al menos quiero documentarlo aqui para no olvidarlo y para que quede a disposicion de quienes les pueda ser de utilidad.

Nota: Estoy trabajando sobre Ubuntu Intrepid a 64bits (aclaro por temas de paths, etc …).

Mi meta es utilizar WindmillBook para testear los proyectos en los que trabajo con plone y zope, utilizo buildouts asi que en mi buildout.cfg agrego lo siguiente :

parts =

En donde las secciones windmill, python y pythonbin son :

eggs =
simplejson <=2.0.0dev

recipe = zc.recipe.cmmi
url =
executable = ${buildout:directory}/parts/python/bin/python2.5

recipe = plone.recipe.command
command = ln -s ${python:executable} ${buildout:bin-directory}/python

Corremos nuevamente nuestro buildout:

x-ip@x-ip-lain:~/workspace/gibeta$ ./bin/buildout -vN

Y estamos listos! Ahora detalle, como trabajo con zope y preciso python2.4, y windmill precisa una version particular de python2.5 (el por ello que utilizamos un recipe en nuestro buildout para tener un python2.5) lo ejecutamos de esta forma :

x-ip@x-ip-lain:~/workspace/gibeta$ ./bin/python
Python 2.5.2 (r252:60911, Nov 20 2008, 16:48:26)
[GCC 4.3.2] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
x-ip@x-ip-lain:~/workspace/gibeta$ ./bin/python ./bin/windmill
windmill web test automation system.
windmill [-cdelptmxs] action [option=value] [firefox|ie|safari] []

Available Actions:
shell         Enter the windmilll shell environment (modified python shell).
Uses ipython if installed. Exit using ^d
run_service   Run the windmill service in foreground. Kill using ^c.

Available Options:
browserdebug :: Enable browser debugging.
Python tests will all load in to the server at once. Defaults to False
-c, continueonfailure :: Keep the browser running tests after failure. Defaults to False
-d, debug :: Turn on debugging.
-e, exit :: Exit after all tests have run. Defaults to False
extensions= :: The directory containing any windmill javascript extensions.
firebug :: Install Full Firebug. Firefox only!
jsdir= :: JavaScript Test Framework :
Root directory of JavaScript tests.
jsfilter= :: JavaScript Test Framework :
Filter tests, example; ns:test_login,tests:test_user.
jsphase= :: JavaScript Test Framework :
Specify the phases the framework should run example; setup,test,teardown
-l, loadtest= :: Run the given test file/dir
loglevel= :: Log level command, sets the global logging level.
nocompress :: Do not compress windmill javascript files. Defaults to True
-p, pdb :: Enable pdb debugging when running python tests. Defaults to False
port= :: Set port for windmill to run. Default is 4444.
-t, test= :: Run the given test file/dir
-m, firefox :: Start the firefox browser. Defaults to False
-x, ie :: Start the internet explorer browser. Windows Only. Defaults to False
-s, safari :: Start the Safari browser. Mac Only. Defaults to False
usecode :: Use the code module rather than ipython. Defaults to False

Si no lo hacemos, y se ejecuta con un python2.4 nos va a dar este error …

x-ip@x-ip-lain:~/workspace/gibeta$ ./bin/windmill shell is not a windmill argument. Sticking in functest registry.
Traceback (most recent call last):
File “./bin/windmill”, line 19, in ?
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/bin/”, line 52, in main
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/bin/”, line 329, in command_line_startup
shell_objects = setup()
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/bin/”, line 134, in setup
httpd, httpd_thread, console_log_handler = run_threaded(windmill.settings[‘CONSOLE_LOG_LEVEL’])
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/bin/”, line 95, in run_threaded
httpd, console_handler = setup_servers(console_level)
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/bin/”, line 89, in setup_servers
httpd = windmill.server.wsgi.make_windmill_server()
File “/home/x-ip/Trabajo/Rcom/Instancias/gibeta/trunk/eggs/windmill-0.9.1-py2.4.egg/windmill/server/”, line 189, in make_windmill_server
windmill_xmlrpc_app = wsgi_xmlrpc.WSGIXMLRPCApplication(instance=xmlrpc_methods_instance)
File “/home/x-ip/.pyenv/lib/python2.4/site-packages/PIL/”, line 25, in __init__

TypeError: __init__() got an unexpected keyword argument ‘allow_none’

Estan avisados! 😉

Bueno, perfecto! lo tenemos trabajando. Algo con lo que me encontre fue que al seguir la documentacion para aprender este framework en: al seguir este ejemplo:

x-ip@x-ip-lain: ./bin/python ./bin/windmill firefox

Me daba un error por buscar en /usr/lib/firefox-$miversion/ esto ‘mozrunner-firefox‘ lo cual,
solucione con un enlace simbolico de esta forma:

x-ip@x-ip-lain:/usr/lib/firefox-3.0.4$ pwd

x-ip@x-ip-lain:/usr/lib/firefox-3.0.4$ sudo su
[sudo] password for x-ip:

root@x-ip-lain: ln -ls /usr/lib/firefox-3.0.4/firefox /usr/lib/firefox-3.0.4/mozrunner-firefox

Solucionado esto pude correr satisfactoriamente:

x-ip@x-ip-lain: ./bin/python ./bin/windmill firefox

Eso es todo, espero les halla sido de utilidad 🙂

Archivado en: Plone, Python

Test, tests, and more tests

Unit Testing

Obtenido de:

Author: Jean-Francois Roche

Test driven development has proved to be a really great practice for productive developers. And that’s why writing tests for every new functionality, for every change and bug fix, is now done by most of the Zope/Plone developer community. (So much so that if you submit code and want your code released, don’t imagine it will see the light of day without tests!).

Writing tests takes time, and you must take the time to write them. Once written that time will be payed back many times. Well planned tests will show you that your changes, bug fixes, and refactoring didn’t create other bugs in your code, and will prevent you losing time looking for them.

Testing is magic, it transforms a developer into a user. When you are writing your tests you place yourself in the user’s skin and begin to see how he might use your code. This can show you how nice your code is: where are the obscure parts, the hard to understand methods, the wrong class decompositions, etc.

You know that one of the biggest assets of Plone is its community. Tests are even more important in collective code for two main reasons:

  1. You want to share with others your functionalities, your way of coding, your way of thinking and show the right way how to use these functionalities.
  2. Many different people can/could write inside your code, if many tests are already written they can assure themselves that the code they write doesn’t break yours.
  3. It covers you when the maintainer, or release manager come to scream at you that you broke something with your last changes.

So all this is about verifying your code and explaining it to others.

Tests should:

  • Be repeatable
  • Run without human intervention needed
  • Be concise
  • Tell a story
  • Not test obvious things
  • Be deterministic


When you create tests for Plone, you don’t want to lose time installing a Plone portal, or other basic Zope/Plone products. So to get quicker test results, we use a unit test framework that create automated unit tests suites. Plone has its own: PloneTestCase. As Plone is based on Zope, PloneTestCase is a layer on the top of the ZopeTestCase (the zope unit test framework – which is based on Python’s unittest package [and if you want to know the whole story, python’s unittest package is based on Java’s JUnit and the Smalltalk testing framework]). This framework is a huge help for running your test quickly, often and with clear results.


By test we mean a test method.

By unittest we mean a class which contains all the test methods (if you want to use the Plone test framework, this class should inherit from PloneTestCase).

By unittest suite we mean a collection of unittest.

We will describe here a bunch of basic things available in PloneTestCase and that we use a lot in the next sections.

Products Installed:

Here is the list of default installed products. If you need additional products you will have to install them explicitly with the method we describe later.


  • ZCTextIndex
  • MailHost
  • PageTemplates
  • PythonScripts
  • ExternalMethod
  • GroupUserFolder
  • Five


  • CMFCore
  • CMFDefault
  • CMFCalendar
  • CMFTopic
  • DCWorkflow
  • CMFUid
  • CMFActionIcons
  • CMFQuickInstallerTool
  • CMFFormController

Plone – Archetypes

  • Archetypes
  • MimetypesRegistry
  • PortalTransfroms
  • ATContentTypes
  • ATReferenceBrowserWidget
  • CMFDynamicViewFTI
  • ExternalEditor
  • ExtendedPathIndex
  • ResourceRegistries
  • SecureMailHost
  • kupu

and, last but not least, CMFPlone.

In Plone 2.5 other important products are also installed: CMFPlacefulWorkflow, PlonePAS…

Objects installed:

Here are the objects you can use when you instantiate a PloneTestCase:

  • self.portal : a fresh Plone Portal install with all the portal tools you need inside.
  • self.folder : when running a PloneTestCase you are logged as a default user. This (empty) folder is the home folder of the default user. As it will be important for you to be able to do everything you need in this folder, default user is the owner of this folder.

Useful Methods you can use:

Here are the methods you can use on the self object (inside the PloneTestCase instance):

  • addProduct(name) : Uses the quickinstaller to install a products inside the Plone portal (self.portal). So if you defined your Product and your content type, don’t forget to install it in the Plone Portal before trying to invoke it.
  • setRoles(roles, name=default_user) : Change the current user’s roles (roles can be a string, a tuple or a list). Really important if you want to check security issues. You can also change the roles of other users by setting the name parameter.
  • setGroup(groups, name=default_user) : Change the current user’s groups (groups can be a string, a tuple or a list). You can also change the groups for other users by setting the name parameter.
  • setPermissions(permissions, role) : Change the permissions on the portal object for the role. Permissions can be a string, a tuple or a list. Role must be a string.
  • login(name) : It’s sometimes clearer to create new users with different roles or groups and after that login in as these users.
  • logout() : You want to be relegated to Anonymous inside the Plone instance? Use this method.

Unit Test Setup

The testing framework will run all the methods inside any class where the method name starts with test. So testMethod1(self) will be automatically run by the framework and you won’t have to bother anymore about explicitly calling it somewhere.

You might often want to repeat the same initialization before calling your test method. The framework gives you an powerful method for that:

  • afterSetUp(self) : You should put in this method all the code you want to do before running each of your test methods.

Unit Test Setup

Assertion Testing Methods (python unittest based)

With test there is a known input and an expected output. This input-output correctness is checked by assertion. Python unittest package give us a range of methods to test assertions:

  • failIf(expression) : Fail the test if the expression is true.
  • failUnless(expression) : Fail the test unless the expression is true.
  • failUnlessEqual(first, second) : Fail if the two objects are unequal as determined by the == operator.
  • failIfEqual(first, second) : Fail if the two objects are equal as determined by the == operator.
  • fail(msg) : Fail immediately, with the given message.
  • failUnlessRaises(excClass, callableObj, args, *kwargs) : Fail unless an exception of class excClass is thrown by callableObj when invoked with arguments args and keyword arguments kwargs.

Failure and Errors

Failures and Errors are two different things!

Failures occur when an assertion has failed (you were expecting the opposite result from the test assertion).

Errors occur when something you didn’t expect occurs (exceptions, errors in your code…).

Let’s create a test! You learn better with practice. You will see it’s easy.

Common practice is to create a test class for each class you want to test and one (and sometimes more) test method for each important method in your class (getter and setter are often left untested due to their obviousness).

First download the PloneTestCase ( or from svn and extract it in your favourite Zope Products folder (let’s assume that you installed Plone in there ;)).

Let’s take the PloneTestCase class with all the things we need inside:

    >>> from Products.PloneTestCase import PloneTestCase

Let’s say we want to test some of the Plone Document (ATDocument) behaviour. Let’s create a class which will use this great PloneTestCase we have just imported:

    class TestATDocument(PloneTestCase):
           A basic test case for Plone Document

Here it is we have done our first Plone test case. Not hard ? Yes i agree this doesn’t test much :). Let’s test two things:

1) when I edit the title of my document, I want it to be edited correctly (I agree that we are basically testing obvious thing here, let’s keep things simple).

2) when I add a document, I want to it to be inside the Plone Catalog.

As you see in these two tests we will need a basic document created, so let’s do it once in the afterSetUp method so that our document will be created before each test.

Remember that each testing method must begin with test. Let’s create

     class TestATDocument(PloneTestCase):
            A less basic test case for Plone Document
          def afterSetUp(self):
               Let's create in our home folder the document we need
             self.folder.invokeFactory('Document', id='doc')
             # We now have a document with id "doc" inside our home folder

          def testEditTitle(self):
              Let's see if a title change on the document goes well
             self.folder.doc.setTitle('A wonderful document title')
             self.assertEqual(self.folder.doc.Title, 'A wonderful document title')
             # this will fail if the setTitle didn't  correctly do its job!

          def testDocumentInCatalog(self):
               Let's see if the document is in the catalog
             # the catalog is in the Plone portal

And there it is. If this passes we can be sure that we can change the title of a document and that once created a document is in the plone catalog.

Now comes the time to include our fresh testcase inside a testsuite and to run our tests.

To be able to run this you will need two files :

  • : To be able to run test from python you will need to setup a few PATHs, this file will do most of the job for you.
  • : This small python code will just run all the files in the current directory which begin the the word test.

Copy these files from the PloneTestCase folder to the folder where all your test cases are (often the “tests” folder).

So, to run the test suite, we will need to decorate our PloneTestCase. To add a bit of difficulty I want also to install a product inside my portal which isn’t provided in the above list. Let’s say I want to use the Plone Language Tool (I agree, we won’t need it for executing our test):

       # First, above all, execute the

       import os, sys 
       if __name__ == '__main__':
          execfile(os.path.join(sys.path[0], ''))

       # Install the PloneLanguageTool Product in Zope

       from Testing import ZopeTestCase

       # Initialize our Plone and default install PloneLanguageTool in it

       from Products.PloneTestCase import PloneTestCase

       # Here it is, everything installed. We can put here our testcase...

       class TestATDocument(PloneTestCase.PloneTestCase):
               A less basic test case for Plone Document
             def afterSetUp(self):
                  Let's create in our home folder the document we need
                self.folder.invokeFactory('Document', id='doc')
                # We now have a document with id "doc" inside our home folder

             def testEditTitle(self):
                  Let's see if a title change on the document goes well
                self.folder.doc.setTitle('A wonderful document title')
                self.assertEqual(self.folder.doc.Title(), 'A wonderful document title')
                # this will fail if the setTitle didn't correctly do its job!

             def testDocumentInCatalog(self):
                  Let's see if the document is in the catalog
                # the catalog is in the Plone portal

       # Now we need our testcase inside a test suite.

       def test_suite():
            from unittest import TestSuite, makeSuite
            suite = TestSuite()
            return suite

       # and if you want to be able to run your suite directly (python

       if __name__ == '__main__':

Everything is set up now. Last thing to do is to say where your zope is in your system. On Unix based system you can do this like so :

      export SOFTWARE_HOME=/usr/lib/zope2.9/lib/python

Now you have two ways to run your test suite, either

  • “python” : Which will look in every file with the name beginning with “test” and run all defined test suites.
  • “python” : Which will run the specified test suite.

While running you will see

1) Installation of the Zope – Products

2) Once executed, a single test (method) will be represented by:

“.” : which means that your test ran correctly.

“F” : which means that your test failed (you will get more information at the end).

“E” : which means that your test has error (you will get more information at the end).

Each time a test fails or has an error you will get a traceback and more verbose information about the failure/error.


What do you see in the word “DocTest”? Doc and Test. So a doctest is documentation and, at the same time, a test that proves that your code is working.

Many argue that people should read unit tests and they should be clear enough so that no more verbose comments should be added. It’s correct that tests should be clear but I wouldn’t be that strict. I think the more people I can explain my code to, the more feedback I will get .

Although we consider test cases as developer documentation, doctest is considered as a middle technique between documentation and test case. No more stale and useless documentation! Doctest enables living documentation, always in step with the current implementation.

A doctest is a text, or structured text file (which should be written inside the docs folder of your package/products). So inside this file you will explain your code and at the same time you will be able to call python code. To call python code just do:


This represents a call to the python interpreter. Around it you can place your explanation. If your python code returns something, you have to do exactly the same as if you would call this code from a python interpreter session. For example:

    >>> print 'hello world'
    hello world

The return value must be written at the same indentation level as the >>>

One problem is that one doctest represents in itself more than one test. You want to show multiple things inside your doctest, but afterSetUp is only run once before execution of the whole doctest. One doctest represents one python session. So if i do:

    >>> a = 'hello'

My variable a will be set to hello until the end of the document. Never forget that, it could lead to some big problems!

By the way, all this document is a doctest for the ATContentTypes products. It can be executed there.


  • a doctest “file” will represent the txt file which include our doctest.
  • a doctest “class” will represent the unittest class that defines a doctest.

Once written, the doctest file should be linked to a testcase class and a testsuite. So let’s see how do we setup a doctest in the test part (this should go inside a python file in the tests folder – with a file name which begins with test):

      # Like before we use the

      import os, sys
      if __name__ == '__main__':
           execfile(os.path.join(sys.path[0], ''))

      # We install plone as usual. We want to test plone related stuff in
      # our doctest

      from Products.PloneTestCase import PloneTestCase

      # then we need the zope doctestsuite and link our doctest text file
      # with a functional test case

      from Testing.ZopeTestCase import FunctionalDocFileSuite
      from Products.PloneTestCase.PloneTestCase import FunctionalTestCase

      # we have a doctest file named archive.txt which is located in
      # ATContentTypes inside the docs folder (I say it again, the doctest file
      # should always be inside the docs folder, not in tests folder).

      def test_suite():
            import unittest
            suite = unittest.TestSuite()
      if __name__ == '__main__':

As usual you can run this file directly, or just run python

Now imagine that you want to prepare some things inside your testcase class before running your doctest file (archive.txt). It’s easy, just create your test class which inherits from FunctionalTestCase, define the afterSetUp method and change the test suite to use your class. Let’s do it…

Be careful with this, it could confuse the people reading your doctest file if you don’t explain clearly that you have already created tests in the doctest class.

We keep it basic. We imagine that we really need to create a document inside the home folder but don’t need to show that in the doctest file:

      import os, sys
      if __name__ == '__main__':
           execfile(os.path.join(sys.path[0], ''))

      from Products.PloneTestCase import PloneTestCase

      # we will now subclass FunctionalTestCase and define our afterSetUp method
      from Products.PloneTestCase.PloneTestCase import FunctionalTestCase

      class TestArchiveWithDocument(FunctionalTestCase):
               Our Functional test class with a document inside

            def afterSetUp(self):
                   Creating a document in the home directory that the archive doc test can use
                self.folder.invokeFactory('Document', id='doc')

      # And that's all! No test method, the only test method will be our
      # doctest file.  

      # now we need to link our functional test class to our doctest file
      # inside a test suite:
      from Testing.ZopeTestCase import FunctionalDocFileSuite
      def test_suite():
            import unittest
            suite = unittest.TestSuite()
      if __name__ == '__main__':

Now you know everything about tests, you have no excuse anymore for not writing them!

Archivado en: Plone, Python

Doctests, Huh?

– – – – – – – – – – – –
By Brian Sutherland  |

Articulo copiado de :

For good code to be useful, it requires two additional things: good documentation and good unit testing. Doctests allow the programmer to do both at the same time with the additional benefits that the documentation is tested and the prose between the tests documents the tests themselves. There are few things worse than undocumented unit tests which over time grow contorted by special cases until they are as understandable as black magic. Forming a link between code, documentation and testing is the greatest advantage of doctests.

Normally in a project there are three things you want to test using doctests:

  • Examples in the docstrings of functions, classes and modules showing their use and testing major functionality.
  • Documentation to be sure code examples are correct.
  • Regression testing to make sure past bugs don’t re-appear (Regression tests can become very obscure, so normally these should be separated from the rest to avoid over-complicating the documentation).

Each of these three have a different purpose and in a well structured project should be separated so that they can serve their purpose well. For example, including regression tests in the docstrings of a module will, over time, over-complicate the module’s docstrings. This interferes with the primary use of module docstrings i.e. understanding what the module does.

There are three API’s for writing doctests, a simple API, a unittest API and an advanced API. This article focuses on the unittest API as it is simple enough to learn very quickly, can be combined with more traditional python unit tests and can deal with almost almost every testing problem. In short, a very good compromise.

First we need to set up a project tree with some packages and modules, so just create a directory structure like this:

src/fruit/freshfruit-tutorial.txt   -> documentation
src/fruit/tests/  -> regression testing

Next we need some way to run the tests we are going to write. The best is probably to have a test runner script in the top level directory. You can roll your own, but I’m going to grab one from the Zope project. Just drop into the top level of of the project, make sure you have python 2.4 and the python profiler(Debian specific) installed and we can begin.

Testing Docstrings in ModulesSo what is the first thing to do with fresh fruit. Make a Salad! So we write some tests in and classes in then fill in the code. Have a look at how easy it is to understand the code from the test examples.

This module makes salads, it implements a FoodProcessor to process
ingredients which are then added to the SaladBowl container.

class FoodProcessor:
    """This class contains various food processing functions."""

    def diceCarrot(self, str):
        """This function dices carrots.

            >>> processor = FoodProcessor()
            >>> processor.diceCarrot('carrot')
            'diced carrot'

        and returns a ValueError if the input is not a carrot:

            >>> processor.diceCarrot('A diced carrot')
            Traceback (most recent call last):
            ValueError: 'A diced carrot' is not a carrot
        if str == 'carrot':
            return 'diced carrot'
            raise ValueError("%s is not a carrot" % repr(str))

    def peelBanana(self, str):
        """This function peels bananas.

            >>> processor = FoodProcessor()
            >>> processor.peelBanana('banana')
            'peeled banana'

        and returns a ValueError if the input is not a banana:

            >>> processor.peelBanana('A peeled banana')
            Traceback (most recent call last):
            ValueError: 'A peeled banana' is not a banana
        if str == 'banana':
            return 'peeled banana'
            raise ValueError("%s is not a banana" % repr(str))

class SaladBowl:
    """This is a container for processed ingredients.

    Then you can make a salad bowl and add the prepared ingredients

        >>> bowl = SaladBowl()
        >>> bowl.addIngredient('peeled banana')
        >>> bowl.addIngredient('diced carrot')
        >>> bowl.addIngredient('peeled banana')

    Finally, most people would want to eat it:

        That peeled banana was tasty!
        That diced carrot was tasty!
        That peeled banana was tasty!

    def __init__(self):
        self.contents = []

    def addIngredient(self, addition):
        """Adds ingredients to the salad bowl.

        Make a SaladBowl

            >>> bowl = SaladBowl()

        Add all the ingredients in a string

            >>> bowl.addIngredient('peeled banana')
            >>> bowl.addIngredient('diced carrot')
            >>> bowl.contents
            ['peeled banana', 'diced carrot']


    def eat(self):
        """Eat the contents of the SaladBowl.

        First set up a salad bowl:

            >>> bowl = SaladBowl()
            >>> bowl.contents = ['diced carrot', 'peeled banana']

        eat() out a eating message for all contents of the salad bowl.

            That diced carrot was tasty!
            That peeled banana was tasty!

        and removes them from the bowl:

            >>> bowl.contents
        while self.contents:
            print "That %s was tasty!" % self.contents.pop(0)

Also we need to set up the test runner so that it knows which module to test in This code should do the trick:

import unittest
import doctest

def test_suite():
    suite = unittest.TestSuite()
    return suite

Running the script that we dropped into the top level directory with

> python2.4

should show you that 5 unit tests were run successfully. Try changing the code or the tests to find out what happens when things fail.

As you may have noticed, each docstring is run in its own namespace, so it is necessary to define class instances for every test (i.e. bowl and processor). To make life easier, you can define them in extra parameters to the DocTestSuite call. Explaining exactly how to do this is too much for this article, but the doctest documentation is excellent.

Also, you don’t have to write the entirety of an exception, doctests will ignore all indented text between the first and last lines of an exception. The ... used in the examples is just a convention, but makes for more readable documentation than a copy of the exception itself.

Of the many doctest options there are two which are incredibly useful. The first, doctest.NORMALIZE_WHITESPACE causes the doctest module not to worry whether the whitespace in your test example is exact (Having failing tests because of trailing whitespace can be incredibly irritating). The second, doctest.ELLIPSIS makes ... match any substring in the test output, much like .* in regular expressions. Of course these should be used with care as they can cause failing tests to appear to work!

You can add these options to to the testing framework by modifying as follows:

import unittest
import doctest

def test_suite():
    suite = unittest.TestSuite()
            optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE))
    return suite
Testing Tutorial DocumentationIf you want to write tutorial documentation, doctests make it possible to add interactive code examples in plain text files, you can try this by adding

Making a salad

To make a salad, you first have to get a food processor:

>>> from fruit.freshfruit import FoodProcessor
>>> processor = FoodProcessor()

Then to process the food, you can dice some carrots:

>>> diced_carrot = processor.diceCarrot('carrot')
>>> diced_carrot
'diced carrot'

and peel some bananas:

>>> peeled_banana1 = processor.peelBanana('banana')
>>> peeled_banana2 = processor.peelBanana('banana')
>>> peeled_banana2
'peeled banana'

You can also get a salad bowl and put the ingredients in:

>>> from fruit.freshfruit import SaladBowl
>>> bowl = SaladBowl()
>>> bowl.addIngredient(diced_carrot)
>>> bowl.addIngredient(peeled_banana1)
>>> bowl.addIngredient(peeled_banana2)

Finally, you can eat and enjoy:

That diced carrot was tasty!
That peeled banana was tasty!
That peeled banana was tasty!

to freshfruit-tutorial.txt and slightly modifying to:

import os
import unittest
import doctest

def test_suite():
    suite = unittest.TestSuite()
        os.path.join('..', 'freshfruit-tutorial.txt')))
            optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE))
    return suite

As you can see, the text file is parsed exactly like one large docstring. Simple!

Regression TestingFinally, fruit is released and the bug reports start to roll in. Some people want to dice Unicode carrots and others BANANAS. In fact, there are so many different types of carrots and bananas, that testing every special case will make the in-module and tutorial documentation impossible to read.

This is what the file is for. Simply adding doctest.DocTestSuite()` will test all of the docstrings in `` Regression tests can be added as functions containing only docstrings without fear that they will complicate the documentation. As an example, this includes the tests for Unicode and capitalisation:

import os
import unittest
import doctest

def doctest_Unicode():
    """Test to make sure that deals with Unicode fruit.


        >>> from fruit.freshfruit import FoodProcessor
        >>> processor = FoodProcessor()

    A Unicode bananas and carrots must return Unicode:

        >>> processor.peelBanana(u'banana')
        u'peeled banana'
        >>> processor.diceCarrot(u'carrot')
        u'diced carrot'

def doctest_Capitalize():
    """Test to make sure that deals with CAPITALIZATION.


        >>> from fruit.freshfruit import FoodProcessor
        >>> processor = FoodProcessor()

    A BANANAs and CARROTs are also fruit:

        >>> processor.peelBanana('BANANA')
        'peeled banana'
        >>> processor.diceCarrot('CARROT')
        'diced carrot'

def test_suite():
    suite = unittest.TestSuite()
        os.path.join('..', 'freshfruit-tutorial.txt')))
            optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE))
    return suite

As you can see, it is easy to see exactly what each regression test is testing. It is left as an exercise for the reader to code the solution.

Some Doctest PitfallsThere are of course, some caveats. Specifically, the parsed output:

  • must be the same every time.
  • can be hard to fit into screen width.

Even with these, doctests are still capable of being useful in almost every testing situation. For the rest, there is the Python unittest framework.

FinallyThis short overview should be enough for a programmer to become productive with doctests immediately. He already has all the skills merely by knowing the python command line.

But perhaps the most important aspect of doctests is forcing a good style of documentation that is tested and correct. Every programmer knows the frustration of trying to understand a complex code without the aid of examples or comments. Think about them when you write your tests.

Further Reading and ThanksFor more information you should defiantly have a look at the doctest documentation for implementation, also Jim Fulton’s PyCon 2004 presentation and Phillip J. Eby’s essay go a long way to describe the philosophy behind doctests.

With thanks to Marius Gedminas for introducing me to doctests and helping out with this article. Errors are, of course, mine.

Brian Sutherland

Archivado en: Plone, Python

Development Environment en Plone

Para redactar este articulo use como guia el libro de Martin Aspeli ‘Professional Plone Development’. Puedes adquirirlo Aqui

Antes de comenzar a construir nuestra aplicacion, debemos armar nuestro entorno de desarrollo ( development environment)


Paquetes precompilados de Zope y Plone estan disponibles para varios sistemas operativos. Estos mismos pueden ser de gran ayuda, pero como developers usualmente es mejor tener dominio de toda la configuracion de nuestro entorno de desarrollo.

Sera necesario lo siguiente para poder trabajar:

Python 2.4: Se asume que al escribir el comando python, la version del interprete sera la 2.4. Desafortunadamente, zope, en la version 2.10 no tiene soporte aun para Python 2.5. Notese que varias distribuciones de linux proveen dos paquetes para python, una conteniendo el binario y otra los headers files, llamados generalmente como python-dev o similar. Se precisaran ambos, el binario de Python2.4 y los header files para compilar Zope.

PIL: the Python Imaging Library deberia ser instalado con nuestro interprete. Cuando vimos como crear un virtual python environment al final del articulo logramos tener un interprete de python:

bash-3.1$ ~/.pyenv/bin/python
Python 2.4.4 (#1, Jun 15 2008, 17:41:52)
[GCC 4.2.3] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.

Pues para instalar nuestras dependencias / requisitos como en este caso PIL, deberiamos proceder como se detalla a continuacion:

bash-3.1$ ~/.pyenv/bin/python /home/lain/.temp/PIL/ install

Con esto, la libreria de PIL quedara instalada en el site-packages de nuestro pyenv.

Editor de texto: Se recomienda un editor de texto con soporte para python y highlighting para XML/HTML. Personalmente para maquinas con modestos recursos recomiendo vim y para aquellas maquinas mas poderosas recomiendo Eclipse (el IDE de java) con el plugin de PyDev.

elementtree: es una libreria que procesa XML, requerida por Plone al momento de iniciar. La mayoria de las distribuciones vienen con esta libreria. Tambien puede ser descargada de Aqui

Un Shell: Bash … zsh … lo que quieran siempre y cuando la sepan usar 🙂
Un cliente de Subversion: El clasico Subversion. Quiza hallan mejores opciones, comenten! 😀
Un compilador de C: Se necesita un compilador de C para compilar Zope. El venerable Gcc va de lleno en sistemas tipo UNIX.

Comienzo Veloz: (the quick way)

Si tienes los requisitos mencionados anteriormente y necesitas tener tu entorno de desarrollo en poco tiempo, aqui va la receta. Notese que el comando easy_install fue obtenido en el articulo virtual python environment

lain@lain:~$ ~/.pyenv/bin/easy_install-2.4 ZopeSkel

ZopeSkel son esqueletos / premoldes que nos ahorraran mucho trabajo, errores y lineas de codigo como veremos mas adelante.
Luego utilizamos paster, que fue instalado como dependencia de ZopeSkel para crear un nuevo buildout. Este directorio (el del buildout) contendra el codigo fuente y las dependencias, incluyendo el servidor de aplicaciones Zope.

lain@lain:~/.tmp$ ~/.pyenv/bin/paster create -t plone3_buildout ~/.mybuildout

Aqui utilizamos el comando paster al cual le pedimos que cree algo de tipo plone3_buildout en mi home, directorio .mybuilldout. La construccion y lo que debe hacer lo sabe a partir de ZopeSkel que lo instalamos anteriormente ;).
Al correr este comando, paster nos hara algunas preguntas las cuales podemos dejar por default, excepto cuando nos pida el password, el cual es escogido por nosotros para luego ingresar como administradores como veremos mas adelante. En mi caso:

Enter zope_password (Zope root admin password) [”]: changeme

Escojo el puerto 9999. Tener en cuenta que si ingresamos un puerto dentro del rango privilegiado (0 – 1024), la instancia debera correr como root (innecesariamente), el por ello que elijo como puerto el 9999.

Enter http_port (HTTP port) [8080]: 9999

Dejamos en off el modo de debug, luego podremos manualmente indicarle al iniciar la instancia de que deseamos que se encuentre en modo debug:

Enter debug_mode (Should debug mode be “on” or “off”?) [‘off’]: off

Finalizado el proceso nos encontraremos con el siguiente texto:

Generation finished
You probably want to run python and then edit
buildout.cfg before running bin/buildout -v

See README.txt for details

En hora buena! tenemos nuestro buildout … ahora solo falta hacerlo funcionar!

Ingresamos a nuestro nuevo buildout

lain@lain:~/.tmp/home/lain$ cd ~/.mybuildout/

Corremos el bootstrap el cual nos creara unos scripts para luego armar nuestro buildout

lain@lain:~/.mybuildout$ ~/.pyenv/bin/python ./
Creating directory ‘/home/lain/.mybuildout/bin’.
Creating directory ‘/home/lain/.mybuildout/parts’.
Creating directory ‘/home/lain/.mybuildout/eggs’.
Creating directory ‘/home/lain/.mybuildout/develop-eggs’.
Generated script ‘/home/lain/.mybuildout/bin/buildout’.

Tiempo de comenzar a armar nuestro buildout! 😀

lain@lain:~/.mybuildout$ ~/.pyenv/bin/python ./bin/buildout -v

En este momento comenzara a bajar e instalar y acomodar todo para dejarnos una instancia lista de nuestro plone3 :).
Terminado el proceso podremos arrancar nuestra instancia. Poniendo fg como parametro arrancara nuestra instancia en debug mode.

lain@lain:~/.mybuildout$ ./bin/instance fg

Una vez arrancado nuestro buildout ingresamos con un web browser a . En mi caso:

lain@lain: links

Nos pedira un usuario y password. El usuario si no lo cambiaron en la construccion del buildout, es admin y el password es el que escogimos, en este articulo fue changeme.

Nos encontraremos con lo siguiente, la zmi! 😀  (zope manager interface)

Welcome to the Zmi

Desde ella, en el drop-down box donde dice AtContentTypes tool, escogemos Plone Site y luego click en Add. A lo que recibiremos la siguiente imagen:

Adding a New Plone Site

En el campo ID he puesto miplone. Como dice el field name, este sera el id de nuestro sitio, cual utilizaremos para luego acceder a nuestro sitio plone siendo la url en mi caso

En el campo Title he puesto Mi sitio Plone Personal, este sera el titulo de mi sitio. Luego podra ser cambiado :).

Finalizado esto damos click en el boton Add Plone Site … y … ingresamos la esperada url, en mi caso: teniendo como resultado … lo siguiente! 😀

Plone Site recently created

Esto es todo amigos … hemos creado desde cero nuestro buildout y tenemos nuestro sitio plone, quien quedara sujeto a todo tipo de pruebas mas adelante!.

Nos vemos en la proxima entrega 😛

Archivado en: Plone, Python

SlackBuilds en Slackware 12.1

Dedicado a ASCIIGirl ^.^” (no soy mala onda x’D)

El proyecto SlackBuilds es mantenido por un pequenio grupo de personas que codifican scripts para el uso de la comunidad.

Este proyecto surge a raiz de una de las caracteristicas mas cuestionadas en Slackware … la ‘falta’ de paquetes. Siendo Slackware una distribucion que provee una gran cantidad de paqutes que satisfacen variadas y diferentes necesidades, quedan aplicaciones que no estan incluidas oficialmente y pueden ser requeridas por ciertos usuarios.
Son sabidos algunos servidores donde se suben paquetes precompilados para slackware de terceras partes, a lo cual varios usuarios justificandose reclamaron que no les era fiable el instalar estos paquetes en sus equipos por cuestiones de confianza, seguridad, etc. La solucion para estos usuarios en ese momento era descargar el codigo fuente de la aplicacion y compilarla manualmente, lo que traia inconvenientes por ejemplo en la administracion de sistemas a la hora de mantener las versiones de las distintas aplicaciones.

Solucionando este problema es que aparecen los muchachos de SlackBuilds, quienes codifican scripts que toman el source de la aplicacion y generan un paqute *.tgz, siguiendo la idea de Patrick Volkerding, quien utiliza SlackBuilds para generar los paquetes oficiales que se incluyen en cada release.

Habiendo tenido una pequenia introduccion sobre slackbuilds, veamos como utilizarlos. Para ello vamos a instalar yakuake, una terminal para kde muy bonita 🙂

Ingresamos a

bash-3.1$ links

Buscams el paquete yakuake, descargamos el source y el SlackBuild. Tambien descargamos el fichero que termina en .asc. Nos deben quedar estos ficheros:

bash-3.1$ ls -l yakuake*
-rw-r–r– 1 lain users 535406 2008-06-16 14:28 yakuake-2.8.1.tar.bz2
-rw-r–r– 1 lain users 1938 2008-06-16 14:28 yakuake.tar.gz
-rw-r–r– 1 lain users 197 2008-06-16 14:28 yakuake.tar.gz.asc

Corroboramos con la llave publica yakuake.tar.gz.asc el Slackbuild yakuake.tar.gz:
Si es la primera vez que realizamos esto, tendremos como resultado lo siguiente:

bash-3.1$ yakuake.tar.gz.asc yakuake.tar.gz
Verifying yakuake.tar.gz with yakuake.tar.gz.asc…

gpg: directory `/home/lain/.gnupg’ created
gpg: new configuration file `/home/lain/.gnupg/gpg.conf’ created
gpg: WARNING: options in `/home/lain/.gnupg/gpg.conf’ are not yet active during this run
gpg: keyring `/home/lain/.gnupg/pubring.gpg’ created
gpg: Signature made Wed 07 May 2008 03:08:34 PM ART using DSA key ID 9C7BA3B6
gpg: Can’t check signature: public key not found

No puede encontrar la clave publica … pues la pedimos:

bash-3.1$ 9C7BA3B6
Getting 9C7BA3B6 from…

gpg: keyring `/home/lain/.gnupg/secring.gpg’ created
gpg: requesting key 9C7BA3B6 from hkp server
gpg: /home/lain/.gnupg/trustdb.gpg: trustdb created
gpg: key 9C7BA3B6: public key “ Development Team ” imported
gpg: no ultimately trusted keys found
gpg: Total number processed: 1
gpg: imported: 1

Ahora nuevamente comprobamos el Slackbuild :

bash-3.1$ yakuake.tar.gz.asc yakuake.tar.gz
Verifying yakuake.tar.gz with yakuake.tar.gz.asc…

gpg: Signature made Wed 07 May 2008 03:08:34 PM ART using DSA key ID 9C7BA3B6
gpg: Good signature from “ Development Team ”
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: D307 6BC3 E783 EE74 7F09 B8B7 0368 EF57 9C7B A3B6

Perfecto … el warning se debe a que la llave no se encuentra firmada por una llave en la que yo tenga confianza. Para mas detalles sobre esto click Aqui
Continuamos descomprimiendo el Slackbuild:

bash-3.1$ tar zxvf yakuake.tar.gz

Seguimos, moviendo el source de yakuake dentro del SlackBuild:

bash-3.1$ mv yakuake-2.8.1.tar.bz2 ./yakuake; cd ./yakuake

Corremos el SlackBuilds (puede haber casos en los que se necesite hacer esto como root). Tambien constatar que el fichero tiene permisos de ejecucion:

bash-3.1$ ls -l ./yakuake.SlackBuild
-rwxr-xr-x 1 lain users 1616 2008-03-28 21:07 ./yakuake.SlackBuild
bash-3.1$ ./yakuake.SlackBuild

Terminado el proceso de compilacion, nos encontramos en el directorio /tmp con nuestro paqute *.tgz

bash-3.1$ find /tmp/yakuake*

Ahora lo podemos instalar normalemente como root con pkgtool:

bash-3.1# installpkg /tmp/yakuake-2.8.1-i486-1_SBo.tgz

De esta manera podemos controlar los paqutes de nuestro sistema con pkgtool, resolviendo el problema de mantenimiento de versiones instaladas, paquetes instalados, etc! 😀

Adjunto el codigo de los dos scripts utilizados. Los mismos simplemente ejecutan gpg con los argumentos necesario y el proposito de dichos scripts es el que no podia recordar dichos parametros 😛 … Si les sirve, aqui los tienen.

#!/usr/bin/env python

import os
import sys

if len(sys.argv) != 3:
print 'Usage: '

p = '--verify'
p1 = sys.argv[1]
p2 = sys.argv[2]

print 'Verifying ' + p2 + ' with ' + p1 + '...\n'

(child_stdin,child_stdout) = os.popen4(cmd = 'gpg %s %s %s' % (p, p1, p2))

for i in child_stdout.readlines():
print i,

print '\n'

#!/usr/bin/env python

import os
import sys

if len(sys.argv) != 2:
print 'Usage: '

s = ''
p = '--keyserver %s' % s
k = sys.argv[1]
p1 = '--recv-keys %s' % k

print 'Getting ' + k + ' from ' + s + '...\n'

(child_stdin,child_stdout) = os.popen4(cmd = 'gpg %s %s' % (p, p1))

for i in child_stdout.readlines():
print i,

print '\n'

Archivado en: Python, Slackware

Virtualenv en Slackware 12.1

Dedicado a Katai ^.^”

Teniendo Python 2.4 instalado, vamos a proceder a realizar un virtualenv:

Virtual Python Environment builder:
virtualenv is a successor to workingenv, and an extension of virtual-python.
What it Does?:
virtualenv is a tool to create isolated Python environments.

A lo bestia, digamos que virtualenv es un ‘chroot’ de la instancia python que vamos a utilizar para trabajar. Mas data Aqui

Creando un Python ‘Virtual’:
Si estas en un sistema Linux, BSD, Cygwin o algun otro de OS de tipo UNIX y no tienes acceso de root, puedes crear tu propia instalacion ‘virtual’ de python, cual utiliza sus propias librerias y enlaza algunas otras con las librerias de python del sistema.

Proseguimos descargando

bash-3.1$ wget

Lo instalamos:

bash-3.1$ mkdir ~/.vpython
bash-3.1$ python2.4 –help
usage: [options]

-h, –help show this help message and exit
-v, –verbose Increase verbosity
–prefix=PREFIX The base directory to install to (default ~)
–clear Clear out the non-root install and start from scratch
–no-site-packages Don’t copy the contents of the global site-packages dir
to the non-root site-packages

bash-3.1$ python2.4 ./ –prefix=/home/lain/.vpython
Creating /home/lain/.vpython/lib/python2.4
Creating /home/lain/.vpython/lib/python2.4/site-packages
Creating /home/lain/.vpython/include/python2.4
Creating /home/lain/.vpython/bin
Copying /usr/local/bin/python2.4 to /home/lain/.vpython/bin
You’re now ready to download, and run


Ok, vamos a correr (easy_install) mas datos Aqui

bash-3.1$ /home/lain/.vpython/bin/python
Processing setuptools-0.6c8-py2.4.egg
creating /home/lain/.vpython/lib/python2.4/site-packages/setuptools-0.6c8-py2.4.egg
Extracting setuptools-0.6c8-py2.4.egg to /home/lain/.vpython/lib/python2.4/site-packages
Adding setuptools 0.6c8 to easy-install.pth file
Installing easy_install script to /home/lain/.vpython/bin
Installing easy_install-2.4 script to /home/lain/.vpython/bin

Installed /home/lain/.vpython/lib/python2.4/site-packages/setuptools-0.6c8-py2.4.egg
Processing dependencies for setuptools==0.6c8
Finished processing dependencies for setuptools==0.6c8

Proseguimos con nuestra meta, estamos en condiciones de instalar virtualenv!

bash-3.1$ ~/.vpython/bin/easy_install-2.4 virtualenv
Searching for virtualenv
Best match: virtualenv 1.1
Processing virtualenv-1.1-py2.4.egg
virtualenv 1.1 is already the active version in easy-install.pth
Installing virtualenv script to /home/lain/.vpython/bin

Using /home/lain/.vpython/lib/python2.4/site-packages/virtualenv-1.1-py2.4.egg
Processing dependencies for virtualenv
Finished processing dependencies for virtualenv

Ahora a crear nuestro python environment!

bash-3.1$ mkdir ~/.pyenv
bash-3.1$ ~/.vpython/bin/virtualenv ~/.pyenv
New python executable in /home/lain/.pyenv/bin/python
Installing setuptools………….done.

Voila! terminamos 🙂

bash-3.1$ ~/.pyenv/bin/python
Python 2.4.4 (#1, Jun 15 2008, 17:41:52)
[GCC 4.2.3] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.

El sabor de la victoria! ^.^”
Ahora podemos crear tantos pyenv como queramos en nuestro sistema y utilizar cada uno independientemente en nuestros proyectos :).

pd: bue … tmb dedicado a dergerk … derberk … derrr … dererk ~.~”

Archivado en: Plone, Python, Slackware

Instalando Python 2.4 en Slackware 12.1

La version de Python en Salckware 12.1 es Python 2.5.2 (r252:60911, Mar 1 2008, 13:52:45).
Para instalar la version 2.4 que necesitamos para Zope procedemos de la siguiente manera:

Primero bajamos el source de Python desde el sitio oficial de python

bash-3.1$ wget


bash-3.1$ tar jxvf Python-2.4.4.tar.bz2

Cambiamos al directorio desempaquetado:

bash-3.1$ cd Python-2.4.4

Corremos el configure:

bash-3.1$ ./configure

Compilamos, notese el parametro -j :

-j [jobs], –jobs[=jobs]
Specifies the number of jobs (commands) to run simultaneously. If there is more than one -j option, the last one is effective. If the -j option is given
without an argument, make will not limit the number of jobs that can run simultaneously.

En mi caso:

bash-3.1$ make -j3

Instalamos ( como root ):

bash-3.1$ make install

Eso es todo, ahora ejecutamos :

bash-3.1$ python2.4

Archivado en: Python, Slackware