Recent posts

xPressent, the PDF slides remote and cheats

I have started working on a new project called xPressent.

As a teacher, I work everyday with PDF slides on a laptop connected to a projector. I used to have notes related to the slides on a printed paper. With xPressent, you can remotely control the slides on the projector, and see the slide preview and related notes on the IT screen.

xPressent is composed of a server side, a simple full screen PDF viewer using Python, Poppler and SDL, and a client side, the bluetooth remote control, that runs on Maemo Devices (there's also a compact .NET version, and a J2ME version coming).

To make it work, you create a .xpr file with the notes for the slides, with the same name of the .pdf file, and run the xpressent server on the laptop with the .xpr file as parameter. Them, you can start the remote client on the internet tablet, and connect via bluetooth to the server to change the slides and read the notes. The current slide and notes are sent over the bluetooth connection. Don't forget to edit the xpressent.conf file to add the remote bluetooth address to the list of known addresses!

You cand find more information here:

 https://devel.airadier.com/xpressent
 https://garage.maemo.org/projects/xpressent-maemo/

This project is on-going work. I hope to have maemo packages ready soon, but right now you can download the script and run it from command line, or using some launcher.

Some other interesting features, like drawing over the slides or virtual pointer from the remote are coming soon.

Hope you enjoy it!

Virtual Environments using Apache and mod_python

After searching for a while on The Internet I couldn't find a good solution for my problem: running two versions of Trac on the same Apache web server, using mod_python.

Some experience with python, and Apache configuration is assumed!

The problem

Setting up the new airadier.com development site (the one you're reading this post on), I decided to go for the recently released Trac 0.11. The same server was already running an installation of Trac version 0.10.4. So, I needed to be able to run both versions at the same time, each with their own modules and plugins (I didn't went to go through the upgrade process for the current site).

First approach

At first, I tried moving the currently intalled Trac folder and plugin eggs to an isolated folder, that is:

/usr/local/trac0.10

which contained:

trac/
trac-0.10.4.egg-info
TracAccountManager-0.1.3dev_r2548-py2.5.egg/
TracPageToPDF-0.2-py2.5.egg/
TracProjectMenu-1.0-py2.5.egg/
TracTocMacro-1.0-py2.5.egg/
TracWebAdmin-0.1.2dev-py2.5.egg/

then I made a new folder:

/usr/local/trac0.11

and installed the new version of Trac 0.11 and some plugins. After installing these items, the trac0.11 folder had the following files and folders:

cgi-styler-form.py
cgi-styler.py
clearsilver-0.10.1-py2.5-linux-i686.egg/
easy-install.pth
Genshi-0.5.1-py2.5-linux-i686.egg/
IniAdmin-0.2-py2.5.egg/
pygmentize
Pygments-0.10-py2.5.egg/
SilverCity-0.9.7-py2.5-linux-i686.egg/
source2html.py
textile-2.0.11-py2.5.egg/
Trac-0.11stable_r7327-py2.5.egg/
TracAccountManager-0.2.1dev_r3857-py2.5.egg/
trac-admin
tracd
TracFullBlogPlugin-0.1-py2.5.egg/
tracsuperuser-0.2-py2.5.egg/

Installing to an isolated folder

To install python packages or eggs to an isolated folder (e.g. /usr/local/trac0.11), I recommend using easy_install. To install an egg file, just do:

PYTHONPATH=/path/to/virtualenvironment easy_install -d /path/to/virtualenvironment -Z egg_to_install.egg

make sure you have write permissions in the destination folder, you might need to use sudo in front of easy_install:

PYTHONPATH=/path/to/virtualenvironment sudo easy_install -d /path/to/virtualenvironment -Z egg_to_install.egg

to install a ready to build package (that is, a folder containing the setuptools setup.py file), just do:

PYTHONPATH=/path/to/virtualenvironment easy_install -d /path/to/virtualenvironment -Z package_folder/

again, don't forget about write permissions.

Configuing Apache

Next step, I filled the virtual site configuration for Apache, using a Location directive (as instructed by the Trac installation guide for mod_pythoon):

Site for Trac 0.10.4:

        ...

        <Location /trac>
                SetHandler mod_python
                PythonPath "['/usr/local/trac0.10',] + sys.path"
                PythonHandler trac.web.modpython_frontend
                PythonOption TracEnvParentDir /path_to_trac0.10_envs/
                PythonOption TracUriRoot /trac
        </Location>

        ...

Virtual Site configuration for Trac 0.11:

        ...

        <Location /trac>
                SetHandler mod_python
                PythonPath "['/usr/local/trac0.11',] + sys.path"
                PythonHandler trac.web.modpython_frontend
                PythonOption TracEnvParentDir /path_to_trac0.11_envs/
                PythonOption TracUriRoot /trac
        </LocationMatch>

        ...

That way the PythonPath should be different for the different virtual hosts.

Why it didn't work

The previous approach didn't work at all. I got mod_python complaining about the "trac" module couldn't be found. Why? Finally I noticed. Apparently, working from command line, everything was correct:

$ PYTHONPATH=/usr/local/trac0.11/ python
iPython 2.5.1 (r251:54863, Mar  7 2008, 03:41:45) 
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
m>>> import trac
>>> dir(trac)
['__builtins__', '__doc__', '__file__', '__name__', '__path__', '__version__']
>>> trac.__version__
'0.11stable-r7327'
>>>

$ PYTHONPATH=/usr/local/trac0.10/ python
Python 2.5.1 (r251:54863, Mar  7 2008, 03:41:45) 
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import trac
>>> trac.__version__
'0.10.4'

The key was the difference between the PYTHONPATH environment variable, and the PythonPath option for mod_python. The first is set before the python interpreter is loaded. When the interpreter stars, the folders in PYTHONPATH are added to sys.path. Then, every folder in sys.path is scanned for eggs and .pth files, and files or folders listed in there are added to the sys.path too. These are the contents of /usr/local/trac0.11/easy_install.pth:

import sys; sys.__plen = len(sys.path)
./Trac-0.11stable_r7327-py2.5.egg
./Genshi-0.5.1-py2.5-linux-i686.egg
./TracAccountManager-0.2.1dev_r3857-py2.5.egg
./Pygments-0.10-py2.5.egg
./clearsilver-0.10.1-py2.5-linux-i686.egg
./textile-2.0.11-py2.5.egg
./SilverCity-0.9.7-py2.5-linux-i686.egg
./TracFullBlogPlugin-0.1-py2.5.egg
./IniAdmin-0.2-py2.5.egg
./tracsuperuser-0.2-py2.5.egg
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:];
p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

And this are the contents of sys.path when starting the python interpreter from the command line:

$ PYTHONPATH=/usr/local/trac0.11/ python
Python 2.5.1 (r251:54863, Mar  7 2008, 03:41:45) 
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.path
['',
'/usr/local/trac0.11/Trac-0.11stable_r7327-py2.5.egg',
'/usr/local/trac0.11/Genshi-0.5.1-py2.5-linux-i686.egg',
'/usr/local/trac0.11/TracAccountManager-0.2.1dev_r3857-py2.5.egg',
'/usr/local/trac0.11/Pygments-0.10-py2.5.egg',
'/usr/local/trac0.11/clearsilver-0.10.1-py2.5-linux-i686.egg',
'/usr/local/trac0.11/textile-2.0.11-py2.5.egg',
'/usr/local/trac0.11/SilverCity-0.9.7-py2.5-linux-i686.egg',
'/usr/local/trac0.11/TracFullBlogPlugin-0.1-py2.5.egg',
'/usr/local/trac0.11/IniAdmin-0.2-py2.5.egg',
'/usr/local/trac0.11/tracsuperuser-0.2-py2.5.egg', 
'/usr/local/trac0.11', 
'/usr/lib/python25.zip', 
'/usr/lib/python2.5', 
'/usr/lib/python2.5/plat-linux2', 
'/usr/lib/python2.5/lib-tk', 
'/usr/lib/python2.5/lib-dynload', 
'/usr/local/lib/python2.5/site-packages', 
'/usr/lib/python2.5/site-packages', 
'/usr/lib/python2.5/site-packages/PIL', 
'/var/lib/python-support/python2.5']
>>> 

However (I found out this using the PythonHandler mod_python.testhandler instead of the trac.web.modpython_frontend handler), the sys.path in the mod_python interpreter didn't include all this .egg folder (so, module trac, in folder /usr/local/trac0.11/Trac-0.11stable_r7327-py2.5.egg couldn't be imported).

I guess mod_python loads the python interpreter first, and then adds evaluates the PythonPath option, which adds /usr/local/trac0.11 folder to sys.path, but doesn't check it for .pth files.

The fix

Right now I managed to get it working by wrapping the trac mod_python handler with an special handler that will correctly set-up the python path. This handler is a small python module located in:

/usr/local/trac0.11/handler_wrapper.py

import site
from mod_python import apache
import sys

def handler(req):
        sitedirs = eval(req.get_options()['SiteDirs'])
        for sitedir in sitedirs:
                site.addsitedir(sitedir)
        handler_name = req.get_options()['WrappedHandler']
        mod_handler = __import__(handler_name)
        for comp in handler_name.split('.')[1:]:
                mod_handler = getattr(mod_handler, comp)
        return mod_handler.handler(req)

This handler will take the SiteDirs option from mod_python (see later), and for each specified dir, scan it for .pth files using site.addsitedir from the site module.

After this is done, the wrapped real handler, defined with the WrappedHandler option is imported and called.

The result is sys.path is correctly configured before the real handler is imported and executed, and everything works as expected.

To make this work, a slight modification is needed in the Apache virtual site configuration. The configuration for the 0.11 site is shown below:

        <Location /trac>
                SetHandler mod_python
                PythonPath "['/usr/local/trac0.11'] + sys.path"
                PythonHandler handler_wrapper
                PythonOption WrappedHandler trac.web.modpython_frontend
                PythonOption SiteDirs ['/usr/local/trac0.11']
                PythonOption TracEnvParentDir /path_to_trac0.11_envs/
                PythonOption TracUriRoot /
        </Location>
  • Posted: 2008-07-17 18:41 (Updated: 2008-09-16 09:30)
  • Categories: python trac
  • Comments (492)