Chuckmove: a tool for reorganizing Python source trees
diff --git a/xos/tools/chuckmove b/xos/tools/chuckmove
new file mode 100755
index 0000000..09d9f4e
--- /dev/null
+++ b/xos/tools/chuckmove
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+
+from pyparsing import *
+from optparse import OptionParser
+import os
+from os.path import join,exists
+from shutil import copy
+
+usage = "usage: %prog --old <old namespace> --new <new namespace> <directory root>"
+
+parser = OptionParser(usage=usage)
+
+parser.add_option("-q", "--quiet",
+                          action="store_false", dest="verbose",
+                          help="be vewwy quiet (I'm hunting wabbits)")
+
+parser.add_option("-o", "--old", 
+                          metavar="old", help="Old namespace")
+
+parser.add_option("-n", "--new",
+                          default="new", help="New namespace")
+
+(options, args) = parser.parse_args()
+
+old_ns = options.old
+new_ns = options.new
+
+# grammar
+
+comment = '#' + SkipTo(lineEnd)
+
+module_ns = Word(alphanums + '-' + '_' + '.')
+old_module_ns = Combine(old_ns + Optional(module_ns))
+end_of_python_line = Or([lineEnd,comment])
+
+as_suffix = 'as' + module_ns
+
+import_pure = 'import' + old_module_ns 
+import_pure_line = import_pure + Optional(as_suffix) + end_of_python_line
+
+import_from = 'from' + old_module_ns + 'import' + module_ns 
+import_from_line = import_from + Optional(as_suffix) + end_of_python_line
+
+import_line = Or([import_pure_line, import_from_line])
+
+# Make a list of Python files to deal with
+
+try:
+    f = args[0]
+except IndexError:
+    print 'Specify a directory root'
+    exit(1)
+
+for root, dirs, files in os.walk(f):
+    for n in files:
+        if (n.endswith('.py')):
+            full_path = '/'.join([root,n])
+            orig_path = full_path + '.orig'
+            print 'Working on %s:'%full_path
+            if (not os.path.exists(orig_path)):
+                print 'Copying %s->%s:'%(full_path,orig_path)
+                copy(full_path, orig_path)
+            f_contents = open(orig_path).read()
+            new_contents = []
+            for l in f_contents.splitlines():
+                try:
+                    match = import_line.parseString(l)
+                    l = l.replace(old_ns, new_ns)
+                except ParseException:
+                    pass
+                new_contents.append(l)
+
+            new_file = '\n'.join(new_contents)
+            if (f_contents.endswith('\n')):
+                new_file+='\n'
+            open(full_path,'w').write(new_file)
diff --git a/xos/tools/chuckmove.README b/xos/tools/chuckmove.README
new file mode 100644
index 0000000..5039548
--- /dev/null
+++ b/xos/tools/chuckmove.README
@@ -0,0 +1,27 @@
+Hi,
+
+I've written a tool called 'chuckmove' for helping move Python modules around in a source tree. You use it as follows. Lets say you want to relocate a module to a different location in the tree, and also rename it. So for instance, x is to become y.z. The syntax you use is:
+
+chuckmove -o x -n y.z <root directory>
+
+Invoking this command makes the tool recursively descend into the root directory, make a backup copy of each file (adding the prefix '.orig') and rewrite the imports in it, so that "import x" gets turned into "import y.z"
+
+It recognizes Python grammar, so it works with all of these forms:
+
+from x import a
+from x.b import c 
+import x.d.e.f as foo # Comments are also handled
+
+...with the nit that lines with syntax/grammatical errors are left as is.
+
+For example, for the observer/synchronizer changes, I just had to do the following:
+
+chuckmove -o observer -n synchronizers.base xos
+
+...and then to generate a patch with the changes:
+
+gendiff xos .orig
+
+It's checked into the xos repository under tools (with a README file!).
+
+Sapan