pb.recipes.pydev

Contents

Author: Tiberiu Ichim, tibi@pixelblaster.ro

Overview

This recipe is about modifying a .pydevproject file, which is used by Eclipse and PyDev to hold a list of folders which hold Python packages (for code completition, auto-import and so on). This recipe won't generate a new one! If you're running the buildout directly in the PyDev project, you're all set, otherwise you have to set the path to the .pydevproject file.

The idea is to fill this file with paths pointing to the used egg folders. Although this recipe is written with a zope3 instance in mind, it can probably be used for any other scenario (for Plone integration read at the end of the file. After running the buildout you'll have to close and reopen the Eclipse project, to regenerate the project's module indexes.

How it works

>>> import os

A full recipe would include the following options:

>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = pydev
...
... [pydev]
... recipe = pb.recipes.pydev
... pydevproject_path = ${buildout:directory}/.pydevproject_test
... extra-paths = /something/else
... eggs = pb.recipes.pydev
... """)

We need a working .pydevproject file. This recipe won't generate a new one. I'll use this project's project file for testing.

>>> fc = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
... <?eclipse-pydev version="1.0"?>
... <pydev_project>
... <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
... <path>/pb.recipes.pydev/src</path>
... </pydev_pathproperty>
... <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.4</pydev_property>
... <pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH">
... <path>/home/tibi/workspace3.3/pb.recipes.pydev/eggs/zc.buildout-1.0.0b31-py2.4.egg</path>
... <path>/home/tibi/workspace3.3/pb.recipes.pydev/eggs/zc.recipe.egg-1.0.0-py2.4.egg</path>
... </pydev_pathproperty>
... </pydev_project>"""
>>> pf_path = os.path.join(sample_buildout, '.pydevproject_test')
>>> f = open(pf_path, 'w')
>>> f.write(fc)
>>> f.close()

Now we can run the buildout

>>> print system(buildout) #doctest: +NORMALIZE_WHITESPACE
Installing pydev.

With this recipe we only override the external source paths entry, so we'll only check that:

>>> import os
>>> from xml.dom import minidom
>>> document = minidom.parse(os.path.join(sample_buildout,
...                             '.pydevproject_test'))
>>> nodes = document.getElementsByTagName('pydev_pathproperty')
>>> paths_node = filter(lambda node: (node.getAttribute('name') ==
...                     'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'),
...                nodes
...             )[0]
>>> data = paths_node.toxml()

The paths should contain what we have specified in teh the extra-paths option:

>>> '/something/else' in data
True

In our sample buildout.cfg we have placed, in the eggs option, this recipe's egg. In your projects, this can be anything, (and it should be your developing application's egg name, as declared in your setup.py). The pb.recipes.pydev egg depends on zc.recipe.egg, zc.buildout and setuptools, let's see if all these were included.

>>> 'zc.recipe.egg' in data
True
>>> 'zc.buildout' in data
True
>>> 'setuptools' in data
True

Note: at this moment, the project's source path is also included, in theory it shouldn't affect Eclipse.

>>> code_path = __file__[:len(__file__) -
...                          len('/src/pb/recipes/pydev/README.txt')]
>>> pydev_egg_src = os.path.join(code_path, 'src')
>>> pydev_egg_src in data
True

In version 0.2 the paths were added twice, let's check that this doesn't happen anymore:

>>> data.count('zc.recipe.egg')
1

In version 0.3, the recipe would fail with a .pydevproject file with no external source path node, let's check this doesn't happen anymore:

>>> fc = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
... <?eclipse-pydev version="1.0"?>
... <pydev_project>
... <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
... <path>/pb.recipes.pydev/src</path>
... </pydev_pathproperty>
... </pydev_project>"""
>>> pf_path = os.path.join(sample_buildout, '.pydevproject_test')
>>> f = open(pf_path, 'w')
>>> f.write(fc)
>>> f.close()
>>> print system(buildout) #doctest: +NORMALIZE_WHITESPACE
Updating pydev.
>>> import os
>>> from xml.dom import minidom
>>> document = minidom.parse(os.path.join(sample_buildout,
...                             '.pydevproject_test'))
>>> nodes = document.getElementsByTagName('pydev_pathproperty')
>>> paths_node = filter(lambda node: (node.getAttribute('name') ==
...                     'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'),
...                nodes
...             )[0]
>>> data = paths_node.toxml()
>>> '/something/else' in data
True
>>> 'zc.recipe.egg' in data
True
>>> 'zc.buildout' in data
True
>>> 'setuptools' in data
True

Integration strategies (and Plone)

Almost all options of this recipe for the buildout.cfg are optional. The only one required is the eggs option. A sample zope3 instance buildout, with the pydev recipe could be something like this:

[buildout]
develop = .
parts = instance pydev

[sample-app]
recipe = zc.zope3recipes:app
eggs = something [app, third_party]

[pydev]
recipe = pb.recipes.pydev
eggs = ${sample-app:eggs}

To integrate with Plone, follow the example posted by Silvio in this post on Product-Developers mailing list: http://www.nabble.com/Re:--Product-Developers--buildout-and-eclipse-p16699863s20094.html:

[make_pydev_init_files]
# we need this (a Products directory with symlinks to all plone products)
# to have completion of code in the Products namespace
recipe = iw.recipe.cmd:py
on_install=true
cmds =
      >>> dirs = """${instance:products}""".split("\n")
      >>> prodlinks = os.path.join("""${buildout:directory}""".strip() , 'prodlinks')
      >>> Products = os.path.join(prodlinks,'Products')
      >>> import os
      >>> if not os.path.isdir(prodlinks): os.mkdir(prodlinks)
      >>> if not os.path.isdir(Products): os.mkdir(Products)
      >>> open(os.path.join(Products , '__init__.py'),'w').write('#')
      >>> for dir in dirs:
      >>>      if dir:
      >>>           for product in [os.path.join(dir,a) for a in os.listdir(dir) if os.path.isdir(os.path.join(dir,a))]:
      >>>               linkname = os.path.join(Products, os.path.basename(product))
      >>>               if not os.path.islink(linkname): os.symlink(product,linkname)