| 
									
										
										
										
											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 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  | import sys, string, re | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | import getopt | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +00:00
										 |  |  | from distutils.errors import * | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # 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). | 
					
						
							| 
									
										
										
										
											2009-09-21 13:01:54 +00:00
										 |  |  | longopt_xlate = str.maketrans('-', '_') | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 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 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def __init__(self, option_table=None): | 
					
						
							| 
									
										
										
										
											2004-08-02 17:58:51 +00:00
										 |  |  |         # The option table is (currently) a list of tuples.  The | 
					
						
							|  |  |  |         # tuples may have 3 or four values: | 
					
						
							|  |  |  |         #   (long_option, short_option, help_string [, repeatable]) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         # 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 = {} | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         # 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 = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def set_option_table(self, option_table): | 
					
						
							| 
									
										
										
										
											2000-04-21 02:09:26 +00:00
										 |  |  |         self.option_table = option_table | 
					
						
							|  |  |  |         self._build_index() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def add_option(self, long_option, short_option=None, help_string=None): | 
					
						
							| 
									
										
										
										
											2006-08-18 22:13:04 +00:00
										 |  |  |         if long_option in self.option_index: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise DistutilsGetoptError( | 
					
						
							|  |  |  |                   "option conflict: already an option '%s'" % long_option) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def has_option(self, long_option): | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  |         """Return true if the option table for this parser has an
 | 
					
						
							|  |  |  |         option with long name 'long_option'."""
 | 
					
						
							| 
									
										
										
										
											2006-08-18 22:13:04 +00:00
										 |  |  |         return long_option in self.option_index | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def get_attr_name(self, long_option): | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  |         """Translate long option name 'long_option' to the form it
 | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  |         has as an attribute of some object: ie., translate hyphens | 
					
						
							|  |  |  |         to underscores."""
 | 
					
						
							| 
									
										
										
										
											2009-09-21 13:01:54 +00:00
										 |  |  |         return long_option.translate(longopt_xlate) | 
					
						
							| 
									
										
										
										
											2000-04-21 02:31:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def _check_alias_dict(self, aliases, what): | 
					
						
							|  |  |  |         assert isinstance(aliases, dict) | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |         for (alias, opt) in aliases.items(): | 
					
						
							| 
									
										
										
										
											2006-08-18 22:13:04 +00:00
										 |  |  |             if alias not in self.option_index: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsGetoptError(("invalid %s '%s': " | 
					
						
							|  |  |  |                        "option '%s' not defined") % (what, alias, alias)) | 
					
						
							| 
									
										
										
										
											2006-08-18 22:13:04 +00:00
										 |  |  |             if opt not in self.option_index: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsGetoptError(("invalid %s '%s': " | 
					
						
							|  |  |  |                        "aliased option '%s' not defined") % (what, alias, opt)) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def set_aliases(self, alias): | 
					
						
							| 
									
										
										
										
											2000-04-21 04:22:01 +00:00
										 |  |  |         """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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def set_negative_aliases(self, negative_alias): | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         """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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     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() | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |         self.repeat = {} | 
					
						
							| 
									
										
										
										
											2000-08-07 00:45:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         for option in self.option_table: | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |             if len(option) == 3: | 
					
						
							|  |  |  |                 long, short, help = option | 
					
						
							|  |  |  |                 repeat = 0 | 
					
						
							|  |  |  |             elif len(option) == 4: | 
					
						
							|  |  |  |                 long, short, help, repeat = option | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # the option table is part of the code, so simply | 
					
						
							|  |  |  |                 # assert that it is correct | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise ValueError("invalid option tuple: %r" % (option,)) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Type- and value-check the option names | 
					
						
							| 
									
										
										
										
											2007-10-16 18:12:55 +00:00
										 |  |  |             if not isinstance(long, str) or len(long) < 2: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsGetoptError(("invalid long option '%s': " | 
					
						
							|  |  |  |                        "must be a string of length >= 2") % long) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (not ((short is None) or | 
					
						
							| 
									
										
										
										
											2007-10-16 18:12:55 +00:00
										 |  |  |                      (isinstance(short, str) and len(short) == 1))): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsGetoptError("invalid short option '%s': " | 
					
						
							|  |  |  |                        "must a single character or None" % short) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-04 20:24:05 +00:00
										 |  |  |             self.repeat[long] = repeat | 
					
						
							| 
									
										
										
										
											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]: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                         raise DistutilsGetoptError( | 
					
						
							|  |  |  |                               "invalid negative alias '%s': " | 
					
						
							|  |  |  |                               "aliased option '%s' takes a value" | 
					
						
							|  |  |  |                               % (long, alias_to)) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     self.long_opts[-1] = long # XXX redundant?! | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 self.takes_arg[long] = 0 | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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]: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                     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): | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |                 raise DistutilsGetoptError( | 
					
						
							|  |  |  |                        "invalid long option name '%s' " | 
					
						
							|  |  |  |                        "(must be letters, numbers, hyphens only" % long) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def getopt(self, args=None, object=None): | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |         """Parse command-line options in args. Store as attributes on 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 left untouched. | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             created_object = True | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             created_object = False | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self._grok_option_table() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-17 08:48:32 +00:00
										 |  |  |         short_opts = ' '.join(self.short_opts) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |             opts, args = getopt.getopt(args, short_opts, self.long_opts) | 
					
						
							| 
									
										
										
										
											2007-01-10 16:19:56 +00:00
										 |  |  |         except getopt.error as msg: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise DistutilsArgError(msg) | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |         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]] | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |                 assert len(opt) > 2 and opt[:2] == '--' | 
					
						
							|  |  |  |                 opt = opt[2:] | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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? | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |                 assert val == '', "boolean option can't have 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] | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |             # The only repeating option at the moment is 'verbose'. | 
					
						
							|  |  |  |             # It has a negative option -q quiet, which should set verbose = 0. | 
					
						
							|  |  |  |             if val and self.repeat.get(attr) is not None: | 
					
						
							|  |  |  |                 val = getattr(object, attr, 0) + 1 | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2002-06-04 20:14:43 +00:00
										 |  |  |             return args, object | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return args | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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: | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |             raise RuntimeError("'getopt()' hasn't been called yet") | 
					
						
							| 
									
										
										
										
											2000-04-21 01:41:54 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return self.option_order | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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:'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-04 21:10:35 +00:00
										 |  |  |         for option in self.option_table: | 
					
						
							| 
									
										
										
										
											2002-06-04 21:11:56 +00:00
										 |  |  |             long, short, help = option[:3] | 
					
						
							| 
									
										
										
										
											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-04-21 02:09:26 +00:00
										 |  |  |         return lines | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-03-22 14:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-31 10:37:15 +00:00
										 |  |  | WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +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] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-17 08:48:32 +00:00
										 |  |  |     text = text.expandtabs() | 
					
						
							|  |  |  |     text = text.translate(WS_TRANS) | 
					
						
							| 
									
										
										
										
											2000-09-26 02:12:31 +00:00
										 |  |  |     chunks = re.split(r'( +|-+)', text) | 
					
						
							| 
									
										
										
										
											2007-09-25 21:48:09 +00:00
										 |  |  |     chunks = [ch for ch in chunks if ch] # ' - ' 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! | 
					
						
							| 
									
										
										
										
											2007-04-17 08:48:32 +00:00
										 |  |  |         lines.append(''.join(cur_line)) | 
					
						
							| 
									
										
										
										
											1999-12-12 16:54:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return lines | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-25 01:58:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  | def translate_longopt(opt): | 
					
						
							| 
									
										
										
										
											2000-09-25 01:58:31 +00:00
										 |  |  |     """Convert a long option name to a valid Python identifier by
 | 
					
						
							|  |  |  |     changing "-" to "_". | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2009-09-21 13:01:54 +00:00
										 |  |  |     return opt.translate(longopt_xlate) | 
					
						
							| 
									
										
										
										
											2001-12-06 20:51:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-30 03:52:21 +00:00
										 |  |  |     def __init__(self, options=[]): | 
					
						
							| 
									
										
										
										
											2000-05-23 01:44:20 +00:00
										 |  |  |         """Create a new OptionDummy instance.  The attributes listed in
 | 
					
						
							|  |  |  |         'options' will be initialized to None."""
 | 
					
						
							|  |  |  |         for opt in options: | 
					
						
							|  |  |  |             setattr(self, opt, None) | 
					
						
							| 
									
										
										
										
											2010-07-22 12:50:05 +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) | 
					
						
							|  |  |  |         print("\n".join(wrap_text(text, w))) | 
					
						
							|  |  |  |         print() |