Tuesday, October 13, 2009

Using doxypy for Python code documentation

Last time I wrote a long discussion about Python module documentation that led me toward using doxypy feeding into doxygen to produce my docs. Since I don't expect Python programmers in particular to be familiar with doxygen, a simple tutorial for how to get started doing that seemed appropriate. I had to document this all for myself anyway.

Running on Ubuntu, here's what I did to get the basics installed (less interesting bits clipped here):
$ sudo apt-get install doxygen
$ cd $HOME
$ wget http://code.foosel.org/files/doxypy-0.4.1.tar.gz
$ tar xvfz doxypy-0.4.1.tar.gz
$ sudo python setup.py install
running install
running build
running build_scripts
running install_scripts
creating /usr/local/local
creating /usr/local/local/bin
copying build/scripts-2.6/doxypy.py -> /usr/local/local/bin
changing mode of /usr/local/local/bin/doxypy.py to 755
running install_egg_info
Creating /usr/local/local/lib/python2.6/dist-packages/
Writing /usr/local/local/lib/python2.6/dist-packages/doxypy-0.4.1.egg-info

That last part is clearly wrong. The setup.py code that ships with doxypy is putting "/usr/local" where "/usr" should go, which results in everything going into "/usr/local/local". That needs to get fixed at some point (update: as of doxypy-0.4.2.tar.gz 2009-10-14, this bug is gone), for now I was content to just move things where they were supposed to go to work around it and cleanup the mess:
$ sudo mv /usr/local/local/bin/doxypy.py /usr/local/bin
$ sudo mv /usr/local/local/lib/python2.6/dist-packages/doxypy-0.4.1.egg-info /usr/local/lib/python2.6/dist-packages/
$ sudo rmdir /usr/local/local/bin
$ sudo rmdir /usr/local/local/lib/python2.6/dist-packages/
$ sudo rmdir /usr/local/local/lib/python2.6/
$ sudo rmdir /usr/local/
$ sudo rmdir /usr/local/local/lib/
$ sudo rmdir /usr/local/local
And, yes, I am so paranoid about running "rm -rf" anywhere that I deleted the directories one at a time instead of letting recursive rm loose on /usr/local/local instead. You laugh, but I've watched a badly written shell script wipe out a terabyte of data not being careful with rm.

Now we need a sample project work on. Here's a tired old example I've updated with a first guess at markup that works here:
$ cd <my project>
$ $EDITOR fib.py
#!/usr/bin/env python
"""@package fib
Compute the first ten numbers in the Fibonacci sequence
"""

def fib(n):
"""
Return a Fibonacci number

@param n Number in the sequence to return
@retval The nth Fibonacci number
"""
if n>1:
return fib(n-1)+fib(n-2)
if n==0 or n==1:
return 1

if __name__ == '__main__':
for i in range(1,10):
print fib(i)

Now we ask doxygen to create a template configuration file for us, edit it to add some lines to the end, and run it:
$ doxygen -g
$ $EDITOR Doxyfile

(add these lines to the end and comment them
out where they appear earlier)

# Customizations for doxypy.py
FILTER_SOURCE_FILES = YES
INPUT_FILTER = "python /usr/local/bin/doxypy.py"
OPTIMIZE_OUTPUT_JAVA = YES
EXTRACT_ALL = YES
FILE_PATTERNS = "*.py"
INPUT = "fib.py"

$ doxygen

The basics of how to get Doxygen going are documented in its starting guide. For a non-trivial program, you'll probably want to make INPUT more general and expand FILE_PATTERNS (which doesn't even do anything the way INPUT is setup here). As hinted above, I'd suggest commenting out all of the lines in the file where the parameter's we're touching above originally appear, along with adding a block like this to the end of it with your local changes. That's easier to manage than touching all of the values where they show up in the template.

Now fire up a web browser and take a look at what comes out in the html/ directory. You have to drill down into "Namespaces" or "Files" to find things in this simple example.

Function Documentation
def fib::fib ( n )

Return a Fibonacci number.

Parameters:
n Number in the sequence to return

Return values:
The nth Fibonacci number
There's a few more things I need to do here before I'll be happy enough with this to use it everywhere:
While there's plenty left to learn and some loose ends, so far I'm happy enough with this simple proof of concept to keep going in this direction.

No comments: