[Zope3-checkins] CVS: Zope3/src/zope/fssync - fssync.py:1.9 main.py:1.4

Guido van Rossum guido@python.org
Tue, 13 May 2003 15:16:59 -0400


Update of /cvs-repository/Zope3/src/zope/fssync
In directory cvs.zope.org:/tmp/cvs-serv30438

Modified Files:
	fssync.py main.py 
Log Message:
Towards more rational command line handling.

=== Zope3/src/zope/fssync/fssync.py 1.8 => 1.9 ===
--- Zope3/src/zope/fssync/fssync.py:1.8	Tue May 13 13:32:50 2003
+++ Zope3/src/zope/fssync/fssync.py	Tue May 13 15:16:58 2003
@@ -39,6 +39,8 @@
 from zope.fssync.metadata import Metadata
 from zope.fssync.merger import Merger
 
+unwanted = ("", os.curdir, os.pardir)
+
 class Error(Exception):
     """User-level error, e.g. non-existent file.
 
@@ -133,7 +135,6 @@
 
         If no root url is found, return None.
         """
-        unwanted = ("", os.curdir, os.pardir)
         dir = realpath(target)
         while dir:
             zopedir = join(dir, "@@Zope")
@@ -307,22 +308,31 @@
         if exists(target) and not isdir(target):
             raise Error("target should be a directory", target)
         self.ensuredir(target)
-        fp, headers = self.network.httpreq(rootpath,
-                                           "@@toFS.zip?writeOriginals=False")
+        fp, headers = self.network.httpreq(rootpath, "@@toFS.zip")
         try:
             self.merge_zipfile(fp, target)
         finally:
             fp.close()
         self.network.saverooturl(target)
 
+    def multiple(self, args, method):
+        if not args:
+            args = [os.curdir]
+        for target in args:
+            if self.metadata.getentry(target):
+                method(target)
+            else:
+                names = self.metadata.getnames(target)
+                if not names:
+                    method(target) # Will raise an exception
+                else:
+                    for name in names:
+                        method(join(target, name))
+
     def commit(self, target):
         entry = self.metadata.getentry(target)
         if not entry:
-            names = self.metadata.getnames(target)
-            if len(names) != 1:
-                raise Error("can only commit a single directory")
-            target = join(target, names[0])
-            entry = self.metadata.getentry(target)
+            raise Error("nothing known about", target)
         self.network.loadrooturl(target)
         path = entry["path"]
         zipfile = tempfile.mktemp(".zip")
@@ -353,16 +363,11 @@
     def update(self, target):
         entry = self.metadata.getentry(target)
         if not entry:
-            names = self.metadata.getnames(target)
-            if len(names) != 1:
-                raise Error("can only commit a single directory")
-            target = join(target, names[0])
-            entry = self.metadata.getentry(target)
+            raise Error("nothing known about", target)
         self.network.loadrooturl(target)
         head, tail = split(realpath(target))
         path = entry["path"]
-        fp, headers = self.network.httpreq(path,
-                                           "@@toFS.zip?writeOriginals=False")
+        fp, headers = self.network.httpreq(path, "@@toFS.zip")
         try:
             self.merge_zipfile(fp, head)
         finally:
@@ -397,8 +402,7 @@
         if entry:
             raise Error("path '%s' is already registered", path)
         head, tail = split(path)
-        unwanted = ("", os.curdir, os.pardir)
-        if tail in unwanted:
+        if tail in unwanted or not head:
             path = realpath(path)
             head, tail = split(path)
             if head == path or tail in unwanted:
@@ -416,8 +420,6 @@
         entry["flag"] = "added"
         if isdir(path):
             entry["type"] = "zope.app.content.folder.Folder"
-            self.ensuredir(join(path, "@@Zope"))
-            self.dumpentries({}, path)
         else:
             # XXX Need to guess better based on extension
             entry["type"] = "zope.app.content.file.File"
@@ -426,6 +428,9 @@
         self.metadata.flush()
 
     def merge_dirs(self, localdir, remotedir):
+        if not isdir(remotedir):
+            return
+
         self.ensuredir(localdir)
 
         ldirs, lnondirs = classifyContents(localdir)


=== Zope3/src/zope/fssync/main.py 1.3 => 1.4 ===
--- Zope3/src/zope/fssync/main.py:1.3	Tue May 13 11:28:03 2003
+++ Zope3/src/zope/fssync/main.py	Tue May 13 15:16:59 2003
@@ -12,20 +12,18 @@
 # FOR A PARTICULAR PURPOSE.
 # 
 ##############################################################################
-"""New fssync utility.
-
-Connects to the database using HTTP (using the toFS.zip view for
-checkout and update and the fromFS.form view for commit).
-
-An attempt is made to make the behavior similar to that of cvs.
+"""Filesystem synchronization utility for Zope 3.
 
 Command line syntax summary:
 
-fssync checkout URL TARGETDIR
-fssync update [FILE_OR_DIR ...]
-fssync status [FILE_OR_DIR ...]
-fssync commit [FILE_OR_DIR ...]
-fssync diff [FILE_OR_DIR ...]
+fssync [global_options] checkout [options] URL [TARGETDIR]
+fssync [global_options] update [options] [TARGET ...]
+fssync [global_options] commit [options] [TARGET ...]
+fssync [global_options] diff [options] [TARGET ...]
+fssync [global_options] status [options] [TARGET ...]
+
+For now, the only global option is -h/--help; there are no local
+options yet.
 
 $Id$
 """
@@ -44,74 +42,104 @@
 
 # Hack to fix the module search path
 try:
-    import zope.xmlpickle
+    import zope.fssync
     # All is well
 except ImportError:
     # Fix the path to include <root>/src
     srcdir = join(rootdir, "src")
     sys.path.append(srcdir)
 
-from zope.xmlpickle import loads, dumps
-
 from zope.fssync.fssync import Error, FSSync
 
 class Usage(Error):
     """Subclass for usage error (command-line syntax).
 
-    This should set an exit status of 2 rather than 1.
+    You should return an exit status of 2 rather than 1 when catching this.
     """
 
 def main(argv=None):
+    """Main program.
+
+    You can pass it an argument list (which must include the command
+    name as argv[0]); it defaults to sys.argv.
+
+    The return value is the suggested sys.exit() status code:
+    0 or None for success
+    2 for command line syntax errors
+    1 or other for later errors
+    """
     try:
         if argv is None:
             argv = sys.argv
-        # XXX getopt
-        args = argv[1:]
+
+        try:
+            opts, args = getopt.getopt(argv[1:], "h", ["help"])
+        except getopt.error, msg:
+            raise Usage("global option error: %s", msg)
+
+        for o, a in opts:
+            if o in ("-h", "--help"):
+                print __doc__
+                return 0
+
         command = args[0]
-        # XXX more getopt
-        args = args[1:]
-        if command in ("checkout", "co"):
-            url, fspath = args
-            checkout(url, fspath)
-        elif command in ("update", "up"):
-            args = args or [os.curdir]
-            for fspath in args:
-                print "update(%r)" % fspath
-                update(fspath)
-        elif command in ("commit", "com"):
-            args = args or [os.curdir]
-            [fspath] = args
-            commit(fspath)
-        elif command == "add":
-            add(args)
-        else:
-            raise Usage("command %r not recognized" % command)
+        if command not in command_table:
+            hits = []
+            for c in command_table:
+                if c.startswith(command):
+                    hits.append(c)
+            if not hits:
+                raise Usage("unrecognized command", command)
+            if len(hits) > 1:
+                raise Usage("ambiguous command abbreviation %r (%s)",
+                            command, "|".join(hits))
+            command = hits[0]
+
+        short_opts, long_opts, handler = command_table[command]
+
+        try:
+            opts, args = getopt.getopt(args[1:], short_opts, long_opts)
+        except getopt.error, msg:
+            raise Usage("%s option error: %s", command, msg)
+
+        return handler(opts, args)
+
     except Usage, msg:
-        print msg
-        print "for help use --help"
+        print >>sys.stderr, msg
+        print >>sys.stderr, "for help use --help"
         return 2
+
     except Error, msg:
-        print msg
+        print >>sys.stderr, msg
         return 1
+
     else:
         return None
 
-def checkout(rooturl, target):
+def checkout(opts, args):
+    rooturl, target = args
     fs = FSSync(rooturl=rooturl)
     fs.checkout(target)
 
-def commit(target):
+def commit(opts, args):
     fs = FSSync()
-    fs.commit(target)
+    fs.multiple(args, fs.commit)
 
-def update(target):
+def update(opts, args):
     fs = FSSync()
-    fs.update(target)
+    fs.multiple(args, fs.update)
 
-def add(args):
-    fs = FSSync(os.curdir)
+def add(opts, args):
+    fs = FSSync()
     for a in args:
         fs.add(a)
+
+command_table = {
+    "checkout": ("", [], checkout),
+    "update":   ("", [], update),
+    "commit":   ("", [], commit),
+    "add":      ("", [], add),
+    }
 
 if __name__ == "__main__":
     sys.exit(main(sys.argv))