
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';
   >>> import subprocess
   >>> subprocess.call(['rm', '-rf', repo_dir, checkout_dir])
   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")

