| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | """distutils.fancy_getopt
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Wrapper around the standard getopt module that provides the following | 
					
						
							|  |  |  | additional features: | 
					
						
							|  |  |  |   * short and long options are tied together | 
					
						
							|  |  |  |   * options have help strings, so fancy_getopt could potentially | 
					
						
							|  |  |  |     create a complete usage summary | 
					
						
							|  |  |  |   * options set attributes of a passed-in object | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # created 1999/03/03, Greg Ward | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-02 01:49:45 +00:00
										 |  |  | __revision__ = "$Id$" | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | import sys, string, re | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | from types import * | 
					
						
							|  |  |  | import getopt | 
					
						
							|  |  |  | from distutils.errors import * | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Much like command_re in distutils.core, this is close to but not quite | 
					
						
							|  |  |  | # the same as a Python NAME -- except, in the spirit of most GNU | 
					
						
							|  |  |  | # utilities, we use '-' in place of '_'.  (The spirit of LISP lives on!) | 
					
						
							|  |  |  | # The similarities to NAME are again not a coincidence... | 
					
						
							| 
									
										
										
										
											1999-10-03 20:48:53 +00:00
										 |  |  | longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  | longopt_re = re.compile(r'^%s$' % longopt_pat) | 
					
						
							| 
									
										
										
										
											1999-10-03 20:48:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # For recognizing "negative alias" options, eg. "quiet=!verbose" | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  | neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) | 
					
						
							| 
									
										
										
										
											1999-10-03 20:48:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | # This is used to translate long options to legitimate Python identifiers | 
					
						
							|  |  |  | # (for use as attributes of some object). | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  | longopt_xlate = string.maketrans('-', '_') | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | # This records (option, value) pairs in the order seen on the command line; | 
					
						
							|  |  |  | # it's close to what getopt.getopt() returns, but with short options | 
					
						
							|  |  |  | # expanded.  (Ugh, this module should be OO-ified.) | 
					
						
							|  |  |  | _option_order = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FancyGetopt: | 
					
						
							|  |  |  |     """Wrapper around the standard 'getopt()' module that provides some
 | 
					
						
							|  |  |  |     handy extra functionality: | 
					
						
							|  |  |  |       * short and long options are tied together | 
					
						
							|  |  |  |       * options have help strings, and help text can be assembled | 
					
						
							|  |  |  |         from them | 
					
						
							|  |  |  |       * options set attributes of a passed-in object | 
					
						
							|  |  |  |       * boolean options can have "negative aliases" -- eg. if | 
					
						
							|  |  |  |         --quiet is the "negative alias" of --verbose, then "--quiet" | 
					
						
							|  |  |  |         on the command line sets 'verbose' to false | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__ (self, option_table=None): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # The option table is (currently) a list of 3-tuples: | 
					
						
							|  |  |  |         #   (long_option, short_option, help_string) | 
					
						
							|  |  |  |         # if an option takes an argument, its long_option should have '=' | 
					
						
							|  |  |  |         # appended; short_option should just be a single character, no ':' | 
					
						
							|  |  |  |         # in any case.  If a long_option doesn't have a corresponding | 
					
						
							|  |  |  |         # short_option, short_option should be None.  All option tuples | 
					
						
							|  |  |  |         # must have long options. | 
					
						
							|  |  |  |         self.option_table = option_table | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # 'option_index' maps long option names to entries in the option | 
					
						
							|  |  |  |         # table (ie. those 3-tuples). | 
					
						
							|  |  |  |         self.option_index = {} | 
					
						
							|  |  |  |         if self.option_table: | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |             self._build_index() | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |         # 'alias' records (duh) alias options; {'foo': 'bar'} means | 
					
						
							|  |  |  |         # --foo is an alias for --bar | 
					
						
							|  |  |  |         self.alias = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         # 'negative_alias' keeps track of options that are the boolean | 
					
						
							|  |  |  |         # opposite of some other option | 
					
						
							|  |  |  |         self.negative_alias = {} | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         # These keep track of the information in the option table.  We | 
					
						
							|  |  |  |         # don't actually populate these structures until we're ready to | 
					
						
							|  |  |  |         # parse the command-line, since the 'option_table' passed in here | 
					
						
							|  |  |  |         # isn't necessarily the final word. | 
					
						
							|  |  |  |         self.short_opts = [] | 
					
						
							|  |  |  |         self.long_opts = [] | 
					
						
							|  |  |  |         self.short2long = {} | 
					
						
							|  |  |  |         self.attr_name = {} | 
					
						
							|  |  |  |         self.takes_arg = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # And 'option_order' is filled up in 'getopt()'; it records the | 
					
						
							|  |  |  |         # original order of options (and their values) on the command-line, | 
					
						
							|  |  |  |         # but expands short options, converts aliases, etc. | 
					
						
							|  |  |  |         self.option_order = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # __init__ () | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |     def _build_index (self): | 
					
						
							| 
									
										
										
										
											2000-08-07 00:45:51 +00:00
										 |  |  |         self.option_index.clear() | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         for option in self.option_table: | 
					
						
							|  |  |  |             self.option_index[option[0]] = option | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |     def set_option_table (self, option_table): | 
					
						
							|  |  |  |         self.option_table = option_table | 
					
						
							|  |  |  |         self._build_index() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |     def add_option (self, long_option, short_option=None, help_string=None): | 
					
						
							|  |  |  |         if self.option_index.has_key(long_option): | 
					
						
							|  |  |  |             raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                   "option conflict: already an option '%s'" % long_option | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             option = (long_option, short_option, help_string) | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             self.option_table.append(option) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |             self.option_index[long_option] = option | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def has_option (self, long_option): | 
					
						
							|  |  |  |         """Return true if the option table for this parser has an
 | 
					
						
							|  |  |  |         option with long name 'long_option'."""
 | 
					
						
							|  |  |  |         return self.option_index.has_key(long_option) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_attr_name (self, long_option): | 
					
						
							|  |  |  |         """Translate long option name 'long_option' to the form it 
 | 
					
						
							|  |  |  |         has as an attribute of some object: ie., translate hyphens | 
					
						
							|  |  |  |         to underscores."""
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         return string.translate(long_option, longopt_xlate) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |     def _check_alias_dict (self, aliases, what): | 
					
						
							|  |  |  |         assert type(aliases) is DictionaryType | 
					
						
							|  |  |  |         for (alias, opt) in aliases.items(): | 
					
						
							|  |  |  |             if not self.option_index.has_key(alias): | 
					
						
							|  |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                       ("invalid %s '%s': " | 
					
						
							|  |  |  |                        "option '%s' not defined") % (what, alias, alias) | 
					
						
							|  |  |  |             if not self.option_index.has_key(opt): | 
					
						
							|  |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                       ("invalid %s '%s': " | 
					
						
							|  |  |  |                        "aliased option '%s' not defined") % (what, alias, opt) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |     def set_aliases (self, alias): | 
					
						
							|  |  |  |         """Set the aliases for this option parser.""" | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         self._check_alias_dict(alias, "alias") | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |         self.alias = alias | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |     def set_negative_aliases (self, negative_alias): | 
					
						
							|  |  |  |         """Set the negative aliases for this option parser.
 | 
					
						
							|  |  |  |         'negative_alias' should be a dictionary mapping option names to | 
					
						
							|  |  |  |         option names, both the key and value must already be defined | 
					
						
							|  |  |  |         in the option table."""
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         self._check_alias_dict(negative_alias, "negative alias") | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         self.negative_alias = negative_alias | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _grok_option_table (self): | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         """Populate the various data structures that keep tabs on the
 | 
					
						
							|  |  |  |         option table.  Called by 'getopt()' before it can do anything | 
					
						
							|  |  |  |         worthwhile. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-08-07 00:45:51 +00:00
										 |  |  |         self.long_opts = [] | 
					
						
							|  |  |  |         self.short_opts = [] | 
					
						
							|  |  |  |         self.short2long.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         for option in self.option_table: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 (long, short, help) = option | 
					
						
							|  |  |  |             except ValueError: | 
					
						
							|  |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                       "invalid option tuple " + str(option) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Type- and value-check the option names | 
					
						
							|  |  |  |             if type(long) is not StringType or len(long) < 2: | 
					
						
							|  |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                       ("invalid long option '%s': " | 
					
						
							|  |  |  |                        "must be a string of length >= 2") % long | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (not ((short is None) or | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                      (type(short) is StringType and len(short) == 1))): | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                       ("invalid short option '%s': " | 
					
						
							|  |  |  |                        "must a single character or None") % short | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             self.long_opts.append(long) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if long[-1] == '=':             # option takes an argument? | 
					
						
							|  |  |  |                 if short: short = short + ':' | 
					
						
							|  |  |  |                 long = long[0:-1] | 
					
						
							|  |  |  |                 self.takes_arg[long] = 1 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 # Is option is a "negative alias" for some other option (eg. | 
					
						
							|  |  |  |                 # "quiet" == "!verbose")? | 
					
						
							|  |  |  |                 alias_to = self.negative_alias.get(long) | 
					
						
							|  |  |  |                 if alias_to is not None: | 
					
						
							|  |  |  |                     if self.takes_arg[alias_to]: | 
					
						
							|  |  |  |                         raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                               ("invalid negative alias '%s': " | 
					
						
							|  |  |  |                                "aliased option '%s' takes a value") % \ | 
					
						
							|  |  |  |                                (long, alias_to) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     self.long_opts[-1] = long # XXX redundant?! | 
					
						
							|  |  |  |                     self.takes_arg[long] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self.takes_arg[long] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |             # If this is an alias option, make sure its "takes arg" flag is | 
					
						
							|  |  |  |             # the same as the option it's aliased to. | 
					
						
							|  |  |  |             alias_to = self.alias.get(long) | 
					
						
							|  |  |  |             if alias_to is not None: | 
					
						
							|  |  |  |                 if self.takes_arg[long] != self.takes_arg[alias_to]: | 
					
						
							|  |  |  |                     raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                           ("invalid alias '%s': inconsistent with " | 
					
						
							|  |  |  |                            "aliased option '%s' (one of them takes a value, " | 
					
						
							|  |  |  |                            "the other doesn't") % (long, alias_to) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Now enforce some bondage on the long option name, so we can | 
					
						
							|  |  |  |             # later translate it to an attribute name on some object.  Have | 
					
						
							|  |  |  |             # to do this a bit late to make sure we've removed any trailing | 
					
						
							|  |  |  |             # '='. | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             if not longopt_re.match(long): | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 raise DistutilsGetoptError, \ | 
					
						
							|  |  |  |                       ("invalid long option name '%s' " + | 
					
						
							|  |  |  |                        "(must be letters, numbers, hyphens only") % long | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             self.attr_name[long] = self.get_attr_name(long) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |             if short: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                 self.short_opts.append(short) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 self.short2long[short[0]] = long | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # for option_table | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # _grok_option_table() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getopt (self, args=None, object=None): | 
					
						
							|  |  |  |         """Parse the command-line options in 'args' and store the results
 | 
					
						
							|  |  |  |         as attributes of 'object'.  If 'args' is None or not supplied, uses | 
					
						
							|  |  |  |         'sys.argv[1:]'.  If 'object' is None or not supplied, creates a new | 
					
						
							|  |  |  |         OptionDummy object, stores option values there, and returns a tuple | 
					
						
							|  |  |  |         (args, object).  If 'object' is supplied, it is modified in place | 
					
						
							|  |  |  |         and 'getopt()' just returns 'args'; in both cases, the returned | 
					
						
							|  |  |  |         'args' is a modified copy of the passed-in 'args' list, which is | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         left untouched. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         if args is None: | 
					
						
							|  |  |  |             args = sys.argv[1:] | 
					
						
							|  |  |  |         if object is None: | 
					
						
							| 
									
										
										
										
											2000-05-23 03:53:10 +00:00
										 |  |  |             object = OptionDummy() | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |             created_object = 1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             created_object = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._grok_option_table() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         short_opts = string.join(self.short_opts) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             (opts, args) = getopt.getopt(args, short_opts, self.long_opts) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         except getopt.error, msg: | 
					
						
							|  |  |  |             raise DistutilsArgError, msg | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (opt, val) in opts: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             if len(opt) == 2 and opt[0] == '-': # it's a short option | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 opt = self.short2long[opt[1]] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             elif len(opt) > 2 and opt[0:2] == '--': | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 opt = opt[2:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise DistutilsInternalError, \ | 
					
						
							|  |  |  |                       "this can't happen: bad option string '%s'" % opt | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |             alias = self.alias.get(opt) | 
					
						
							|  |  |  |             if alias: | 
					
						
							|  |  |  |                 opt = alias | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |             if not self.takes_arg[opt]:     # boolean option? | 
					
						
							|  |  |  |                 if val != '':               # shouldn't have a value! | 
					
						
							|  |  |  |                     raise DistutilsInternalError, \ | 
					
						
							|  |  |  |                           "this can't happen: bad option value '%s'" % value | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                 alias = self.negative_alias.get(opt) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |                 if alias: | 
					
						
							|  |  |  |                     opt = alias | 
					
						
							|  |  |  |                     val = 0 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     val = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             attr = self.attr_name[opt] | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             setattr(object, attr, val) | 
					
						
							|  |  |  |             self.option_order.append((opt, val)) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # for opts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if created_object: | 
					
						
							|  |  |  |             return (args, object) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # getopt() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |     def get_option_order (self): | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         """Returns the list of (option, value) tuples processed by the
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         previous run of 'getopt()'.  Raises RuntimeError if | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         'getopt()' hasn't been called yet. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         if self.option_order is None: | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |             raise RuntimeError, "'getopt()' hasn't been called yet" | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return self.option_order | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-23 02:50:45 +00:00
										 |  |  |     def generate_help (self, header=None): | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         """Generate help text (a list of strings, one per suggested line of
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         output) from the option table for this FancyGetopt object. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         # Blithely assume the option table is good: probably wouldn't call | 
					
						
							|  |  |  |         # 'generate_help()' unless you've already called 'getopt()'. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # First pass: determine maximum length of long option names | 
					
						
							|  |  |  |         max_opt = 0 | 
					
						
							|  |  |  |         for option in self.option_table: | 
					
						
							|  |  |  |             long = option[0] | 
					
						
							|  |  |  |             short = option[1] | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             l = len(long) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |             if long[-1] == '=': | 
					
						
							|  |  |  |                 l = l - 1 | 
					
						
							|  |  |  |             if short is not None: | 
					
						
							|  |  |  |                 l = l + 5                   # " (-x)" where short == 'x' | 
					
						
							|  |  |  |             if l > max_opt: | 
					
						
							|  |  |  |                 max_opt = l | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         opt_width = max_opt + 2 + 2 + 2     # room for indent + dashes + gutter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # 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): | 
					
						
							|  |  |  |         #   --foo (-f)  controls foonabulation | 
					
						
							|  |  |  |         # If adding the short option would make the left column too wide, | 
					
						
							|  |  |  |         # we push the explanation off to the next line | 
					
						
							|  |  |  |         #   --flimflam (-l) | 
					
						
							|  |  |  |         #               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) | 
					
						
							|  |  |  |         #   - 5 characters (incl. space) for short option name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Now generate lines of help text.  (If 80 columns were good enough | 
					
						
							|  |  |  |         # for Jesus, then 78 columns are good enough for me!) | 
					
						
							|  |  |  |         line_width = 78 | 
					
						
							|  |  |  |         text_width = line_width - opt_width | 
					
						
							|  |  |  |         big_indent = ' ' * opt_width | 
					
						
							|  |  |  |         if header: | 
					
						
							|  |  |  |             lines = [header] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             lines = ['Option summary:'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (long,short,help) in self.option_table: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             text = wrap_text(help, text_width) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |             if long[-1] == '=': | 
					
						
							|  |  |  |                 long = long[0:-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Case 1: no short option at all (makes life easy) | 
					
						
							|  |  |  |             if short is None: | 
					
						
							|  |  |  |                 if text: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                     lines.append("  --%-*s  %s" % (max_opt, long, text[0])) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                     lines.append("  --%-*s  " % (max_opt, long)) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Case 2: we have a short option, so we have to include it | 
					
						
							|  |  |  |             # just after the long option | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 opt_names = "%s (-%s)" % (long, short) | 
					
						
							|  |  |  |                 if text: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                     lines.append("  --%-*s  %s" % | 
					
						
							|  |  |  |                                  (max_opt, opt_names, text[0])) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                     lines.append("  --%-*s" % opt_names) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-06-08 00:35:33 +00:00
										 |  |  |             for l in text[1:]: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                 lines.append(big_indent + l) | 
					
						
							| 
									
										
										
										
											2000-06-08 00:35:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         # for self.option_table | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return lines | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # generate_help () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-23 02:50:45 +00:00
										 |  |  |     def print_help (self, header=None, file=None): | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         if file is None: | 
					
						
							|  |  |  |             file = sys.stdout | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         for line in self.generate_help(header): | 
					
						
							|  |  |  |             file.write(line + "\n") | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | # class FancyGetopt | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | def fancy_getopt (options, negative_opt, object, args): | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |     parser = FancyGetopt(options) | 
					
						
							|  |  |  |     parser.set_negative_aliases(negative_opt) | 
					
						
							|  |  |  |     return parser.getopt(args, object) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  | WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def wrap_text (text, width): | 
					
						
							| 
									
										
										
										
											2000-08-30 17:16:27 +00:00
										 |  |  |     """wrap_text(text : string, width : int) -> [string]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Split 'text' into multiple lines of no more than 'width' characters | 
					
						
							|  |  |  |     each, and return the list of strings that results. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if text is None: | 
					
						
							|  |  |  |         return [] | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |     if len(text) <= width: | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |         return [text] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |     text = string.expandtabs(text) | 
					
						
							|  |  |  |     text = string.translate(text, WS_TRANS) | 
					
						
							|  |  |  |     chunks = re.split(r'( +|-+)', text) | 
					
						
							|  |  |  |     chunks = filter(None, chunks)      # ' - ' results in empty strings | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |     lines = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while chunks: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cur_line = []                   # list of chunks (to-be-joined) | 
					
						
							|  |  |  |         cur_len = 0                     # length of current line | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while chunks: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |             l = len(chunks[0]) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |             if cur_len + l <= width:    # can squeeze (at least) this chunk in | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                 cur_line.append(chunks[0]) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |                 del chunks[0] | 
					
						
							|  |  |  |                 cur_len = cur_len + l | 
					
						
							|  |  |  |             else:                       # this line is full | 
					
						
							|  |  |  |                 # drop last chunk if all space | 
					
						
							|  |  |  |                 if cur_line and cur_line[-1][0] == ' ': | 
					
						
							|  |  |  |                     del cur_line[-1] | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if chunks:                      # any chunks left to process? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # if the current line is still empty, then we had a single | 
					
						
							|  |  |  |             # chunk that's too big too fit on a line -- so we break | 
					
						
							|  |  |  |             # down and break it up at the line width | 
					
						
							|  |  |  |             if cur_len == 0: | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |                 cur_line.append(chunks[0][0:width]) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |                 chunks[0] = chunks[0][width:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # all-whitespace chunks at the end of a line can be discarded | 
					
						
							|  |  |  |             # (and we know from the re.split above that if a chunk has | 
					
						
							|  |  |  |             # *any* whitespace, it is *all* whitespace) | 
					
						
							|  |  |  |             if chunks[0][0] == ' ': | 
					
						
							|  |  |  |                 del chunks[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # and store this line in the list-of-all-lines -- as a single | 
					
						
							|  |  |  |         # string, of course! | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         lines.append(string.join(cur_line, '')) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # while chunks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return lines | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # wrap_text () | 
					
						
							| 
									
										
										
										
											2000-09-25 01:58:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def translate_longopt (opt): | 
					
						
							|  |  |  |     """Convert a long option name to a valid Python identifier by
 | 
					
						
							|  |  |  |     changing "-" to "_". | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     return string.translate(opt, longopt_xlate) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |          | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | class OptionDummy: | 
					
						
							|  |  |  |     """Dummy class just used as a place to hold command-line option
 | 
					
						
							|  |  |  |     values as instance attributes."""
 | 
					
						
							| 
									
										
										
										
											2000-05-23 01:44:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__ (self, options=[]): | 
					
						
							|  |  |  |         """Create a new OptionDummy instance.  The attributes listed in
 | 
					
						
							|  |  |  |         'options' will be initialized to None."""
 | 
					
						
							|  |  |  |         for opt in options: | 
					
						
							|  |  |  |             setattr(self, opt, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # class OptionDummy | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |      | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     text = """\
 | 
					
						
							|  |  |  | Tra-la-la, supercalifragilisticexpialidocious. | 
					
						
							|  |  |  | How *do* you spell that odd word, anyways? | 
					
						
							|  |  |  | (Someone ask Mary -- she'll know [or she'll | 
					
						
							|  |  |  | say, "How should I know?"].)"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for w in (10, 20, 30, 40): | 
					
						
							|  |  |  |         print "width: %d" % w | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         print string.join(wrap_text(text, w), "\n") | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  |         print |