Entries from January 2005 ↓

P4Python – Python interface to Perforce via API

I recently released P4Python – an interface to Perforce via it’s C++ API for Python. Fortunately I didn’t have to start from scratch. First I used the model for P4Ruby andP4Perl by Tony Smith. Then I was able to build on the original Python work by Mike Meyer (see license for link to his original).

Writing Python extensions in C++ (as this had to be because of the Perforce API) is a bit of a palaver due to reference counting etc. I had a look round at various toolkits such as SWIG, and Boost.Python. However, I wanted to make it as easy as possible for other people to download and build, so in the end decided to evolve what Mike had originally done. I quickly wrapped it in distutils, so that installation (when compile/build is required), should be just a case of:

python setup.py install

It might be necessary to hack setup.py to set a couple of perforce specific compile/link flags and point at the appropriate API. It is easy to provide some prebuilt Windows binaries.

The implementation turned out to be pretty straightforward. One of the neat tricks Mike had done which I continued gratefully was to push quite a bit of processing out from C++ into a Python wrapper module which made the code a whole lot easier to write and test. I discovered a few tricks to achieve the equivalent of Ruby’s method_missing magic, such as using __getattr__ and the like along the way. Together with a nice test harness (unfortunately I can’t release the Perforce repository which it assumes is there but principles are the same) it should hopefully be nice and easy to maintain.

All in all quite a fun project. As a final validation, I include in the download a simple performance testing script which compares 50 runs of the “p4 info” command. The average time taken to run it via the new interface is 0.0013 seconds, whereas spawning a p4.exe and parsing the output takes 0.2297 seconds (1,700% increase!). This may not be terribly significant when executing a few commands (where the server time taken is likely to be much longer than the overhead), but is nice to have in a variety of situations. One particular area is the writing of Perforce triggers themselves – nice to be able to do these in Python at speed!

Note that I copied the P4Ruby idiom and raise exceptions etc leading to nice clean code. From the web page:

  from p4 import P4

  template = "my-client-template"
  client_root = r"c:\work\my-root"

  p4 = P4()
  p4.parse_forms()
  p4.connect()

  try:
      # Run a "p4 client -t template -o" and convert it into a Python dictionary
      spec = p4.fetch_client("-t", template)

      # Now edit the fields in the form
      spec["Root"] = client_root

      # Now save the udpated spec and sync it
      p4.save_client(spec)
      p4.run_sync()

  except:
    # If any errors occur, we'll jump in here. Just log them
    # and raise the exception up to the higher level
    for e in p4.errors:
        print e

As a final exercise, I have just published it to PyPI the Python Package Index (www.python.org/pypi).