[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: svn commit: r29467 - trunk/tools/examples

From: David Glasser <glasser_at_davidglasser.net>
Date: Thu, 21 Feb 2008 16:17:05 -0500

Just as a note to anyone else who may be confused: this script
requires Python 2.5 (it uses try/except/finally, and a class with an
empty list of base classes).

--dave

On Wed, Feb 20, 2008 at 10:05 PM, <cmpilato_at_tigris.org> wrote:
> Author: cmpilato
> Date: Wed Feb 20 19:05:30 2008
> New Revision: 29467
>
> Log:
> * tools/examples/SvnCLBrowse
> New toy for playing with Subversion changelists graphically.
> Doesn't do much -- crawls working copies for changelists, and can
> display the changelist members and 'svn info' about those members.
> I basically whipped this up to prove to myself that the changelists
> functionality worked via the Python bindings
>
> Mmm... wxWidgets.
>
> Added:
> trunk/tools/examples/SvnCLBrowse (contents, props changed)
>
> Added: trunk/tools/examples/SvnCLBrowse
> URL: http://svn.collab.net/viewvc/svn/trunk/tools/examples/SvnCLBrowse?pathrev=29467
> ==============================================================================
> --- (empty file)
> +++ trunk/tools/examples/SvnCLBrowse Wed Feb 20 19:05:30 2008
> @@ -0,0 +1,491 @@
> +#!/usr/bin/python
> +#
> +# SvnCLBrowse -- graphical Subversion changelist browser
> +#
> +# ====================================================================
> +# Copyright (c) 2008 CollabNet. All rights reserved.
> +#
> +# This software is licensed as described in the file COPYING, which
> +# you should have received as part of this distribution. The terms
> +# are also available at http://subversion.tigris.org/license-1.html.
> +# If newer versions of this license are posted there, you may use a
> +# newer version instead, at your option.
> +#
> +# This software consists of voluntary contributions made by many
> +# individuals. For exact contribution history, see the revision
> +# history and logs, available at http://subversion.tigris.org/.
> +# ====================================================================
> +
> +import sys
> +import os
> +import os.path
> +import time
> +import string
> +
> +# Try to use Gnu's getopt module.
> +import getopt
> +try:
> + my_getopt = getopt.gnu_getopt
> +except AttributeError:
> + my_getopt = getopt.getopt
> +
> +# Try to import the wxWidgets modules.
> +try:
> + import wx
> + import wx.xrc
> +except ImportError:
> + sys.stderr.write("""
> +ERROR: This program requires the wxWidgets Python bindings, which you
> + do not appear to have installed.
> +
> +""")
> + raise
> +
> +# Try to import the Subversion modules.
> +try:
> + import svn.client, svn.wc, svn.core
> +except ImportError:
> + sys.stderr.write("""
> +ERROR: This program requires the Subversion Python bindings, which you
> + do not appear to have installed.
> +
> +""")
> + raise
> +
> +status_code_map = {
> + svn.wc.status_none : ' ',
> + svn.wc.status_normal : ' ',
> + svn.wc.status_added : 'A',
> + svn.wc.status_missing : '!',
> + svn.wc.status_incomplete : '!',
> + svn.wc.status_deleted : 'D',
> + svn.wc.status_replaced : 'R',
> + svn.wc.status_modified : 'M',
> + svn.wc.status_merged : 'G',
> + svn.wc.status_conflicted : 'C',
> + svn.wc.status_obstructed : '~',
> + svn.wc.status_ignored : 'I',
> + svn.wc.status_external : 'X',
> + svn.wc.status_unversioned : '?',
> + }
> +
> +def output_info(path, info, window):
> + window.AppendText("Path: %s\n" % os.path.normpath(path))
> + if info.kind != svn.core.svn_node_dir:
> + window.AppendText("Name: %s\n" % os.path.basename(path))
> + if info.URL:
> + window.AppendText("URL: %s\n" % info.URL)
> + if info.repos_root_URL:
> + window.AppendText("Repository Root: %s\n" % info.repos_root_URL)
> + if info.repos_UUID:
> + window.AppendText("Repository UUID: %s\n" % info.repos_UUID)
> + if info.rev >= 0:
> + window.AppendText("Revision: %ld\n" % info.rev)
> + if info.kind == svn.core.svn_node_file:
> + window.AppendText("Node Kind: file\n")
> + elif info.kind == svn.core.svn_node_dir:
> + window.AppendText("Node Kind: directory\n")
> + elif info.kind == svn.core.svn_node_none:
> + window.AppendText("Node Kind: none\n")
> + else:
> + window.AppendText("Node Kind: unknown\n")
> + if info.has_wc_info:
> + if info.schedule == svn.wc.schedule_normal:
> + window.AppendText("Schedule: normal\n")
> + elif info.schedule == svn.wc.schedule_add:
> + window.AppendText("Schedule: add\n")
> + elif info.schedule == svn.wc.schedule_delete:
> + window.AppendText("Schedule: delete\n")
> + elif info.schedule == svn.wc.schedule_replace:
> + window.AppendText("Schedule: replace\n")
> + if info.depth == svn.core.svn_depth_unknown:
> + pass
> + elif info.depth == svn.core.svn_depth_empty:
> + window.AppendText("Depth: empty\n")
> + elif info.depth == svn.core.svn_depth_files:
> + window.AppendText("Depth: files\n")
> + elif info.depth == svn.core.svn_depth_immediates:
> + window.AppendText("Depth: immediates\n")
> + elif info.depth == svn.core.svn_depth_infinity:
> + pass
> + else:
> + window.AppendText("Depth: INVALID\n")
> + if info.copyfrom_url:
> + window.AppendText("Copied From URL: %s\n" % info.copyfrom_url)
> + if info.copyfrom_rev >= 0:
> + window.AppendText("Copied From Rev: %ld\n" % info.copyfrom_rev)
> + if info.last_changed_author:
> + window.AppendText("Last Changed Author: %s\n" % info.last_changed_author)
> + if info.last_changed_rev >= 0:
> + window.AppendText("Last Changed Rev: %ld\n" % info.last_changed_rev)
> + if info.last_changed_date:
> + window.AppendText("Last Changed Date: %s\n" %
> + svn.core.svn_time_to_human_cstring(info.last_changed_date))
> + if info.has_wc_info:
> + if info.text_time:
> + window.AppendText("Text Last Updated: %s\n" %
> + svn.core.svn_time_to_human_cstring(info.text_time))
> + if info.prop_time:
> + window.AppendText("Properties Last Updated: %s\n" %
> + svn.core.svn_time_to_human_cstring(info.prop_time))
> + if info.checksum:
> + window.AppendText("Checksum: %s\n" % info.checksum)
> + if info.conflict_old:
> + window.AppendText("Conflict Previous Base File: %s\n" % info.conflict_old)
> + if info.conflict_wrk:
> + window.AppendText("Conflict Previous Working File: %s\n" % info.conflict_wrk)
> + if info.conflict_new:
> + window.AppendText("Conflict Current Base File: %s\n" % info.conflict_new)
> + if info.prejfile:
> + window.AppendText("Conflict Properties File: %s\n" % info.prejfile)
> + if info.lock:
> + if info.lock.token:
> + window.AppendText("Lock Token: %s\n" % info.lock.token)
> + if info.lock.owner:
> + window.AppendText("Lock Owner: %s\n" % info.lock.owner)
> + if info.lock.creation_date:
> + window.AppendText("Lock Created: %s\n" %
> + svn.core.svn_time_to_human_cstring(info.lock.creation_date))
> + if info.lock.expiration_date:
> + window.AppendText("Lock Expires: %s\n" %
> + svn.core.svn_time_to_human_cstring(info.lock.expiration_date))
> + if info.lock.comment:
> + num_lines = len(info.lock.comment.split("\n"))
> + window.AppendText("Lock Comment (%d line%s): %s\n"
> + % (num_lines, num_lines > 1 and "s" or "", info.lock.comment))
> + if info.changelist:
> + window.AppendText("Changelist: %s\n" % info.changelist)
> + window.AppendText("\n")
> +
> +class _item():
> + pass
> +
> +class SvnCLBrowse(wx.App):
> + def __init__(self, wc_dir):
> + svn.core.svn_config_ensure(None)
> + self.svn_ctx = svn.client.ctx_t()
> + self.svn_ctx.config = svn.core.svn_config_get_config(None)
> + if wc_dir is not None:
> + self.wc_dir = svn.core.svn_path_canonicalize(wc_dir)
> + else:
> + self.wc_dir = wc_dir
> + wx.App.__init__(self)
> +
> + def OnInit(self):
> + self.SetAppName("SvnCLBrowse")
> +
> + self.xrc = wx.xrc.EmptyXmlResource()
> + wx.FileSystem.AddHandler(wx.MemoryFSHandler())
> + wx.MemoryFSHandler.AddFile('XRC/SvnCLBrowse.xrc', _XML_RESOURCE)
> + self.xrc.Load('memory:XRC/SvnCLBrowse.xrc')
> +
> + # XML Resource stuff.
> + self.resources = _item()
> + self.resources.CLBFrame = self.xrc.LoadFrame(None, 'CLBFrame')
> + self.resources.CLBMenuBar = self.xrc.LoadMenuBar('CLBMenuBar')
> + self.resources.CLBMenuFileQuit = self.xrc.GetXRCID('CLBMenuFileQuit')
> + self.resources.CLBMenuOpsInfo = self.xrc.GetXRCID('CLBMenuOpsInfo')
> + self.resources.CLBMenuOpsMembers = self.xrc.GetXRCID('CLBMenuOpsMembers')
> + self.resources.CLBMenuHelpAbout = self.xrc.GetXRCID('CLBMenuHelpAbout')
> + self.resources.CLBDirNav = self.resources.CLBFrame.FindWindowById(
> + self.xrc.GetXRCID('CLBDirNav'))
> + self.resources.CLBChangelists = self.resources.CLBFrame.FindWindowById(
> + self.xrc.GetXRCID('CLBChangelists'))
> + self.resources.CLBVertSplitter = self.resources.CLBFrame.FindWindowById(
> + self.xrc.GetXRCID('CLBVertSplitter'))
> + self.resources.CLBHorzSplitter = self.resources.CLBFrame.FindWindowById(
> + self.xrc.GetXRCID('CLBHorzSplitter'))
> + self.resources.CLBOutput = self.resources.CLBFrame.FindWindowById(
> + self.xrc.GetXRCID('CLBOutput'))
> + self.resources.CLBStatusBar = self.resources.CLBFrame.CreateStatusBar(2)
> +
> + # Glue some of our extra stuff onto the main frame.
> + self.resources.CLBFrame.SetMenuBar(self.resources.CLBMenuBar)
> + self.resources.CLBStatusBar.SetStatusWidths([-1, 100])
> +
> + # Event handlers. They are the key to the world.
> + wx.EVT_CLOSE(self.resources.CLBFrame, self._FrameClosure)
> + wx.EVT_MENU(self, self.resources.CLBMenuFileQuit, self._FileQuitMenu)
> + wx.EVT_MENU(self, self.resources.CLBMenuOpsInfo, self._OpsInfoMenu)
> + wx.EVT_MENU(self, self.resources.CLBMenuOpsMembers, self._OpsMembersMenu)
> + wx.EVT_MENU(self, self.resources.CLBMenuHelpAbout, self._HelpAboutMenu)
> + wx.EVT_TREE_ITEM_ACTIVATED(self, self.resources.CLBDirNav.GetTreeCtrl().Id,
> + self._DirNavSelChanged)
> +
> + # Reset our working directory
> + self._SetWorkingDirectory(self.wc_dir)
> +
> + # Resize and display our frame.
> + self.resources.CLBFrame.SetSize(wx.Size(600, 400))
> + self.resources.CLBFrame.Center()
> + self.resources.CLBFrame.Show(True)
> + self.resources.CLBVertSplitter.SetSashPosition(
> + self.resources.CLBVertSplitter.GetSize()[0] / 2)
> + self.resources.CLBHorzSplitter.SetSashPosition(
> + self.resources.CLBHorzSplitter.GetSize()[1] / 2)
> +
> + # Tell wxWidgets that this is our main window
> + self.SetTopWindow(self.resources.CLBFrame)
> +
> + # Return a success flag
> + return True
> +
> + def _SetWorkingDirectory(self, wc_dir):
> + if wc_dir is None:
> + return
> + if not os.path.isdir(wc_dir):
> + wc_dir = os.path.abspath('/')
> + self.wc_dir = os.path.abspath(wc_dir)
> + self.resources.CLBChangelists.Clear()
> + self.resources.CLBDirNav.SetPath(self.wc_dir)
> + self.resources.CLBFrame.SetTitle("SvnCLBrowse - %s" % (self.wc_dir))
> + changelists = {}
> + self.resources.CLBFrame.SetStatusText("Checking '%s' for status..." \
> + % (self.wc_dir))
> + wx.BeginBusyCursor()
> +
> + def _status_callback(path, status, clists=changelists):
> + if status.entry and status.entry.changelist:
> + clists[status.entry.changelist] = None
> +
> + # Do the status crawl, using _status_callback() as our callback function.
> + revision = svn.core.svn_opt_revision_t()
> + revision.type = svn.core.svn_opt_revision_head
> + try:
> + svn.client.status2(self.wc_dir, revision, _status_callback,
> + svn.core.svn_depth_infinity,
> + False, False, False, True, self.svn_ctx)
> + except svn.core.SubversionException:
> + self.resources.CLBStatusBar.SetStatusText("UNVERSIONED", 2)
> + else:
> + changelist_names = changelists.keys()
> + changelist_names.sort()
> + for changelist in changelist_names:
> + self.resources.CLBChangelists.Append(changelist)
> + finally:
> + wx.EndBusyCursor()
> + self.resources.CLBFrame.SetStatusText("")
> +
> + def _Destroy(self):
> + self.resources.CLBFrame.Destroy()
> +
> + def _DirNavSelChanged(self, event):
> + self._SetWorkingDirectory(self.resources.CLBDirNav.GetPath())
> +
> + def _GetSelectedChangelists(self):
> + changelists = []
> + items = self.resources.CLBChangelists.GetSelections()
> + for item in items:
> + changelists.append(str(self.resources.CLBChangelists.GetString(item)))
> + return changelists
> +
> + def _OpsMembersMenu(self, event):
> + self.resources.CLBOutput.Clear()
> + changelists = self._GetSelectedChangelists()
> + if not changelists:
> + return
> +
> + def _info_receiver(path, info, pool):
> + self.resources.CLBOutput.AppendText(" %s\n" % (path))
> +
> + for changelist in changelists:
> + self.resources.CLBOutput.AppendText("Changelist: %s\n" % (changelist))
> + revision = svn.core.svn_opt_revision_t()
> + revision.type = svn.core.svn_opt_revision_working
> + svn.client.info2(self.wc_dir, revision, revision,
> + _info_receiver, svn.core.svn_depth_infinity,
> + [changelist], self.svn_ctx)
> + self.resources.CLBOutput.AppendText("\n")
> +
> + def _OpsInfoMenu(self, event):
> + self.resources.CLBOutput.Clear()
> + changelists = self._GetSelectedChangelists()
> + if not changelists:
> + return
> +
> + def _info_receiver(path, info, pool):
> + output_info(path, info, self.resources.CLBOutput)
> +
> + revision = svn.core.svn_opt_revision_t()
> + revision.type = svn.core.svn_opt_revision_working
> + svn.client.info2(self.wc_dir, revision, revision,
> + _info_receiver, svn.core.svn_depth_infinity,
> + changelists, self.svn_ctx)
> +
> + def _FrameClosure(self, event):
> + self._Destroy()
> +
> + def _FileQuitMenu(self, event):
> + self._Destroy()
> +
> + def _HelpAboutMenu(self, event):
> + wx.MessageBox("SvnCLBrowse"
> + " -- graphical Subversion changelist browser.\n\n",
> + "About SvnCLBrowse",
> + wx.OK | wx.CENTER,
> + self.resources.CLBFrame)
> +
> + def OnExit(self):
> + pass
> +
> +
> +_XML_RESOURCE = """<?xml version="1.0" ?>
> +<resource>
> + <object class="wxMenuBar" name="CLBMenuBar">
> + <object class="wxMenu">
> + <label>&amp;File</label>
> + <object class="wxMenuItem" name="CLBMenuFileQuit">
> + <label>&amp;Quit</label>
> + <accel>CTRL+Q</accel>
> + <help>Quit SvnCLBrowse.</help>
> + </object>
> + </object>
> + <object class="wxMenu">
> + <label>&amp;Subversion</label>
> + <object class="wxMenuItem" name="CLBMenuOpsInfo">
> + <label>&amp;Info</label>
> + <help>Show information about members of the selected changelist(s).</help>
> + </object>
> + <object class="wxMenuItem" name="CLBMenuOpsMembers">
> + <label>&amp;Members</label>
> + <help>List the members of the selected changelist(s).</help>
> + </object>
> + </object>
> + <object class="wxMenu">
> + <label>&amp;Help</label>
> + <object class="wxMenuItem" name="CLBMenuHelpAbout">
> + <label>&amp;About...</label>
> + <help>About SvnCLBrowse.</help>
> + </object>
> + </object>
> + </object>
> + <object class="wxFrame" name="CLBFrame">
> + <title>SvnCLBrowse -- graphical Subversion changelist browser</title>
> + <centered>1</centered>
> + <style>wxDEFAULT_FRAME_STYLE|wxCAPTION|wxSYSTEM_MENU|wxRESIZE_BORDER|wxRESIZE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxTAB_TRAVERSAL</style>
> + <object class="wxFlexGridSizer">
> + <cols>1</cols>
> + <rows>1</rows>
> + <object class="sizeritem">
> + <object class="wxSplitterWindow" name="CLBVertSplitter">
> + <object class="wxPanel">
> + <object class="wxFlexGridSizer">
> + <cols>1</cols>
> + <rows>3</rows>
> + <growablecols>0</growablecols>
> + <growablerows>0</growablerows>
> + <growablerows>1</growablerows>
> + <growablerows>2</growablerows>
> + <object class="sizeritem">
> + <object class="wxSplitterWindow" name="CLBHorzSplitter">
> + <orientation>horizontal</orientation>
> + <sashpos>200</sashpos>
> + <minsize>50</minsize>
> + <style>wxSP_NOBORDER|wxSP_LIVE_UPDATE</style>
> + <object class="wxPanel">
> + <object class="wxStaticBoxSizer">
> + <label>Local Modifications</label>
> + <orient>wxHORIZONTAL</orient>
> + <object class="sizeritem">
> + <object class="wxGenericDirCtrl" name="CLBDirNav">
> + <style>wxDIRCTRL_DIR_ONLY</style>
> + </object>
> + <flag>wxEXPAND</flag>
> + <option>1</option>
> + </object>
> + </object>
> + </object>
> + <object class="wxPanel">
> + <object class="wxStaticBoxSizer">
> + <label>Changelists</label>
> + <orient>wxHORIZONTAL</orient>
> + <object class="sizeritem">
> + <object class="wxListBox" name="CLBChangelists">
> + <content>
> + <item/></content>
> + <style>wxLB_MULTIPLE</style>
> + </object>
> + <option>1</option>
> + <flag>wxALL|wxEXPAND</flag>
> + </object>
> + </object>
> + </object>
> + </object>
> + <flag>wxEXPAND</flag>
> + <option>1</option>
> + </object>
> + </object>
> + </object>
> + <object class="wxPanel">
> + <object class="wxFlexGridSizer">
> + <cols>1</cols>
> + <object class="sizeritem">
> + <object class="wxStaticBoxSizer">
> + <label>Output</label>
> + <orient>wxVERTICAL</orient>
> + <object class="sizeritem">
> + <object class="wxTextCtrl" name="CLBOutput">
> + <style>wxTE_MULTILINE|wxTE_READONLY|wxTE_LEFT|wxTE_DONTWRAP</style>
> + </object>
> + <option>1</option>
> + <flag>wxEXPAND</flag>
> + </object>
> + </object>
> + <option>1</option>
> + <flag>wxALL|wxEXPAND</flag>
> + <border>5</border>
> + </object>
> + <rows>1</rows>
> + <growablecols>0</growablecols>
> + <growablerows>0</growablerows>
> + </object>
> + </object>
> + <orientation>vertical</orientation>
> + <sashpos>130</sashpos>
> + <minsize>50</minsize>
> + <style>wxSP_NOBORDER|wxSP_LIVE_UPDATE</style>
> + </object>
> + <option>1</option>
> + <flag>wxEXPAND</flag>
> + </object>
> + <growablecols>0</growablecols>
> + <growablerows>0</growablerows>
> + </object>
> + </object>
> +</resource>
> +"""
> +
> +def usage_and_exit(errmsg=None):
> + stream = errmsg and sys.stderr or sys.stdout
> + progname = os.path.basename(sys.argv[0])
> + stream.write("""%s -- graphical Subversion changelist browser
> +
> +Usage: %s [DIRECTORY]
> +
> +Launch the SvnCLBrowse graphical changelist browser, using DIRECTORY
> +(or the current working directory, if DIRECTORY is not provided) as
> +the initial browse location.
> +
> +""" % (progname, progname))
> + if errmsg:
> + stream.write("ERROR: %s\n" % (errmsg))
> + sys.exit(errmsg and 1 or 0)
> +
> +def main():
> + opts, args = my_getopt(sys.argv[1:], 'h?', ['help'])
> + for name, value in opts:
> + if name == '-h' or name == '-?' or name == '--help':
> + usage_and_exit()
> + argc = len(args)
> + if argc == 0:
> + wc_dir = '.'
> + elif argc == 1:
> + wc_dir = sys.argv[1]
> + else:
> + usage_and_exit("Too many arguments")
> + app = SvnCLBrowse(wc_dir)
> + app.MainLoop()
> + app.OnExit()
> +
> +if __name__ == "__main__":
> + main()
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: svn-unsubscribe_at_subversion.tigris.org
> For additional commands, e-mail: svn-help_at_subversion.tigris.org
>
>

-- 
David Glasser | glasser@davidglasser.net | http://www.davidglasser.net/
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe_at_subversion.tigris.org
For additional commands, e-mail: dev-help_at_subversion.tigris.org
Received on 2008-02-21 22:17:22 CET

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.