[Zodb-checkins] CVS: ZODB3/zdaemon - zdoptions.py:1.1 zdctl.py:1.14

Guido van Rossum guido@python.org
Wed, 15 Jan 2003 14:09:32 -0500


Update of /cvs-repository/ZODB3/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv19740

Modified Files:
	zdctl.py 
Added Files:
	zdoptions.py 
Log Message:
A new, more generic approach to command line option parsing integrated
with ZConfig schema-driven config file parsing.  Prototyped in zdaemon.py.


=== Added File ZODB3/zdaemon/zdoptions.py ===
"""Option processing for zdaemon and related code."""

import os
import sys
import getopt

import ZConfig

class ZDOptions:

    doc = None
    progname = None
    configfile = None
    schemafile = "schema.xml"

    def __init__(self):
        self.table = {}
        self.short_options = {}
        self.long_options = {}
        self.add("help", "h", "help", handler=self.help)
        self.add("configfile", "C:", "configure=")

    def help(self, value):
        print self.doc.strip()
        sys.exit(0)

    def usage(self, msg):
        sys.stderr.write(str(msg) + "\n")
        progname = self.progname
        if progname is None:
            progname = sys.argv[0]
        sys.stderr.write("for more help, use %s --help\n" % progname)
        sys.exit(2)

    def add(self,
            name,                       # attribute name on self
            short=None,                 # short option name
            long=None,                  # long option name
            confname=None,              # name in ZConfig (may be dotted)
            handler=None,               # handler (defaults to string)
            required=None,              # message issed if missing
            ):
        if self.table.has_key(name):
            raise ValueError, "duplicate option name: " + repr(name)
        if short:
            if short[0] == "-":
                raise ValueError, "short option should not start with '-'"
            key, rest = short[:1], short[1:]
            if rest and rest != ":":
                raise ValueError, "short option should be 'x' or 'x:'"
            if self.short_options.has_key(key):
                raise ValueError, "duplicate short option: " + repr(short)
        if long:
            if long[0] == "-":
                raise ValueError, "long option should not start with '-'"
            key = long
            if key[-1] == "=":
                key = key[:-1]
            if self.long_options.has_key(key):
                raise ValueError, "duplicate long option: " + repr(long)
        if short and long:
            if short.endswith(":") != long.endswith("="):
                raise ValueError, "inconsistent short/long options: %r %r" % (
                    short, long)
        self.table[name] = (short, long, confname, handler, required)
        if short:
            self.short_options[short[0]] = name
        if long:
            key = long
            if key[-1] == "=":
                key = key[:-1]
            self.long_options[key] = name

    def realize(self, args=None, progname=None, doc=None):
        """Realize a configuration."""

        if args is None:
            args = sys.argv[1:]
        if progname is None:
            self.progname = sys.argv[0]
        if doc is None:
            import __main__
            doc = __main__.__doc__
        self.progname = progname
        self.doc = doc

        # Construct short and long option tables for getopt
        shorts = ""
        longs = []
        for name, (short, long, xxx, xxx, xxx) in self.table.items():
            if short:
                shorts += short
            if long:
                longs.append(long)

        # Call getopt
        try:
            self.options, self.args = getopt.getopt(args, shorts, longs)
        except getopt.error, msg:
            self.usage(msg)

        # Process options returned by getopt
        for o, a in self.options:
            if o.startswith("--"):
                name = self.long_options[o[2:]]
            elif o.startswith("-"):
                name = self.short_options[o[1:]]
            else:
                self.usage("unrecognized option " + repr(o))
            handler = self.table[name][3]
            if handler is None:
                value = a
            else:
                try:
                    value = handler(a)
                except ValueError, msg:
                    self.usage("invalid value for %s %s: %s" % (o, a, msg))
            setattr(self, name, value)

        if self.configfile is not None:
            # Load schema
            here = os.path.dirname(__file__)
            self.schemafile = os.path.join(here, self.schemafile)
            self.schema = ZConfig.loadSchema(self.schemafile)

            # Load configuration
            try:
                self.configroot, xxx = ZConfig.loadConfig(self.schema,
                                                          self.configfile)
            except ZConfig.ConfigurationError, msg:
                self.usage(str(msg))

        # Copy config options to attributes of self.  This only fills
        # in options that aren't already set from the command line.
        for name in self.table.keys():
            if not hasattr(self, name) or getattr(self, name) is None:
                confname = self.table[name][2]
                parts = confname.split(".")
                obj = self.configroot
                for part in parts:
                    if obj is None:
                        break
                    # If this raises AttributeError, that's not a user error!
                    obj = getattr(obj, part)
                setattr(self, name, obj)


def _test():
    # Stupid test program
    z = ZDOptions()
    z.add("program", "p:", "program=", "zdctl.program")
    z.realize()
    names = z.table.keys()
    names.sort()
    for name in names:
        print "%-20s = %.56r" % (name, getattr(z, name))

if __name__ == "__main__":
    __file__ = sys.argv[0]
    _test()


=== ZODB3/zdaemon/zdctl.py 1.13 => 1.14 ===
--- ZODB3/zdaemon/zdctl.py:1.13	Tue Jan 14 17:36:53 2003
+++ ZODB3/zdaemon/zdctl.py	Wed Jan 15 14:09:29 2003
@@ -47,10 +47,14 @@
     sys.path.append(dirname(dirname(normpath(abspath(sys.argv[0])))))
 
 import ZConfig
-from ZEO.runsvr import Options
+import zdoptions
 
 
-class ZDOptions(Options):
+def string_list(arg):
+    return arg.split()
+
+
+class ZDOptions(zdoptions.ZDOptions):
 
     # Where's python?
     python = sys.executable
@@ -72,24 +76,9 @@
     user = None                         # -u USER
     zdirectory = "/"                    # -z DIRECTORY
 
-    # Program (and arguments) for zdaemon
-    program = None
-
-    def load_schema(self):
-        self.schemafile = os.path.join(self._dir, "schema.xml")
-        self.schema = ZConfig.loadSchema(self.schemafile)
-
-    def load_configuration(self):
-        Options.load_configuration(self) # Sets self.rootconf
-        if not self.rootconf:
-            self.usage("a configuration file is required; use -C")
-        # XXX Should allow overriding more zdaemon options here
-        if self.program is None:
-            program = self.rootconf.zdctl.program
-            if program:
-                self.program = program.split()
-        if self.program is None:
-            self.usage("no program specified in configuration")
+    def __init__(self):
+        zdoptions.ZDOptions.__init__(self)
+        self.add("program", "p:", "program=", "zdctl.program", string_list)
 
 
 class ZDCmd(cmd.Cmd):
@@ -110,6 +99,9 @@
                     print "our program   =", self.options.program
                     print "daemon's args =", args
 
+    def emptyline(self):
+        pass # We don't want a blank line to repeat the last command
+
     def send_action(self, action):
         """Send an action to the zdaemon server and return the response.
 
@@ -288,7 +280,7 @@
 
     def show_options(self):
         print "schemafile:  ", repr(self.options.schemafile)
-        print "configuration:", repr(self.options.configuration)
+        print "configfile:  ", repr(self.options.configfile)
         print "zdaemon:     ", repr(self.options.zdaemon)
         print "program:     ", repr(self.options.program)
         print "backofflimit:", repr(self.options.backofflimit)
@@ -342,7 +334,8 @@
                "stop the daemon manager.")
 
 def main(args=None):
-    options = ZDOptions(args)
+    options = ZDOptions()
+    options.realize(args)
     c = ZDCmd(options)
     if options.args:
         c.onecmd(" ".join(options.args))