James Tauber

journeyman of some

Python Subversion Binding

Here are some ongoing notes on using the Python bindings to Subversion.

Note that I'm using Subversion 1.3 which handles pools for you. For this reason, what I say below won't work with versions earlier than 1.3.

from svn import fs, repos, core
assert (core.SVN_VER_MAJOR, core.SVN_VER_MINOR) >= (1, 3), "Subversion 1.3 or later required"

General Repository Access

To get a repository object:

repository = repos.open(root_path)

where root_path is the location of the repository on your local filesystem.

The fs module functions take an fs_ptr so you'll want to grab one:

fs_ptr = repos.fs(repository)

You can now get the latest (i.e. youngest) revision number:

youngest_revision_number = fs.youngest_rev(fs_ptr)

It's also possible to get revision-level properties:

property_value = fs.revision_prop(fs_ptr, revision_number, property_name)

Valid property names include "svn:log", "svn:date", "svn:author", etc.

Node Access

To get a node (i.e. file or directory), you first need to get the root of the revision:

root = fs.revision_root(fs_ptr, revision_number)

Then you can get a file stream with:

stream = fs.file_contents(root, path)

where path is the path of the file you want within the repository.

This stream can then be read with:

core.svn_stream_read(stream, length)

and then closed with

core.svn_stream_close(stream)

Alternatively, the stream can be wrapped as a Python file-like object:

core.Stream(stream)

To get a node's properties:

property_dict = fs.node_proplist(root, path)

To get a specific property:

property_value = fs.node_prop(root, path, property_name)

Transactions

To begin a transaction:

txn = fs.begin_txn(fs_ptr, revision)

where revision is the revision to base the changes off.

You then get the root of the transaction:

txn_root = fs.txn_root(txn)

This can then be read and modified as if it were a revision root (see below).

To abort the transaction:

fs.abort_txn(txn)

To commit the transaction:

fs.commit_txn(txn)

This will return a pair of values, the second of which will be the new revision number (or None if the commit failed)

Modifications

Note these all use transaction roots not revision roots.

To create a new (empty) file node:

fs.make_file(txn_root, path)

To delete a node:

fs.delete(txn_root, path)

To change a node's properties:

fs.change_node_prop(txn_root, path, property_name, property_value)

To change a file's content:

stream = fs.apply_text(txt_root, path, None)
core.svn_stream_write(stream, "hello world!\n")
core.svn_stream_close(stream)

Note that this can be done after an fs.make_file to provide the content for a new file.

Unfortunately, the Stream wrapper object doesn't have a close() method which renders it useless for writing.

Categories:
prev « python » next
prev « subversion » next

Comments (7)

David Geller on Feb. 18, 2006:

What about apply_txdelta for changing files? This is done in the sample putfile.py.

David

Larry Maccherone on Feb. 28, 2006:

I'm curious why you used these bindings which only work with Python 2.3 and have little to no documentation instead of the pysvn bindings (also on the Tigris site at: http://pysvn.tigris.org/). I just started a project that called for svn bindings and the first documentation I found was your notes above. When I took those as far as they go, I went looking for me. That's when I found pysvn. pysvn supports Python 2.3 and 2.4 and it has decent documentation. I have only just started looking at it but it looks pretty good. Please email me your reply to larry a@t maccherone d.o.t com.

James Tauber on May 15, 2006:

Larry, I was explicitly told on the pysvn mailing list that it was for svn protocol clients, not for interfacing server software to the repository layer. So I didn't explore it any further.

Tim Coulter on April 1, 2007:

Would you know of a way to get the youngest revision of a given path (i.e., the last revision that modified it) even if the path has been deleted, and you don't know the revision number it was deleted on?

I've got a small problem, and can't seem to figure it out. Any help would be very appreciated.

Anand Patil on Dec. 7, 2007:

Would this package allow me to remove a file from a repository without actually removing it from my working copy, on my local disk?

I use svn to back up code for multi-day simulations. Sometimes I go to back up, but accidentally add the output file of one of the simulations that's currently running. Moving the files to another directory, svn rm'ing them and then moving them back isn't an option in this case.

Googling reveals I'm not the only person who finds this a hassle, so if your package provides a workaround let people know!

MisterPete on April 14, 2008:

Anand, why not just use "svn delete <url>"?
Sorry if I'm misunderstanding your problem.

Kevin Gillette on Aug. 3, 2008:

## To lock files using the swig wrapper,
## you must do the following:

# This block is necessary to associate a
# username with our pointer to the repo
username = 'nobody'
access = fs.create_access(username)
fs.set_access(fs_ptr, access)

# To lock a path
lock_token = fs.generate_lock_token(fs_ptr)
lock_path = 'path/to/file'
lock_comment = "testing the lock mechanism"
is_dav_comment = False
lock_expiration_date = 0
steal_lock = False
lock = fs.lock(fs_ptr, lock_path,
lock_token, lock_comment,
is_dav_comment, lock_expiration,
youngest_revision_number,
steal_lock)

# To unlock again
break_lock = False
fs.unlock(fs_ptr, lock_path
lock_token, break_lock)

"""
Some notes:
I've only found a lock_expiration of 0 to be useful, since all positive integers I've tried have resulted in immediate expirations, including large numbers (like 2000 for relative times, or time.time() + 2000 for absolute times).
"""

Add a Comment

Created: Feb. 8, 2006
Last Modified: Feb. 8, 2006
Author: jtauber