
First, the test setup: we'll set up a repository and local checkout.
Sven won't do this for you:

   >>> repo_dir = '/tmp/svensvn/'; checkout_dir = '/tmp/svencheckout'; other_checkoutdir = '/tmp/svenothercheckout'
   >>> import subprocess
   >>> subprocess.call(['rm', '-rf', repo_dir, checkout_dir, other_checkoutdir])
   0
   >>> subprocess.call(['svnadmin', 'create', repo_dir])
   0
   >>> subprocess.call(['svn', 'co', 'file://%s'%repo_dir, checkout_dir])
   0

Instantiate an object that knows about your repository:

   >>> from sven.backend import SvnAccess
   >>> client = SvnAccess(repo_dir, checkout_dir)

Sven throws its own flavors of IOErrors to deal with various edge cases of
interacting with the repository. For instance, trying to get metadata on
a resource that doesn't exist:

   >>> client.last_changed_rev('/path/to/file')
   Traceback (most recent call last):
   ...
   NoSuchResource: [Errno 2] No such file or directory: 'path/to/file'

The same error is thrown if you're looking for a particular revision:

   >>> client.last_changed_rev('/path/to/file', rev=4)
   Traceback (most recent call last):
   ...
   NoSuchResource: [Errno 2] No such file or directory: 'path/to/file'

Or if you're trying to get the contents of a resource:

   >>> client.read('/path/to/file')
   Traceback (most recent call last):
   ...
   NoSuchResource: [Errno 2] No such file or directory: 'path/to/file'

   >>> client.read('/path/to/file', rev=4)
   Traceback (most recent call last):
   ...
   NoSuchResource: [Errno 2] No such file or directory: 'path/to/file'

Now let's actually save a file. Sven's workflow encourages you to think
of your repository as a filesystem; you write to a file and commit it
to the repository in a single step:
   
   >>> client.write('file', "first version")

Sven will create all necessary directories based on the resource path[1]:

   >>> client.write('/path/to/file', "a second versioned content")

Commit changes to a file in the same way as saving a new file:

   >>> client.write('/file', "second version")

Now that we have some resources, we can finally start reading them too:

   >>> client.read('file')
   {'body': 'second version\n', 'kind': None}

And we can read previous versions as well, by passing in the desired
global revision number:

   >>> client.read('file', rev=1)
   {'body': 'first version\n', 'kind': None}

Sven will throw a different flavor of IOError if you try to read content
from a path that exists, but is a directory instead of a file:

   >>> client.read('/path')
   Traceback (most recent call last):
   ...
   NotAFile: [Errno 21] Is a directory: 'path'

To read the most recent version of a resource, don't pass in any revision:

   >>> client.read('path/to/file')
   {'body': 'a second versioned content\n', 'kind': None}

But if you do pass in a revision, sven will tell you if the file was unchanged
in that revision, with a custom exception:

   >>> client.read('path/to/file', rev=4)
   Traceback (most recent call last):
   ...
   ResourceUnchanged: Resource 'path/to/file' unchanged since 3

This ResourceUnchaged exception contains the revision number of the last
change made to the file:

   >>> from sven.exc import ResourceUnchanged
   >>> try:
   ...     client.read('path/to/file', rev=4)
   ... except ResourceUnchanged, e:
   ...     print "last changed at r%d" % e.last_change
   last changed at r3
   
   >>> client.read('path/to/file', rev=3)
   {'body': 'a second versioned content\n', 'kind': None}

You cannot overwrite directories with files, of course:

   >>> client.write('/path/to', "i'm gonna clobber this directory with a file!")
   Traceback (most recent call last):
   ...
   NotAFile: [Errno 21] Is a directory: 'path/to'

When writing a file, you can pass in a commit message, and you can also
set the file's svn:mime-type property:

   >>> client.write('file', "now with metadata",
   ...              msg="Changed the content",
   ...              kind="text/plain")

   >>> client.read('file')
   {'body': 'now with metadata\n', 'kind': 'text/plain'}

Great work!

[1] "Sven will create all necessary directories based on the resource path"
This is actually tricky -- so let's exercise it in a couple of cases:

   >>> client.write('a_file_at_the_root', "foo")
   >>> client.write('one_level/deep', "foo")
   >>> client.write('one_level/again', "foo")
   >>> client.write('totally/nonexistent/path_to/a_file', "foo")
   >>> client.write('totally/partly/extant_path/to_a_file', "foo")
   >>> client.write('totally/partly/extant/path/to_another_file', "foo")
   >>> client.write('a_file_at_the_root', "foobar")
   >>> client.write('one_level/deep', "foobar")
   >>> client.write('one_level/again', "foobar")
   >>> client.write('totally/nonexistent/path_to/a_file', "foobar")
   >>> client.write('totally/partly/extant_path/to_a_file', "foobar")
   >>> client.write('totally/partly/extant/path/to_another_file', "foobar")

Sven tries to think of your repository-backed filesystem as basically
stateless -- a sort of RESTful approach to versioning. Because of this,
sven tries to make version conflicts just sort of melt away by always
clobbering the repository's copy with the client's new copy:

   >>> subprocess.call(['svn', 'co', 'file://%s'%repo_dir, other_checkoutdir])
   0

   >>> client2 = SvnAccess(repo_dir, other_checkoutdir)
   >>> client.write('file', "version 1\n2\n3")
   >>> client2.write('file', 'feeble feeblefeed')
   >>> client.write('file', "version 1\n5\n3")

   >>> client.read('file')['body']
   'version 1\n5\n3\n'

   client2.read('file')['body']
   'feeble feeblefeed\n'

Synchronization errors can still occur if you tell sven not to
keep files up-to-date before writing, though:

   >>> from sven.exc import ResourceChanged
   >>> client2.write('file', 'moobly moo', update_before_write=False)
   Traceback (most recent call last):
   ...
   ResourceChanged: Resource 'file' is out of date

   >>> client.write('file', "version 1\n2\n3", update_before_write=False)

So there are a couple of approaches possible here if you prefer to keep
auto-updates off for performance reasons. One simple approach would be
to assume that writes will succeed until proven otherwise, while retaining
sven's natural inclination to clobber conflicting changesets, viz.:

   >>> try:
   ...     client2.write('file', 'booga booga boo', update_before_write=False)
   ... except ResourceChanged:
   ...     client2.write('file', 'booga booga boo', update_before_write=True)


