Options.py :  » XML » 4Suite » 4Suite-XML-1.0.2 » Ft » Lib » CommandLine » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » XML » 4Suite 
4Suite » 4Suite XML 1.0.2 » Ft » Lib » CommandLine » Options.py
########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Lib/CommandLine/Options.py,v 1.13 2005/04/13 23:41:04 jkloth Exp $
"""
Classes that support advanced option processing for command-line scripts

Copyright 2004 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

from distutils.fancy_getopt import wrap_text
from Ft.Lib.CommandLine import CommandLineUtil,CONSOLE_WIDTH

class Options(list):
    """
    A set of options that are available to be used in an invocation of a
    command-line script, plus related functions.
    """
    def __init__(self, options=None):
        if options is None:
            options = ()
        list.__init__(self, options)

        # Sanity check the supplied options
        shorts = []
        longs = []
        for opt in options:
            if not isinstance(opt, BaseOption):
                raise TypeError("Option %s is not of BaseOption" % opt)
            (short, long) = opt.getForGetOpt({}, {})
            for s in short:
                if s != ':' and s in shorts:
                    raise Exception('Duplicate short option in %s' % str(self))
                shorts.append(s)
            for l in long:
                if l in longs:
                    raise Exception('Duplicate long option in %s' % str(self))
                longs.append(l)

    def findMaxOption(self, level=0):
        max_opt = 0
        for option in self:
            l = option.displayLength()

            # add two spaces for each level of sub-options
            l = l + 2*level

            if hasattr(option, 'subOptions'):
                sublen = option.subOptions.findMaxOption(level+1)
                if sublen > l:
                    l = sublen

            if l > max_opt:
                max_opt = l

        return max_opt

    def generate_help(self, level=1, max_opt=0):
        """Generate help text (a list of strings, one per suggested line of
        output) from the option table for this FancyGetopt object.
        """
        # If max_opt is > 0, this is help for a sub-option
        # maximum width has already been determined.
        if max_opt > 0:
            # The indent for sub-options is included in option length
            opt_width = max_opt - 2*(level-1)
        else:
            opt_width = max_opt = self.findMaxOption()

        # room for indent + short option + dashes + longest option + gutter + add'l indent
        col_width = 2*level + 4 + 2 + max_opt + 2 + 2

        # Typical help block looks like this:
        #   --foo       controls foonabulation
        # Help block for longest option looks like this:
        #   --flimflam  set the flim-flam level
        # and with wrapped text:
        #   --flimflam  set the flim-flam level (must be between
        #               0 and 100, except on Tuesdays)
        # Options with short names will have the short name shown (but
        # it doesn't contribute to max_opt):
        #   -f, --foo   controls foonabulation
        # If adding the short option would make the left column too wide,
        # we push the explanation off to the next line
        #   -l, --flimflam
        #               set the flim-flam level
        # Important parameters:
        #   - 2 spaces before option block start lines
        #   - 2 dashes for each long option name
        #   - min. 2 spaces between option and explanation (gutter)

        # Now generate lines of help text.
        line_width = CONSOLE_WIDTH
        text_width = line_width - col_width
        indent = '  ' * level
        big_indent = ' ' * (col_width)
        lines = []

        for option in self:
            if isinstance(option, ExclusiveOptions):
                lines.extend(option.choices.generate_help(level, max_opt))
                continue

            text = wrap_text(option.description, text_width)

            short_opt = option.shortName
            if option.takesArg:
                long_opt = '%s=<%s>' % (option.longName, option.argName)
            else:
                long_opt = option.longName
            if option.shortName:
                short_part = '-%s' % (short_opt)
            else:
                short_part = '  '
            if option.shortName and option.longName:
                short_part += ', '
            else:
                short_part += '  '
            long_part = "--%-*s" % (opt_width, long_opt)
            if text:
                lines.append('%s%s%s  %s' % (indent, short_part, long_part, text[0]))
            else:
                lines.append('%s%s%s' % (indent, short_part, long_part))

            # Add any description that didn't fit on the first line
            for line in text[1:]:
                lines.append(big_indent + line)

            if isinstance(option, TypedOption):
                for (val, desc) in option.allowed:
                    text = wrap_text(desc, text_width)
                    lines.append('%s    %-*s%s' % (indent, opt_width, val, text[0]))
                    for line in text[1:]:
                        lines.append(big_indent + line)

            if hasattr(option, 'subOptions'):
                lines.extend(option.subOptions.generate_help(level + 1, max_opt))

        return lines


class BaseOption:
    """
    An option that is available to be used in an invocation of a
    command-line script, plus related functions.
    """
    multiple = False

    def validate(self):
        return

    def getForGetOpt(self, short2long, takes_arg):
        raise NotImplementedError('subclass must override')

    def displayLength(self):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def gen_command_line(self):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def gen_description(self):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def apply_options(self, options):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def isApplied(self):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def getName(self):
        raise NotImplementedError('subclass %s must override' % self.__class__)

    def __str__(self):
        return self.gen_command_line()
    __repr__ = __str__


class Option(BaseOption):

    def __init__(self, shortName, longName, description, subOptions=None,
                 multiple=False):
        # Type- and value-check the option names
        if len(longName) < 2:
            raise SyntaxError('invalid long option: ' + longName)
        if shortName is not None and len(shortName) != 1:
            raise SyntaxError('invalid short option: ' + shortName)

        self.shortName = shortName or ''
        i = longName.find('=')
        if i > 0:
            self.takesArg = 1
            argName = longName[i+1:]
            longName = longName[:i]
            self.argName = argName or longName
        else:
            self.takesArg = 0
        self.longName = longName
        self.description = description

        if not isinstance(subOptions, Options):
            subOptions = Options(subOptions)
        self.subOptions = subOptions
        self.multiple = multiple

    def getForGetOpt(self, short2long, takes_arg):
        short_opts = self.shortName
        if self.takesArg:
            if short_opts:
                short_opts = short_opts + ':'
            long_opts = [self.longName + '=']
        else:
            long_opts = [self.longName]

        takes_arg[self.longName] = self.takesArg
        if self.shortName:
            short2long[self.shortName] = self.longName

        # get getopt options for any sub-options
        for option in self.subOptions:
            (short, long) = option.getForGetOpt(short2long, takes_arg)
            short_opts = short_opts + short
            long_opts.extend(long)

        return (short_opts, long_opts)

    def displayLength(self):
        l = len(self.longName)
        if self.takesArg:
            # add the "=argName" length
            l = l + 1 + len(self.argName)
        return l

    def validate(self):
        for option in self.subOptions:
            option.validate()

        for option in self.subOptions:
            if option.isApplied() and not self.applied:
                raise CommandLineUtil.ArgumentError("%s specified without %s" % (option.getName(),self.longName))
        return

    def apply_options(self, options):
        self.applied = options.has_key(self.longName)
        for option in self.subOptions:
            option.apply_options(options)
        return


    def isApplied(self):
        return self.applied

    def getName(self):
        return self.longName

    def gen_command_line(self):
        cl = '[--%s' % self.longName
        if self.takesArg:
            cl = cl + '=<%s>' % self.argName
        if self.subOptions:
            sub = map(lambda s: s.gen_command_line(), self.subOptions)
            if len(sub) > 1:
                cl = cl + ' [%s]' % ' '.join(sub)
            else:
                cl = cl + ' ' + sub[0]
        return cl + ']'





class TypedOption(Option):
    def __init__(self, shortName, longName, description, allowed, subOptions=None):
        Option.__init__(self, shortName, longName, description, subOptions)
        self.allowedValues = map(lambda (value, desc): value, allowed)
        self.allowed = allowed

    def apply_options(self, options):
        self.applied = options.get(self.longName)
        for option in self.subOptions:
            option.apply_options(options)
        return

    def validate(self):
        if self.applied and self.applied not in self.allowedValues:
            expected = ', '.join(self.allowedValues)
            raise SyntaxError('option %s: expected %s, got %s' %
                              (self.longName, expected, self.applied))

        Option.validate(self)
        return

    def gen_command_line(self):
        sub = ''
        for option in self.subOptions:
            sub = sub + '%s ' % option.gen_command_line()
        av = '['
        for a in self.allowedValues:
            av = av + a
            if a != self.allowedValues[-1]:
                av = av + '|'
        av =av + ']'
        if sub:
            return '[--%s=%s [%s]]' % (self.longName,av,sub)
        else:
            return '[--%s=%s]' % (self.longName,av)


class ExclusiveOptions(BaseOption):
    def __init__(self, choices):
        if not isinstance(choices, Options):
            choices = Options(choices)
        self.choices = choices

    def getForGetOpt(self, short2long, takes_arg):
        short_opts = ''
        long_opts = []
        # get getopt options for all choices
        for option in self.choices:
            (short, long) = option.getForGetOpt(short2long, takes_arg)
            short_opts = short_opts + short
            long_opts.extend(long)

        return (short_opts, long_opts)

    def displayLength(self):
        return self.choices.findMaxOption()

    def validate(self):
        # make sure only one of our choices is in the options

        applied = 0
        for opt in self.choices:
            if opt.isApplied():
                if applied:
                    opts = ', '.join(map(lambda x: '--%s' % x.getName(), self.choices))
                    raise CommandLineUtil.ArgumentError("Only one of %s allowed" % opts)
                applied = opt
        if applied:
            #NOTE, this allows some sub commands to sneak in
            applied.validate()
        else:
            #Validate them all
            for option in self.choices:
                option.validate()

    def apply_options(self, options):
        for option in self.choices:
            option.apply_options(options)
        return

    def isApplied(self):
        for option in self.choices:
            if option.isApplied(): return 1
        return 0


    def getName(self):
        return '(%s)' % ', '.join(map(lambda x: '--%s' % x.getName(), self.choices))


    def gen_command_line(self):
        cl = '['
        first = 1
        for c in self.choices:
            if not first:
                cl = cl + ' | '
            else:
                first = 0
            cl = cl + c.gen_command_line()
        return cl + ']'


www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.