[svnbook commit] r1392 - in trunk/src/en: . book
cmpilato
svnbook-dev at red-bean.com
Tue May 31 12:32:31 CDT 2005
Author: cmpilato
Date: Tue May 31 12:32:30 2005
New Revision: 1392
Modified:
trunk/src/en/TODO
trunk/src/en/book/ch08.xml
Log:
* src/en/book/ch08.xml
Update the Python bindings examples with some real (and really working) code.
* src/en/TODO
Finish off a TODO item.
Modified: trunk/src/en/TODO
==============================================================================
--- trunk/src/en/TODO (original)
+++ trunk/src/en/TODO Tue May 31 12:32:30 2005
@@ -65,8 +65,6 @@
- Update status output (adding extra cols) (everywhere in the
whole book)
- * Python bindings example (ch8) is very out of date.
-
============================================================================
NEEDED SOMETIME (NOT VERSION-SPECIFIC)
============================================================================
Modified: trunk/src/en/book/ch08.xml
==============================================================================
--- trunk/src/en/book/ch08.xml (original)
+++ trunk/src/en/book/ch08.xml Tue May 31 12:32:30 2005
@@ -944,97 +944,204 @@
library suite, combined with a powerful, flexible binding
language, is so appealing.</para>
- <para>Let's look at an example that uses Subversion's Python
- SWIG bindings. Our example will do the same thing as our last
- example. Note the difference in size and complexity of the
- function this time!</para>
+ <para>Let's look at a sample program that uses Subversion's
+ Python SWIG bindings to recursively crawl the youngest
+ repository revision, and print the various paths reached
+ during the crawl.</para>
<example id="svn.developer.usingapi.otherlangs.ex-1">
<title>Using the Repository Layer with Python</title>
<programlisting>
-from svn import fs
+#!/usr/bin/python
+
+"""Crawl a repository, printing versioned object path names."""
+
+import sys
import os.path
+import svn.fs, svn.core, svn.repos
+
+def crawl_filesystem_dir(root, directory, pool):
+ """Recursively crawl DIRECTORY under ROOT in the filesystem, and return
+ a list of all the paths at or below DIRECTORY. Use POOL for all
+ allocations."""
+
+ # Print the name of this path.
+ print directory + "/"
+
+ # Get the directory entries for DIRECTORY.
+ entries = svn.fs.svn_fs_dir_entries(root, directory, pool)
+
+ # Use an iteration subpool.
+ subpool = svn.core.svn_pool_create(pool)
+
+ # Loop over the entries.
+ names = entries.keys()
+ for name in names:
+ # Clear the iteration subpool.
+ svn.core.svn_pool_clear(subpool)
+
+ # Calculate the entry's full path.
+ full_path = directory + '/' + name
+
+ # If the entry is a directory, recurse. The recursion will return
+ # a list with the entry and all its children, which we will add to
+ # our running list of paths.
+ if svn.fs.svn_fs_is_dir(root, full_path, subpool):
+ crawl_filesystem_dir(root, full_path, subpool)
+ else:
+ # Else it's a file, so print its path here.
+ print full_path
+
+ # Destroy the iteration subpool.
+ svn.core.svn_pool_destroy(subpool)
+
+def crawl_youngest(pool, repos_path):
+ """Open the repository at REPOS_PATH, and recursively crawl its
+ youngest revision."""
+
+ # Open the repository at REPOS_PATH, and get a reference to its
+ # versioning filesystem.
+ repos_obj = svn.repos.svn_repos_open(repos_path, pool)
+ fs_obj = svn.repos.svn_repos_fs(repos_obj)
-def crawl_filesystem_dir (root, directory, pool):
- """Recursively crawl DIRECTORY under ROOT in the filesystem, and return
- a list of all the paths at or below DIRECTORY. Use POOL for all
- allocations."""
-
- # Get the directory entries for DIRECTORY.
- entries = fs.dir_entries(root, directory, pool)
-
- # Initialize our returned list with the directory path itself.
- paths = [directory]
-
- # Loop over the entries
- names = entries.keys()
- for name in names:
- # Calculate the entry's full path.
- full_path = os.path.join(basepath, name)
-
- # If the entry is a directory, recurse. The recursion will return
- # a list with the entry and all its children, which we will add to
- # our running list of paths.
- if fs.is_dir(fsroot, full_path, pool):
- subpaths = crawl_filesystem_dir(root, full_path, pool)
- paths.extend(subpaths)
-
- # Else, it is a file, so add the entry's full path to the FILES list.
- else:
- paths.append(full_path)
+ # Query the current youngest revision.
+ youngest_rev = svn.fs.svn_fs_youngest_rev(fs_obj, pool)
+
+ # Open a root object representing the youngest (HEAD) revision.
+ root_obj = svn.fs.svn_fs_revision_root(fs_obj, youngest_rev, pool)
- return paths
+ # Do the recursive crawl.
+ crawl_filesystem_dir(root_obj, "", pool)
+
+if __name__ == "__main__":
+ # Check for sane usage.
+ if len(sys.argv) != 2:
+ sys.stderr.write("Usage: %s REPOS_PATH\n"
+ % (os.path.basename(sys.argv[0])))
+ sys.exit(1)
+
+ # Call the app-wrapper, which takes care of APR initialization/shutdown
+ # and the creation and cleanup of our top-level memory pool.
+ svn.core.run_app(crawl_youngest, os.path.normpath(sys.argv[1]))
</programlisting>
</example>
- <para>An implementation in C of the previous example would
- stretch on quite a bit longer. The same routine in C would
- need to pay close attention to memory usage, and need to use
- custom datatypes for representing the hash of entries and the
- list of paths. Python has hashes (called
- <quote>dictionaries</quote>) and lists as built-in datatypes,
- and provides a wonderful selection of methods for operating on
- those types. And since Python uses reference counting and
- garbage collection, users of the language don't have to bother
- themselves with allocating and freeing memory.</para>
-
- <para>In the previous section of this chapter, we mentioned the
- <filename>libsvn_client</filename> interface, and how it
- exists for the sole purpose of simplifying the process of
- writing a Subversion client. The following is a brief example
- of how that library can be accessed via the SWIG bindings. In
- just a few lines of Python, you can check out a fully
- functional Subversion working copy!</para>
+ <para>This same program in C would need to deal with custom
+ datatypes (such as those provided by the APR library) for
+ representing the hash of entries and the list of paths, but
+ Python has hashes (called <quote>dictionaries</quote>) and
+ lists as built-in datatypes, and provides a rich collection of
+ functions for operating on those types. So SWIG (with the
+ help of some customizations in Subversion's language bindings
+ layer) takes care of mapping those custom datatypes into the
+ native datatypes of the target language. This provides a more
+ intuitive interface for users of that language.</para>
+
+ <para>The Subversion Python bindings can be used for working
+ copy operations, too. In the previous section of this
+ chapter, we mentioned the <filename>libsvn_client</filename>
+ interface, and how it exists for the sole purpose of
+ simplifying the process of writing a Subversion client. The
+ following is a brief example of how that library can be
+ accessed via the SWIG bindings to recreate a scaled-down
+ version of the <command>svn status</command> command.</para>
<example id="svn.developer.usingapi.otherlangs.ex-2">
- <title>A Simple Script to Check Out a Working Copy.</title>
+ <title>A Python Status Crawler</title>
<programlisting>
#!/usr/bin/env python
-import sys
-from svn import util, _util, _client
-def usage():
- print "Usage: " + sys.argv[0] + " URL PATH\n"
- sys.exit(0)
-
-def run(url, path):
- # Initialize APR and get a POOL.
- _util.apr_initialize()
- pool = util.svn_pool_create(None)
-
- # Checkout the HEAD of URL into PATH (silently)
- _client.svn_client_checkout(None, None, url, path, -1, 1, None, pool)
-
- # Cleanup our POOL, and shut down APR.
- util.svn_pool_destroy(pool)
- _util.apr_terminate()
+"""Crawl a working copy directory, printing status information."""
+import sys
+import os.path
+import getopt
+import svn.core, svn.client, svn.wc
+
+def generate_status_code(status):
+ """Translate a status value into a single-character status code,
+ using the same logic as the Subversion command-line client."""
+
+ if status == svn.wc.svn_wc_status_none:
+ return ' '
+ if status == svn.wc.svn_wc_status_normal:
+ return ' '
+ if status == svn.wc.svn_wc_status_added:
+ return 'A'
+ if status == svn.wc.svn_wc_status_missing:
+ return '!'
+ if status == svn.wc.svn_wc_status_incomplete:
+ return '!'
+ if status == svn.wc.svn_wc_status_deleted:
+ return 'D'
+ if status == svn.wc.svn_wc_status_replaced:
+ return 'R'
+ if status == svn.wc.svn_wc_status_modified:
+ return 'M'
+ if status == svn.wc.svn_wc_status_merged:
+ return 'G'
+ if status == svn.wc.svn_wc_status_conflicted:
+ return 'C'
+ if status == svn.wc.svn_wc_status_obstructed:
+ return '~'
+ if status == svn.wc.svn_wc_status_ignored:
+ return 'I'
+ if status == svn.wc.svn_wc_status_external:
+ return 'X'
+ if status == svn.wc.svn_wc_status_unversioned:
+ return '?'
+ return '?'
+
+def do_status(pool, wc_path, verbose):
+ # Calculate the length of the input working copy path.
+ wc_path_len = len(wc_path)
+
+ # Build a client context baton.
+ ctx = svn.client.svn_client_ctx_t()
+
+ def _status_callback(path, status, root_path_len=wc_path_len):
+ """A callback function for svn_client_status."""
+
+ # Print the path, minus the bit that overlaps with the root of
+ # the status crawl
+ text_status = generate_status_code(status.text_status)
+ prop_status = generate_status_code(status.prop_status)
+ print '%s%s %s' % (text_status, prop_status, path[wc_path_len + 1:])
+
+ # Do the status crawl, using _status_callback() as our callback function.
+ svn.client.svn_client_status(wc_path, None, _status_callback,
+ 1, verbose, 0, 0, ctx, pool)
+
+def usage_and_exit(errorcode):
+ """Print usage message, and exit with ERRORCODE."""
+ stream = errorcode and sys.stderr or sys.stdout
+ stream.write("""Usage: %s OPTIONS WC-PATH
+Options:
+ --help, -h : Show this usage message
+ --verbose, -v : Show all statuses, even uninteresting ones
+""" % (os.path.basename(sys.argv[0])))
+ sys.exit(errorcode)
+
if __name__ == '__main__':
- if len(sys.argv) != 3:
- usage()
- run(sys.argv[1], sys.argv[2])
+ # Parse command-line options.
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "verbose"])
+ except getopt.GetoptError:
+ usage_and_exit(1)
+ verbose = 0
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ usage_and_exit(0)
+ if opt in ("-v", "--verbose"):
+ verbose = 1
+ if len(args) != 1:
+ usage_and_exit(2)
+
+ # Call the app-wrapper, which takes care of APR initialization/shutdown
+ # and the creation and cleanup of our top-level memory pool.
+ svn.core.run_app(do_status, os.path.normpath(args[0]), verbose)
</programlisting>
</example>
More information about the svnbook-dev
mailing list