Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 1 | """distutils.fancy_getopt |
| 2 | |
| 3 | Wrapper around the standard getopt module that provides the following |
| 4 | additional features: |
| 5 | * short and long options are tied together |
| 6 | * options have help strings, so fancy_getopt could potentially |
| 7 | create a complete usage summary |
| 8 | * options set attributes of a passed-in object |
| 9 | """ |
| 10 | |
| 11 | # created 1999/03/03, Greg Ward |
| 12 | |
| 13 | __rcsid__ = "$Id$" |
| 14 | |
| 15 | import string, re |
| 16 | from types import * |
| 17 | import getopt |
| 18 | from distutils.errors import * |
| 19 | |
| 20 | # Much like command_re in distutils.core, this is close to but not quite |
| 21 | # the same as a Python NAME -- except, in the spirit of most GNU |
| 22 | # utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) |
| 23 | # The similarities to NAME are again not a coincidence... |
Greg Ward | a564cc3 | 1999-10-03 20:48:53 +0000 | [diff] [blame] | 24 | longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' |
| 25 | longopt_re = re.compile (r'^%s$' % longopt_pat) |
| 26 | |
| 27 | # For recognizing "negative alias" options, eg. "quiet=!verbose" |
| 28 | neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) |
| 29 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 30 | |
| 31 | # This is used to translate long options to legitimate Python identifiers |
| 32 | # (for use as attributes of some object). |
| 33 | longopt_xlate = string.maketrans ('-', '_') |
| 34 | |
| 35 | |
| 36 | def fancy_getopt (options, object, args): |
| 37 | |
| 38 | # The 'options' table is a list of 3-tuples: |
| 39 | # (long_option, short_option, help_string) |
| 40 | # if an option takes an argument, its long_option should have '=' |
| 41 | # appended; short_option should just be a single character, no ':' in |
| 42 | # any case. If a long_option doesn't have a corresponding |
| 43 | # short_option, short_option should be None. All option tuples must |
| 44 | # have long options. |
| 45 | |
| 46 | # Build the short_opts string and long_opts list, remembering how |
| 47 | # the two are tied together |
| 48 | |
| 49 | short_opts = [] # we'll join 'em when done |
| 50 | long_opts = [] |
| 51 | short2long = {} |
| 52 | attr_name = {} |
| 53 | takes_arg = {} |
Greg Ward | a564cc3 | 1999-10-03 20:48:53 +0000 | [diff] [blame] | 54 | neg_alias = {} |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 55 | |
Greg Ward | 0081cc5 | 1999-08-14 23:44:37 +0000 | [diff] [blame] | 56 | for option in options: |
| 57 | try: |
| 58 | (long, short, help) = option |
| 59 | except ValueError: |
| 60 | raise DistutilsGetoptError, \ |
| 61 | "invalid option tuple " + str (option) |
| 62 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 63 | # Type-check the option names |
| 64 | if type (long) is not StringType or len (long) < 2: |
| 65 | raise DistutilsGetoptError, \ |
Greg Ward | 0081cc5 | 1999-08-14 23:44:37 +0000 | [diff] [blame] | 66 | "long option '%s' must be a string of length >= 2" % \ |
| 67 | long |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 68 | |
| 69 | if (not ((short is None) or |
| 70 | (type (short) is StringType and len (short) == 1))): |
| 71 | raise DistutilsGetoptError, \ |
Greg Ward | 0081cc5 | 1999-08-14 23:44:37 +0000 | [diff] [blame] | 72 | "short option '%s' must be None or string of length 1" % \ |
| 73 | short |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 74 | |
| 75 | long_opts.append (long) |
| 76 | |
| 77 | if long[-1] == '=': # option takes an argument? |
| 78 | if short: short = short + ':' |
| 79 | long = long[0:-1] |
| 80 | takes_arg[long] = 1 |
| 81 | else: |
Greg Ward | a564cc3 | 1999-10-03 20:48:53 +0000 | [diff] [blame] | 82 | |
| 83 | # Is option is a "negative alias" for some other option (eg. |
| 84 | # "quiet=!verbose")? |
| 85 | match = neg_alias_re.match (long) |
| 86 | if match: |
| 87 | (alias_from, alias_to) = match.group (1,2) |
| 88 | if not takes_arg.has_key(alias_to) or takes_arg[alias_to]: |
| 89 | raise DistutilsGetoptError, \ |
| 90 | ("option '%s' is a negative alias for '%s', " + |
| 91 | "which either hasn't been defined yet " + |
| 92 | "or takes an argument") % (alias_from, alias_to) |
| 93 | |
| 94 | long = alias_from |
| 95 | neg_alias[long] = alias_to |
| 96 | long_opts[-1] = long |
| 97 | takes_arg[long] = 0 |
| 98 | |
| 99 | else: |
| 100 | takes_arg[long] = 0 |
| 101 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 102 | |
| 103 | # Now enforce some bondage on the long option name, so we can later |
| 104 | # translate it to an attribute name in 'object'. Have to do this a |
| 105 | # bit late to make sure we've removed any trailing '='. |
| 106 | if not longopt_re.match (long): |
| 107 | raise DistutilsGetoptError, \ |
| 108 | ("invalid long option name '%s' " + |
| 109 | "(must be letters, numbers, hyphens only") % long |
| 110 | |
| 111 | attr_name[long] = string.translate (long, longopt_xlate) |
| 112 | if short: |
| 113 | short_opts.append (short) |
| 114 | short2long[short[0]] = long |
| 115 | |
| 116 | # end loop over 'options' |
| 117 | |
| 118 | short_opts = string.join (short_opts) |
| 119 | try: |
| 120 | (opts, args) = getopt.getopt (args, short_opts, long_opts) |
| 121 | except getopt.error, msg: |
| 122 | raise DistutilsArgError, msg |
| 123 | |
| 124 | for (opt, val) in opts: |
| 125 | if len (opt) == 2 and opt[0] == '-': # it's a short option |
| 126 | opt = short2long[opt[1]] |
| 127 | |
| 128 | elif len (opt) > 2 and opt[0:2] == '--': |
| 129 | opt = opt[2:] |
| 130 | |
| 131 | else: |
| 132 | raise RuntimeError, "getopt lies! (bad option string '%s')" % \ |
| 133 | opt |
| 134 | |
| 135 | attr = attr_name[opt] |
| 136 | if takes_arg[opt]: |
| 137 | setattr (object, attr, val) |
| 138 | else: |
| 139 | if val == '': |
Greg Ward | a564cc3 | 1999-10-03 20:48:53 +0000 | [diff] [blame] | 140 | alias = neg_alias.get (opt) |
| 141 | if alias: |
| 142 | setattr (object, attr_name[alias], 0) |
| 143 | else: |
| 144 | setattr (object, attr, 1) |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 145 | else: |
| 146 | raise RuntimeError, "getopt lies! (bad value '%s')" % value |
| 147 | |
| 148 | # end loop over options found in 'args' |
| 149 | |
| 150 | return args |
| 151 | |
| 152 | # end fancy_getopt() |