subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1541558 - /subversion/trunk/tools/examples/svnlook.py
Date Wed, 13 Nov 2013 15:34:25 GMT
Author: julianfoad
Date: Wed Nov 13 15:34:25 2013
New Revision: 1541558

URL: http://svn.apache.org/r1541558
Log:
Make 'svnlook.py' usable as a library by adding getter methods.

See the email thread "[PATCH] svnlook.py: Make it usable as a library", from
anatoly to dev@, initially on 2012-05-28 [1] with no response, followed up
starting on 2013-11-09 [2].
  [1] e.g. <http://svn.haxx.se/dev/archive-2012-05/0547.shtml>
  [2] e.g. <http://svn.haxx.se/dev/archive-2013-11/0039.shtml>

Patch by: anatoly techtonik <techtonik{_AT_}gmail.com>

* tools/examples/svnlook.py
  (SVNLook): Convert to new-style class. Change order of initializer
    arguments and make some of them optional, to allow using the class
    without immediately running a print command, and for convenience.
    Add getter methods and adjust existing cmd_* methods to use them.
  (DirsChangedEditor, ChangedEditor): Accept a callback and call it
    instead of directly printing the results.
  (Editor, DiffEditor): Accept a callback, for compatibility, but do not
    call it. These would need to be updated to make the 'tree' and 'diff'
    functionality available.

Modified:
    subversion/trunk/tools/examples/svnlook.py

Modified: subversion/trunk/tools/examples/svnlook.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/examples/svnlook.py?rev=1541558&r1=1541557&r2=1541558&view=diff
==============================================================================
--- subversion/trunk/tools/examples/svnlook.py (original)
+++ subversion/trunk/tools/examples/svnlook.py Wed Nov 13 15:34:25 2013
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# svnlook.py : a Python-based replacement for svnlook
+# svnlook.py : alternative svnlook in Python with library API
 #
 ######################################################################
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -22,18 +22,52 @@
 ######################################################################
 #
 
+"""
+svnlook.py can also be used as a Python module::
+
+  >>> import svnlook
+  >>> svnlook = svnlook.SVNLook("/testrepo")
+  >>> svnlook.get_author()
+  'randomjoe'
+
+
+Accessible API::
+
+[x] author
+[x] changed
+[x] date
+[ ] diff
+[x] dirs-changed
+[ ] ids
+[x] info
+[x] log
+[ ] tree
+---
+[ ] generator API to avoid passing lists
+"""
+
+
 import sys
 import time
 import os
 
 from svn import core, fs, delta, repos
 
-class SVNLook:
-  def __init__(self, path, cmd, rev, txn):
+class SVNLook(object):
+  def __init__(self, path, rev=None, txn=None, cmd=None):
+    """
+    path  - path to repository
+    rev   - revision number
+    txn   - path to transaction (usually to the one about to be committed)
+    cmd   - if set, specifies cmd_* method to execute
+
+    if both rev and txn params are empty, inspect latest committed revision
+    """
     path = core.svn_path_canonicalize(path)
     repos_ptr = repos.open(path)
     self.fs_ptr = repos.fs(repos_ptr)
 
+    # if set, txn takes precendence
     if txn:
       self.txn_ptr = fs.open_txn(self.fs_ptr, txn)
     else:
@@ -42,67 +76,114 @@ class SVNLook:
         rev = fs.youngest_rev(self.fs_ptr)
     self.rev = rev
 
-    getattr(self, 'cmd_' + cmd)()
+    if cmd != None:
+      getattr(self, 'cmd_' + cmd)()
 
   def cmd_default(self):
     self.cmd_info()
     self.cmd_tree()
 
   def cmd_author(self):
-    # get the author property, or empty string if the property is not present
-    author = self._get_property(core.SVN_PROP_REVISION_AUTHOR) or ''
-    print(author)
+    print(self.get_author() or '')
 
   def cmd_changed(self):
-    self._print_tree(ChangedEditor, pass_root=1)
+    for status, path in self.get_changed():
+      print("%-3s %s" % (status, path))
 
   def cmd_date(self):
-    if self.txn_ptr:
+    secs = self.get_date(unixtime=True)
+    if secs is None:
       print("")
     else:
-      date = self._get_property(core.SVN_PROP_REVISION_DATE)
-      if date:
-        aprtime = core.svn_time_from_cstring(date)
-        # ### convert to a time_t; this requires intimate knowledge of
-        # ### the apr_time_t type
-        secs = aprtime / 1000000  # aprtime is microseconds; make seconds
-
-        # assume secs in local TZ, convert to tuple, and format
-        ### we don't really know the TZ, do we?
-        print(time.strftime('%Y-%m-%d %H:%M', time.localtime(secs)))
-      else:
-        print("")
+      # assume secs in local TZ, convert to tuple, and format
+      ### we don't really know the TZ, do we?
+      print(time.strftime('%Y-%m-%d %H:%M', time.localtime(secs)))
 
   def cmd_diff(self):
     self._print_tree(DiffEditor, pass_root=1)
 
   def cmd_dirs_changed(self):
-    self._print_tree(DirsChangedEditor)
+    for dir in self.get_changed_dirs():
+      print(dir)
 
   def cmd_ids(self):
     self._print_tree(Editor, base_rev=0, pass_root=1)
 
   def cmd_info(self):
+    """print the author, data, log_size, and log message"""
     self.cmd_author()
     self.cmd_date()
-    self.cmd_log(1)
-
-  def cmd_log(self, print_size=0):
-    # get the log property, or empty string if the property is not present
-    log = self._get_property(core.SVN_PROP_REVISION_LOG) or ''
-    if print_size:
-      print(len(log))
+    log = self.get_log() or ''
+    print(len(log))
     print(log)
 
+  def cmd_log(self):
+    print(self.get_log() or '')
+
   def cmd_tree(self):
     self._print_tree(Editor, base_rev=0)
 
+
+  # --- API getters
+  def get_author(self):
+    """return string with the author name or None"""
+    return self._get_property(core.SVN_PROP_REVISION_AUTHOR)
+
+  def get_changed(self):
+    """return list of tuples (status, path)"""
+    ret = []
+    def list_callback(status, path):
+      ret.append( (status, path) )
+    self._walk_tree(ChangedEditor, pass_root=1, callback=list_callback)
+    return ret
+
+  def get_date(self, unixtime=False):
+    """return commit timestamp in RFC 3339 format (2010-02-08T20:37:25.195000Z)
+       if unixtime is True, return unix timestamp
+       return None for a txn, or if date property is not set
+    """
+    if self.txn_ptr:
+      return None
+
+    date = self._get_property(core.SVN_PROP_REVISION_DATE)
+    if not unixtime or date == None:
+      return date
+
+    # convert to unix time
+    aprtime = core.svn_time_from_cstring(date)
+    # ### convert to a time_t; this requires intimate knowledge of
+    # ### the apr_time_t type
+    secs = aprtime / 1000000  # aprtime is microseconds; make seconds
+    return secs
+
+  def get_changed_dirs(self):
+    """return list of changed dirs
+       dir names end with trailing forward slash even on windows
+    """
+    dirlist = []
+    def list_callback(item):
+      dirlist.append(item)
+    self._walk_tree(DirsChangedEditor, callback=list_callback)
+    return dirlist
+
+  def get_log(self):
+    """return log message string or None if not present"""
+    return self._get_property(core.SVN_PROP_REVISION_LOG)
+
+
+  # --- Internal helpers
   def _get_property(self, name):
     if self.txn_ptr:
       return fs.txn_prop(self.txn_ptr, name)
     return fs.revision_prop(self.fs_ptr, self.rev, name)
 
   def _print_tree(self, e_factory, base_rev=None, pass_root=0):
+    def print_callback(msg):
+       print(msg)
+    self._walk_tree(e_factory, base_rev, pass_root, callback=print_callback)
+
+  # svn fs, delta, repos calls needs review according to DeltaEditor documentation
+  def _walk_tree(self, e_factory, base_rev=None, pass_root=0, callback=None):
     if base_rev is None:
       # a specific base rev was not provided. use the transaction base,
       # or the previous revision
@@ -120,10 +201,13 @@ class SVNLook:
     # the base of the comparison
     base_root = fs.revision_root(self.fs_ptr, base_rev)
 
+    if callback == None:
+      callback = lambda msg: None
+
     if pass_root:
-      editor = e_factory(root, base_root)
+      editor = e_factory(root, base_root, callback)
     else:
-      editor = e_factory()
+      editor = e_factory(callback=callback)
 
     # construct the editor for printing these things out
     e_ptr, e_baton = delta.make_editor(editor)
@@ -135,8 +219,17 @@ class SVNLook:
 		    e_ptr, e_baton, authz_cb, 0, 1, 0, 0)
 
 
+# ---------------------------------------------------------
+# Delta Editors. For documentation see:
+# http://subversion.apache.org/docs/community-guide/#docs
+
+# this one doesn't process delete_entry, change_dir_prop, apply_text_delta,
+# change_file_prop, close_file, close_edit, abort_edit
+# ?set_target_revision
+# need review
 class Editor(delta.Editor):
-  def __init__(self, root=None, base_root=None):
+  def __init__(self, root=None, base_root=None, callback=None):
+    """callback argument is unused for this editor"""
     self.root = root
     # base_root ignored
 
@@ -172,7 +265,14 @@ class Editor(delta.Editor):
       return ' <%s>' % fs.unparse_id(id)
     return ''
 
+# doesn't process close_directory, apply_text_delta,
+# change_file_prop, close_file, close_edit, abort_edit
+# ?set_target_revision
 class DirsChangedEditor(delta.Editor):
+  """print names of changed dirs, callback(dir) is a printer function"""
+  def __init__(self, callback):
+    self.callback = callback
+
   def open_root(self, base_revision, dir_pool):
     return [ 1, '' ]
 
@@ -201,13 +301,15 @@ class DirsChangedEditor(delta.Editor):
   def _dir_changed(self, baton):
     if baton[0]:
       # the directory hasn't been printed yet. do it.
-      print(baton[1] + '/')
+      self.callback(baton[1] + '/')
       baton[0] = 0
 
 class ChangedEditor(delta.Editor):
-  def __init__(self, root, base_root):
+  def __init__(self, root, base_root, callback):
+    """callback(status, path) is a printer function"""
     self.root = root
     self.base_root = base_root
+    self.callback = callback
 
   def open_root(self, base_revision, dir_pool):
     return [ 1, '' ]
@@ -215,13 +317,13 @@ class ChangedEditor(delta.Editor):
   def delete_entry(self, path, revision, parent_baton, pool):
     ### need more logic to detect 'replace'
     if fs.is_dir(self.base_root, '/' + path):
-      print('D   ' + path + '/')
+      self.callback('D', path + '/')
     else:
-      print('D   ' + path)
+      self.callback('D', path)
 
   def add_directory(self, path, parent_baton,
                     copyfrom_path, copyfrom_revision, dir_pool):
-    print('A   ' + path + '/')
+    self.callback('A', path + '/')
     return [ 0, path ]
 
   def open_directory(self, path, parent_baton, base_revision, dir_pool):
@@ -230,12 +332,12 @@ class ChangedEditor(delta.Editor):
   def change_dir_prop(self, dir_baton, name, value, pool):
     if dir_baton[0]:
       # the directory hasn't been printed yet. do it.
-      print('_U  ' + dir_baton[1] + '/')
+      self.callback('_U', dir_baton[1] + '/')
       dir_baton[0] = 0
 
   def add_file(self, path, parent_baton,
                copyfrom_path, copyfrom_revision, file_pool):
-    print('A   ' + path)
+    self.callback('A', path)
     return [ '_', ' ', None ]
 
   def open_file(self, path, parent_baton, base_revision, file_pool):
@@ -257,11 +359,12 @@ class ChangedEditor(delta.Editor):
       status = text_mod + prop_mod
       # was there some kind of change?
       if status != '_ ':
-        print(status + '  ' + path)
+        self.callback(status.rstrip(), path)
 
 
 class DiffEditor(delta.Editor):
-  def __init__(self, root, base_root):
+  def __init__(self, root, base_root, callback=None):
+    """callback argument is unused for this editor"""
     self.root = root
     self.base_root = base_root
     self.target_revision = 0
@@ -435,7 +538,7 @@ def main():
   if not hasattr(SVNLook, 'cmd_' + cmd):
     usage(1)
 
-  SVNLook(sys.argv[1], cmd, rev, txn)
+  SVNLook(sys.argv[1], rev, txn, cmd)
 
 if __name__ == '__main__':
   main()



Mime
View raw message