diff options
author | Craig Silverstein <csilvers+gflags@google.com> | 2007-03-22 00:18:13 +0000 |
---|---|---|
committer | Craig Silverstein <csilvers+gflags@google.com> | 2007-03-22 00:18:13 +0000 |
commit | 573580dcd64b95a01cf5babec6031ef664c8248d (patch) | |
tree | e4cffe60252f3f0ffa4d5b854dd19ca473d99a16 | |
parent | b9f23483e1569615bb0801f1763e8b3a33989284 (diff) | |
download | gflags-573580dcd64b95a01cf5babec6031ef664c8248d.tar.gz gflags-573580dcd64b95a01cf5babec6031ef664c8248d.tar.bz2 gflags-573580dcd64b95a01cf5babec6031ef664c8248d.zip |
gflags 0.2
git-svn-id: https://gflags.googlecode.com/svn/trunk@9 6586e3c6-dcc4-952a-343f-ff74eb82781d
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | README | 17 | ||||
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | packages/rpm/rpm.spec | 5 | ||||
-rwxr-xr-x | python/gflags.py | 1368 | ||||
-rwxr-xr-x | python/gflags2man.py (renamed from contrib/gflags2man.py) | 239 | ||||
-rwxr-xr-x | python/gflags_unittest.py | 800 | ||||
-rwxr-xr-x | python/setup.py | 42 | ||||
-rw-r--r-- | src/google/gflags.h.in | 1 |
10 files changed, 2392 insertions, 108 deletions
@@ -6,3 +6,9 @@ Wed Dec 13 12:37:19 2006 Google Inc. <opensource@google.com> has increased flexibility, including built-in support for C++ types like string, and the ability to define flags in the source file in which they're used. + +Mon Jan 22 15:33:06 2007 Google Inc. <opensource@google.com> + + * google-gflags: version 0.2 + * added support for python commandlineflags, as well as c++ + * gflags2man, a script to turn flags into a man page (dchristian) @@ -1 +1,16 @@ -TODO +This repository contains both a C++ and a python implementation of the +Google commandline flags module. Documentation for the C++ +implementation is in doc/. Documentation for the python +implementation is at the top of gflags/flags.py. + +See INSTALL for (generic) installation instructions for C++: basically + ./configure && make && make install + +To install the python module, run + cd python; python ./setup.py install + +When you install the python library, you also get a helper +application, gflags2man.py, installed into /usr/local/bin. You can +run gflags2man.py to create an instant man page, with all the +commandline flags and their docs, for any C++ or python program you've +written using the gflags library. @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for gflags 0.1. +# Generated by GNU Autoconf 2.59 for gflags 0.2. # # Report bugs to <opensource@google.com>. # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='gflags' PACKAGE_TARNAME='gflags' -PACKAGE_VERSION='0.1' -PACKAGE_STRING='gflags 0.1' +PACKAGE_VERSION='0.2' +PACKAGE_STRING='gflags 0.2' PACKAGE_BUGREPORT='opensource@google.com' ac_unique_file="README" @@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gflags 0.1 to adapt to many kinds of systems. +\`configure' configures gflags 0.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1020,7 +1020,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gflags 0.1:";; + short | recursive ) echo "Configuration of gflags 0.2:";; esac cat <<\_ACEOF @@ -1163,7 +1163,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -gflags configure 0.1 +gflags configure 0.2 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1177,7 +1177,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gflags $as_me 0.1, which was +It was created by gflags $as_me 0.2, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1823,7 +1823,7 @@ fi # Define the identity of the package. PACKAGE='gflags' - VERSION='0.1' + VERSION='0.2' cat >>confdefs.h <<_ACEOF @@ -21158,7 +21158,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by gflags $as_me 0.1, which was +This file was extended by gflags $as_me 0.2, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21221,7 +21221,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -gflags config.status 0.1 +gflags config.status 0.2 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.ac b/configure.ac index ae5c126..98a07c2 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ # make sure we're interpreted by some minimal autoconf AC_PREREQ(2.57) -AC_INIT(gflags, 0.1, opensource@google.com) +AC_INIT(gflags, 0.2, opensource@google.com) # The argument here is just something that should be in the current directory # (for sanity checking) AC_CONFIG_SRCDIR(README) diff --git a/packages/rpm/rpm.spec b/packages/rpm/rpm.spec index f47e23c..35d177d 100644 --- a/packages/rpm/rpm.spec +++ b/packages/rpm/rpm.spec @@ -8,11 +8,11 @@ Summary: A commandline flags library that allows for distributed flags Version: %ver Release: %rel Group: Development/Libraries -URL: http://goog-gflags.sourceforge.net +URL: http://code.google.com/p/google-gflags License: BSD Vendor: Google Packager: Google Inc. <opensource@google.com> -Source: http://goog-gflags.sourceforge.net/%{NAME}-%{PACKAGE_VERSION}.tar.gz +Source: http://google-gflags.googlecode.com/files/%{NAME}-%{PACKAGE_VERSION}.tar.gz Distribution: Redhat 7 and above. Buildroot: %{_tmppath}/%{name}-root Prefix: %prefix @@ -61,7 +61,6 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root) %{prefix}/include/google -%{prefix}/lib/debug %{prefix}/lib/libgflags.a %{prefix}/lib/libgflags.la %{prefix}/lib/libgflags.so diff --git a/python/gflags.py b/python/gflags.py new file mode 100755 index 0000000..257b6f5 --- /dev/null +++ b/python/gflags.py @@ -0,0 +1,1368 @@ +#!/usr/bin/env python + +# Copyright (c) 2007, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# --- +# Author: Chad Lester +# Design and style contributions by: +# Amit Patel, Bogdan Cocosel, Daniel Dulitz, Eric Tiedemann, +# Eric Veach, Laurence Gonsalves, Matthew Springer +# Code reorganized a bit by Craig Silverstein + +""" +This module is used to define and parse command line flags. + +This module defines a *distributed* flag-definition policy: rather +than an application having to define all flags in or near main(), each +python module defines flags that are useful to it. When one python +module imports another, it gains access to the other's flags. (This +is implemented by having all modules share a common, global registry +object containing all the flag information.) + +Flags are defined through the use of one of the DEFINE_xxx functions. +The specific function used determines how the flag is parsed, checked, +and optionally type-converted, when it's seen on the command line. + + +IMPLEMENTATION: DEFINE_* creates a 'Flag' object and registers it with +a 'FlagValues' object (typically the global FlagValues FLAGS, defined +here). The 'FlagValues' object can scan the command line arguments +and pass flag arguments to the corresponding 'Flag' objects for +value-checking and type conversion. The converted flag values are +available as members of the 'FlagValues' object. + +Code can access the flag through a FlagValues object, for instancee +gflags.FLAGS.myflag. Typically, the __main__ module passes the +command line arguments to gflags.FLAGS for parsing. + +At bottom, this module calls getopt(), so getopt functionality is +supported, including short- and long-style flags, and the use of -- to +terminate flags. + +Methods defined by the flag module will throw 'FlagsError' exceptions. +The exception argument will be a human-readable string. + + +FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All +flags take a name, default value, help-string, and optional 'short' +name (one-letter name). Some flags have other arguments, which are +described with the flag. + +DEFINE_string: takes any input, and interprets it as a string. + +DEFINE_boolean: typically does not take an argument: say --myflag to + set FLAGS.myflag to true, or --nomyflag to set + FLAGS.myflag to false. Alternately, you can say + --myflag=true or --myflag=t or --myflag=1 or + --myflag=false or --myflag=f or --myflag=0 + +DEFINE_float: takes an input and interprets it as a floating point + number. Takes optional args lower_bound and + upper_bound; if the number specified on the command line + is out of range, it will raise a FlagError. + +DEFINE_integer: takes an input and interprets it as an integer. Takes + optional args lower_bound and upper_bound as for floats. + +DEFINE_enum: takes a list of strings which represents legal values. If + the command-line value is not in this list, raise a flag + error. Otherwise, assign to FLAGS.flag as a string. + +DEFINE_list: Takes a comma-separated list of strings on the commandline. + Stores them in a python list object. + +DEFINE_spaceseplist: Takes a space-separated list of strings on the + commandline. Stores them in a python list object. + +DEFINE_multistring: The same as DEFINE_string, except the flag can be + specified more than once on the commandline. The + result is a python list object (list of strings), + even if the flag is only on the command line once. + +DEFINE_multi_int: The same as DEFINE_integer, except the flag can be + specified more than once on the commandline. The + result is a python list object (list of ints), + even if the flag is only on the command line once. + + +SPECIAL FLAGS: There are a few flags that have special meaning: + --help (or -?) prints a list of all the flags in a human-readable fashion + --helpshort prints a list of all the flags in the 'main' .py file only + --flagfile=foo read flags from foo. + -- as in getopt(), terminates flag-processing + +Note on --flagfile: + +Flags may be loaded from text files in addition to being specified on +the commandline. + +Any flags you don't feel like typing, throw them in a file, one flag +per line, for instance: + --myflag=myvalue + --nomyboolean_flag +You then specify your file with the special flag +'--flagfile=somefile'. You CAN recursively nest flagfile= tokens OR +use multiple files on the command line. Lines beginning with a single +hash '#' or a double slash '//' are comments in your flagfile. + +Any flagfile=<file> will be interpreted as having a relative path from +the current working directory rather than from the place the file was +included from: + myPythonScript.py --flagfile=config/somefile.cfg + +If somefile.cfg includes further --flagfile= directives, these will be +referenced relative to the original CWD, not from the directory the +including flagfile was found in! + +The caveat applies to people who are including a series of nested +files in a different dir than they are executing out of. Relative +path names are always from CWD, not from the directory of the parent +include flagfile. We do now support '~' expanded directory names. + +Absolute path names ALWAYS work! + + +EXAMPLE USAGE: + + import gflags + FLAGS = gflags.FLAGS + + # Flag names are globally defined! So in general, we need to be + # careful to pick names that are unlikely to be used by other libraries. + # If there is a conflict, we'll get an error at import time. + gflags.DEFINE_string("name", "Mr. President" "NAME: your name") + gflags.DEFINE_integer("age", None, "AGE: your age in years", lower_bound=0) + gflags.DEFINE_boolean("debug", 0, "produces debugging output") + gflags.DEFINE_enum("gender", "male", ["male", "female"], + "GENDER: your gender") + + def main(argv): + try: + argv = FLAGS(argv) # parse flags + except gflags.FlagsError, e: + print '%s\\nUsage: %s ARGS\\n%s' % (e, sys.argv[0], FLAGS) + sys.exit(1) + if FLAGS.debug: print 'non-flag arguments:', argv + print 'Happy Birthday', FLAGS.name + if FLAGS.age != None: + print "You are a %s, who is %d years old" % (FLAGS.gender, FLAGS.age) + + if __name__ == '__main__': main(sys.argv) +""" + +import getopt +import os +import sys + +# Are we running at least python 2.2? +try: + if tuple(sys.version_info[:3]) < (2,2,0): + raise NotImplementedError("requires python 2.2.0 or later") +except AttributeError: # a very old python, that lacks sys.version_info + raise NotImplementedError("requires python 2.2.0 or later") + +# Are we running under pychecker? +_RUNNING_PYCHECKER = 'pychecker.python' in sys.modules + +# module exceptions: +class FlagsError(Exception): "The base class for all flags errors" +class DuplicateFlag(FlagsError): "Thrown if there is a flag naming conflict" +class IllegalFlagValue(FlagsError): "The flag command line argument is illegal" + +# Global variable used by expvar +_exported_flags = {} + + +def __GetModuleName(globals_dict): + """Given a globals dict, find the module in which it's defined.""" + for name, module in sys.modules.iteritems(): + if getattr(module, '__dict__', None) is globals_dict: + if name == '__main__': + return sys.argv[0] + return name + raise AssertionError, "No module was found" + +def __GetCallingModule(): + """Get the name of the module that's calling into this module; e.g., + the module calling a DEFINE_foo... function. + """ + # Walk down the stack to find the first globals dict that's not ours. + for depth in range(1, sys.getrecursionlimit()): + if not sys._getframe(depth).f_globals is globals(): + return __GetModuleName(sys._getframe(depth).f_globals) + raise AssertionError, "No module was found" + +def _GetMainModule(): + """Get the module name from which execution started.""" + for depth in range(1, sys.getrecursionlimit()): + try: + globals_of_main = sys._getframe(depth).f_globals + except ValueError: + return __GetModuleName(globals_of_main) + raise AssertionError, "No module was found" + + +class FlagValues: + """ + Used as a registry for 'Flag' objects. + + A 'FlagValues' can then scan command line arguments, passing flag + arguments through to the 'Flag' objects that it owns. It also + provides easy access to the flag values. Typically only one + 'FlagValues' object is needed by an application: gflags.FLAGS + + This class is heavily overloaded: + + 'Flag' objects are registered via __setitem__: + FLAGS['longname'] = x # register a new flag + + The .value member of the registered 'Flag' objects can be accessed as + members of this 'FlagValues' object, through __getattr__. Both the + long and short name of the original 'Flag' objects can be used to + access its value: + FLAGS.longname # parsed flag value + FLAGS.x # parsed flag value (short name) + + Command line arguments are scanned and passed to the registered 'Flag' + objects through the __call__ method. Unparsed arguments, including + argv[0] (e.g. the program name) are returned. + argv = FLAGS(sys.argv) # scan command line arguments + + The original registered Flag objects can be retrieved through the use + of the dictionary-like operator, __getitem__: + x = FLAGS['longname'] # access the registered Flag object + + The str() operator of a 'FlagValues' object provides help for all of + the registered 'Flag' objects. + """ + def __init__(self): + # Since everything in this class is so heavily overloaded, + # the only way of defining and using fields is to access __dict__ + # directly. + self.__dict__['__flags'] = {} + self.__dict__['__flags_by_module'] = {} # A dict module -> list of flag + + def FlagDict(self): + return self.__dict__['__flags'] + + def _RegisterFlagByModule(self, module_name, flag): + """We keep track of which flag is defined by which module so that + we can later sort the flags by module. + """ + flags_by_module = self.__dict__['__flags_by_module'] + flags_by_module.setdefault(module_name, []).append(flag) + + def __setitem__(self, name, flag): + """ + Register a new flag variable. + """ + fl = self.FlagDict() + if not isinstance(flag, Flag): + raise IllegalFlagValue, flag + if not isinstance(name, type("")): + raise FlagsError, "Flag name must be a string" + if len(name) == 0: + raise FlagsError, "Flag name cannot be empty" + # If running under pychecker, duplicate keys are likely to be defined. + # Disable check for duplicate keys when pycheck'ing. + if (fl.has_key(name) and not flag.allow_override and + not fl[name].allow_override and not _RUNNING_PYCHECKER): + raise DuplicateFlag, name + short_name = flag.short_name + if short_name is not None: + if (fl.has_key(short_name) and not flag.allow_override and + not fl[short_name].allow_override and not _RUNNING_PYCHECKER): + raise DuplicateFlag, short_name + fl[short_name] = flag + fl[name] = flag + global _exported_flags + _exported_flags[name] = flag + + def __getitem__(self, name): + """ + Retrieve the flag object. + """ + return self.FlagDict()[name] + + def __getattr__(self, name): + """ + Retrieve the .value member of a flag object. + """ + fl = self.FlagDict() + if not fl.has_key(name): + raise AttributeError, name + return fl[name].value + + def __setattr__(self, name, value): + """ + Set the .value member of a flag object. + """ + fl = self.FlagDict() + fl[name].value = value + return value + + def __delattr__(self, name): + """ + Delete a previously-defined flag from a flag object. + """ + fl = self.FlagDict() + if not fl.has_key(name): + raise AttributeError, name + del fl[name] + + def SetDefault(self, name, value): + """ + Change the default value of the named flag object. + """ + fl = self.FlagDict() + if not fl.has_key(name): + raise AttributeError, name + fl[name].SetDefault(value) + + def __contains__(self, name): + """ + Return True if name is a value (flag) in the dict. + """ + return name in self.FlagDict() + + has_key = __contains__ # a synonym for __contains__() + + def __iter__(self): + return self.FlagDict().iterkeys() + + def __call__(self, argv): + """ + Searches argv for flag arguments, parses them and then sets the flag + values as attributes of this FlagValues object. All unparsed + arguments are returned. Flags are parsed using the GNU Program + Argument Syntax Conventions, using getopt: + + http://www.gnu.org/software/libc/manual/html_mono/libc.html#Getopt + """ + + # Support any sequence type that can be converted to a list + argv = list(argv) + + shortopts = "" + longopts = [] + + fl = self.FlagDict() + + # This pre parses the argv list for --flagfile=<> options. + argv = self.ReadFlagsFromFiles(argv) + + # Correct the argv to support the google style of passing boolean + # parameters. Boolean parameters may be passed by using --mybool, + # --nomybool, --mybool=(true|false|1|0). getopt does not support + # having options that may or may not have a parameter. We replace + # instances of the short form --mybool and --nomybool with their + # full forms: --mybool=(true|false). + original_argv = list(argv) + shortest_matches = None + for name, flag in fl.items(): + if not flag.boolean: + continue + if shortest_matches is None: + # Determine the smallest allowable prefix for all flag names + shortest_matches = self.ShortestUniquePrefixes(fl) + no_name = 'no' + name + prefix = shortest_matches[name] + no_prefix = shortest_matches[no_name] + + # Replace all occurences of this boolean with extended forms + for arg_idx in range(1, len(argv)): + arg = argv[arg_idx] + if arg.find('=') >= 0: continue + if arg.startswith('--'+prefix) and ('--'+name).startswith(arg): + argv[arg_idx] = ('--%s=true' % name) + elif arg.startswith('--'+no_prefix) and ('--'+no_name).startswith(arg): + argv[arg_idx] = ('--%s=false' % name) + + # Loop over all of the flags, building up the lists of short options and + # long options that will be passed to getopt. Short options are + # specified as a string of letters, each letter followed by a colon if it + # takes an argument. Long options are stored in an array of strings. + # Each string ends with an '=' if it takes an argument. + for name, flag in fl.items(): + longopts.append(name + "=") + if len(name) == 1: # one-letter option: allow short flag type also + shortopts += name + if not flag.boolean: + shortopts += ":" + + try: + optlist, unparsed_args = getopt.getopt(argv[1:], shortopts, longopts) + except getopt.GetoptError, e: + raise FlagsError, e + for name, arg in optlist: + if name.startswith('--'): + # long option + name = name[2:] + short_option = 0 + else: + # short option + name = name[1:] + short_option = 1 + if fl.has_key(name): + flag = fl[name] + if flag.boolean and short_option: arg = 1 + flag.Parse(arg) + + if unparsed_args: + # unparsed_args becomes the first non-flag detected by getopt to + # the end of argv. Because argv may have been modified above, + # return original_argv for this region. + return argv[:1] + original_argv[-len(unparsed_args):] + else: + return argv[:1] + + def Reset(self): + """ + Reset the values to the point before FLAGS(argv) was called. + """ + for f in self.FlagDict().values(): + f.Unparse() + + def RegisteredFlags(self): + """ + Return a list of all registered flags. + """ + return self.FlagDict().keys() + + def FlagValuesDict(self): + """ + Return a dictionary with flag names as keys and flag values as values. + """ + flag_values = {} + + for flag_name in self.RegisteredFlags(): + flag = self.FlagDict()[flag_name] + flag_values[flag_name] = flag.value + + return flag_values + + def __str__(self): + """ + Generate a help string for all known flags. + """ + helplist = [] + + flags_by_module = self.__dict__['__flags_by_module'] + if flags_by_module: + + modules = flags_by_module.keys() + modules.sort() + + # Print the help for the main module first, if possible. + main_module = _GetMainModule() + if main_module in modules: + modules.remove(main_module) + modules = [ main_module ] + modules + + for module in modules: + self.__RenderModuleFlags(module, helplist) + + else: + # Just print one long list of flags. + self.__RenderFlagList(self.FlagDict().values(), helplist) + + return '\n'.join(helplist) + + def __RenderModuleFlags(self, module, output_lines): + """ + Generate a help string for a given module. + """ + flags_by_module = self.__dict__['__flags_by_module'] + if module in flags_by_module: + output_lines.append('\n%s:' % module) + self.__RenderFlagList(flags_by_module[module], output_lines) + + def MainModuleHelp(self): + """ + Generate a help string for all known flags of the main module. + """ + helplist = [] + self.__RenderModuleFlags(_GetMainModule(), helplist) + return '\n'.join(helplist) + + def __RenderFlagList(self, flaglist, output_lines): + fl = self.FlagDict() + flaglist = [(flag.name, flag) for flag in flaglist] + flaglist.sort() + flagset = {} + for (name, flag) in flaglist: + # It's possible this flag got deleted or overridden since being + # registered in the per-module flaglist. Check now against the + # canonical source of current flag information, the FlagDict. + if fl.get(name, None) != flag: # a different flag is using this name now + continue + # only print help once + if flagset.has_key(flag): continue + flagset[flag] = 1 + flaghelp = " " + if flag.short_name: flaghelp += "-%s," % flag.short_name + if flag.boolean: + flaghelp += "--[no]%s" % flag.name + ":" + else: + flaghelp += "--%s" % flag.name + ":" + flaghelp += " " + if flag.help: + flaghelp += flag.help + if flag.default_as_str: + flaghelp += "\n (default: %s)" % flag.default_as_str + if flag.parser.syntactic_help: + flaghelp += "\n (%s)" % flag.parser.syntactic_help + output_lines.append(flaghelp) + + def get(self, name, default): + """ + Retrieve the .value member of a flag object, or default if .value is None + """ + + value = self.__getattr__(name) + if value is not None: # Can't do if not value, b/c value might be '0' or "" + return value + else: + return default + + def ShortestUniquePrefixes(self, fl): + """ + Returns a dictionary mapping flag names to their shortest unique prefix. + """ + # Sort the list of flag names + sorted_flags = [] + for name, flag in fl.items(): + sorted_flags.append(name) + if flag.boolean: + sorted_flags.append('no%s' % name) + sorted_flags.sort() + + # For each name in the sorted list, determine the shortest unique prefix + # by comparing itself to the next name and to the previous name (the latter + # check uses cached info from the previous loop). + shortest_matches = {} + prev_idx = 0 + for flag_idx in range(len(sorted_flags)): + curr = sorted_flags[flag_idx] + if flag_idx == (len(sorted_flags) - 1): + next = None + else: + next = sorted_flags[flag_idx+1] + next_len = len(next) + for curr_idx in range(len(curr)): + if (next is None + or curr_idx >= next_len + or curr[curr_idx] != next[curr_idx]): + # curr longer than next or no more chars in common + shortest_matches[curr] = curr[:max(prev_idx, curr_idx) + 1] + prev_idx = curr_idx + break + else: + # curr shorter than (or equal to) next + shortest_matches[curr] = curr + prev_idx = curr_idx + 1 # next will need at least one more char + return shortest_matches + + def __IsFlagFileDirective(self, flag_string): + """ Detects the --flagfile= token. + Takes a string which might contain a '--flagfile=<foo>' directive. + Returns a Boolean. + """ + if isinstance(flag_string, type("")): + if flag_string.startswith('--flagfile='): + return 1 + elif flag_string == '--flagfile': + return 1 + elif flag_string.startswith('-flagfile='): + return 1 + elif flag_string == '-flagfile': + return 1 + else: + return 0 + return 0 + + def ExtractFilename(self, flagfile_str): + """Function to remove the --flagfile= (or variant) and return just the + filename part. We can get strings that look like: + --flagfile=foo, -flagfile=foo. + The case of --flagfile foo and -flagfile foo shouldn't be hitting this + function, as they are dealt with in the level above this funciton. + """ + if flagfile_str.startswith('--flagfile='): + return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip()) + elif flagfile_str.startswith('-flagfile='): + return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip()) + else: + raise FlagsError('Hit illegal --flagfile type: %s' % flagfile_str) + return '' + + + def __GetFlagFileLines(self, filename, parsed_file_list): + """Function to open a flag file, return its useful (!=comments,etc) lines. + Takes: + A filename to open and read + A list of files we have already read THAT WILL BE CHANGED + Returns: + List of strings. See the note below. + + NOTE(springer): This function checks for a nested --flagfile=<foo> + tag and handles the lower file recursively. It returns a list off + all the lines that _could_ contain command flags. This is + EVERYTHING except whitespace lines and comments (lines starting + with '#' or '//'). + """ + line_list = [] # All line from flagfile. + flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags. + try: + file_obj = open(filename, 'r') + except IOError, e_msg: + print e_msg + print 'ERROR:: Unable to open flagfile: %s' % (filename) + return flag_line_list + + line_list = file_obj.readlines() + file_obj.close() + parsed_file_list.append(filename) + + # This is where we check each line in the file we just read. + for line in line_list: + if line.isspace(): + pass + # Checks for comment (a line that starts with '#'). + elif (line.startswith('#') or line.startswith('//')): + pass + # Checks for a nested "--flagfile=<bar>" flag in the current file. + # If we find one, recursively parse down into that file. + elif self.__IsFlagFileDirective(line): + sub_filename = self.ExtractFilename(line) + # We do a little safety check for reparsing a file we've already done. + if not sub_filename in parsed_file_list: + included_flags = self.__GetFlagFileLines(sub_filename, parsed_file_list) + flag_line_list.extend(included_flags) + else: # Case of hitting a circularly included file. + print >>sys.stderr, ('Warning: Hit circular flagfile dependency: %s' + % sub_filename) + else: + # Any line that's not a comment or a nested flagfile should + # get copied into 2nd position, this leaves earlier arguements + # further back in the list, which makes them have higher priority. + flag_line_list.append(line.strip()) + return flag_line_list + + def ReadFlagsFromFiles(self, argv): + """Process command line args, but also allow args to be read from file + Usage: + Takes: a list of strings, usually sys.argv, which may contain one or more + flagfile directives of the form --flagfile="./filename" + References: Global gflags.FLAG class instance + Returns: a new list which has the original list combined with what we + read from any flagfile(s). + + This function should be called before the normal FLAGS(argv) call. + This function simply scans the input list for a flag that looks like: + --flagfile=<somefile> + Then it opens <somefile>, reads all valid key and value pairs and inserts + them into the input list between the first item of the list and any + subsequent items in the list. + Note that your application's flags are still defined the usual way using + gflags DEFINE_flag() type functions. + + Notes (assuming we're getting a commandline of some sort as our input): + --> Any flags on the command line we were passed in _should_ always take + precedence!!! + --> a further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile. + It will be processed after the parent flag file is done. + --> For duplicate flags, first one we hit should "win". + --> In a flagfile, a line beginning with # or // is a comment + --> Entirely blank lines _should_ be ignored + """ + parsed_file_list = [] + rest_of_args = argv + new_argv = [] + while rest_of_args: + current_arg = rest_of_args[0] + rest_of_args = rest_of_args[1:] + if self.__IsFlagFileDirective(current_arg): + # This handles the case of -(-)flagfile foo. Inthis case the next arg + # really is part of this one. + if current_arg == '--flagfile' or current_arg =='-flagfile': + if not rest_of_args: + raise IllegalFlagValue, '--flagfile with no argument' + flag_filename = os.path.expanduser(rest_of_args[0]) + rest_of_args = rest_of_args[1:] + else: + # This handles the case of (-)-flagfile=foo. + flag_filename = self.ExtractFilename(current_arg) + new_argv = (new_argv[:1] + + self.__GetFlagFileLines(flag_filename, parsed_file_list) + + new_argv[1:]) + else: + new_argv.append(current_arg) + + return new_argv + + def FlagsIntoString(self): + """ + Retreive a string version of all the flags with assignments stored + in this FlagValues object. Should mirror the behavior of the c++ + version of FlagsIntoString. Each flag assignment is seperated by + a newline. + """ + s = '' + for flag in self.FlagDict().values(): + if flag.value is not None: + s += flag.Serialize() + '\n' + return s + + def AppendFlagsIntoFile(self, filename): + """ + Appends all flags found in this FlagInfo object to the file + specified. Output will be in the format of a flagfile. This + should mirror the behavior of the c++ version of + AppendFlagsIntoFile. + """ + out_file = open(filename, 'a') + out_file.write(self.FlagsIntoString()) + out_file.close() + +#end of the FLAGS registry class + + +# The global FlagValues instance +FLAGS = FlagValues() + + +class Flag: + """ + 'Flag' objects define the following fields: + .name - the name for this flag + .default - the default value for this flag + .default_as_str - default value as repr'd string, e.g., "'true'" (or None) + .value - the most recent parsed value of this flag; set by Parse() + .help - a help string or None if no help is available + .short_name - the single letter alias for this flag (or None) + .boolean - if 'true', this flag does not accept arguments + .present - true if this flag was parsed from command line flags. + .parser - an ArgumentParser object + .serializer - an ArgumentSerializer object + .allow_override - the flag may be redefined without raising an error + + The only public method of a 'Flag' object is Parse(), but it is + typically only called by a 'FlagValues' object. The Parse() method is + a thin wrapper around the 'ArgumentParser' Parse() method. The parsed + value is saved in .value, and the .present member is updated. If this + flag was already present, a FlagsError is raised. + + Parse() is also called during __init__ to parse the default value and + initialize the .value member. This enables other python modules to + safely use flags even if the __main__ module neglects to parse the + command line arguments. The .present member is cleared after __init__ + parsing. If the default value is set to None, then the __init__ + parsing step is skipped and the .value member is initialized to None. + + Note: The default value is also presented to the user in the help + string, so it is important that it be a legal value for this flag. + """ + def __init__(self, parser, serializer, name, default, help_string, + short_name=None, boolean=0, allow_override=0): + self.name = name + self.default = default + + if not help_string: + help_string = '(no help available)' + + self.help = help_string + self.short_name = short_name + self.boolean = boolean + self.present = 0 + self.parser = parser + self.serializer = serializer + self.allow_override = allow_override + self.value = None + + # We can't allow a None override because it may end up not being + # passed to C++ code when we're overriding C++ flags. So we + # cowardly bail out until someone fixes the semantics of trying to + # pass None to a C++ flag. See swig_flags.Init() for details on + # this behavior. + if default is None and allow_override: + raise DuplicateFlag, name + + self.Unparse() + + self.default_as_str = self.__GetParsedValueAsString(self.value) + + def __GetParsedValueAsString(self, value): + if self.boolean: + if value: + return repr('true') + else: + return repr('false') + if value is None: + return None + return repr(str(value)) + + def Parse(self, argument): + try: + self.value = self.parser.Parse(argument) + except ValueError, e: # recast ValueError as IllegalFlagValue + raise IllegalFlagValue, ("flag --%s: " % self.name) + str(e) + self.present += 1 + + def Unparse(self): + if self.default != None: + self.Parse(self.default) + self.present = 0 + + def Serialize(self): + if self.value is None: + return '' + if self.boolean: + if self.value: + return "--%s" % self.name + else: + return "--no%s" % self.name + else: + if not self.serializer: + raise FlagsError, "Serializer not present for flag %s" % self.name + return "--%s=%s" % (self.name, self.serializer.Serialize(self.value)) + + def SetDefault(self, value): + """ + Change the default value, and current value, of this flag object + """ + if value: # See __init__ for logic details + self.Parse(value) + self.present -= 1 # reset .present after parsing new default value + else: + self.value = None + self.default = value + self.default_as_str = self.__GetParsedValueAsString(value) + +class ArgumentParser: + """ + This is a base class used to parse and convert arguments. + + The Parse() method checks to make sure that the string argument is a + legal value and convert it to a native type. If the value cannot be + converted, it should throw a 'ValueError' exception with a human + readable explanation of why the value is illegal. + + Subclasses should also define a syntactic_help string which may be + presented to the user to describe the form of the legal values. + """ + syntactic_help = "" + def Parse(self, argument): + """ + The default implementation of Parse() accepts any value of argument, + simply returning it unmodified. + """ + return argument + +class ArgumentSerializer: + """ + This is the base class for generating string representations of a + flag value + """ + def Serialize(self, value): + return str(value) + +class ListSerializer(ArgumentSerializer): + def __init__(self, list_sep): + self.list_sep = list_sep + + def Serialize(self, value): + return self.list_sep.join([str(x) for x in value]) + + +# The DEFINE functions are explained in the module doc string. + +def DEFINE(parser, name, default, help, flag_values=FLAGS, serializer=None, + **args): + """ + This creates a generic 'Flag' object that parses its arguments with a + 'Parser' and registers it with a 'FlagValues' object. + + Developers who need to create their own 'Parser' classes should call + this module function. to register their flags. For example: + + DEFINE(DatabaseSpec(), "dbspec", "mysql:db0:readonly:hr", + "The primary database") + """ + DEFINE_flag(Flag(parser, serializer, name, default, help, **args), + flag_values) + +def DEFINE_flag(flag, flag_values=FLAGS): + """ + This registers a 'Flag' object with a 'FlagValues' object. By + default, the global FLAGS 'FlagValue' object is used. + + Typical users will use one of the more specialized DEFINE_xxx + functions, such as DEFINE_string or DEFINEE_integer. But developers + who need to create Flag objects themselves should use this function to + register their flags. + """ + # copying the reference to flag_values prevents pychecker warnings + fv = flag_values + fv[flag.name] = flag + + if flag_values == FLAGS: + # We are using the global flags dictionary, so we'll want to sort the + # usage output by calling module in FlagValues.__str__ (FLAGS is an + # instance of FlagValues). This requires us to keep track + # of which module is creating the flags. + + # Tell FLAGS who's defining flag. + FLAGS._RegisterFlagByModule(__GetCallingModule(), flag) + + +############################### +################# STRING FLAGS +############################### + +def DEFINE_string(name, default, help, flag_values=FLAGS, **args): + """ + This registers a flag whose value can be any string. + """ + parser = ArgumentParser() + serializer = ArgumentSerializer() + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +############################### +################ BOOLEAN FLAGS +############################### +#### and the special HELP flag +############################### + +class BooleanParser(ArgumentParser): + """ + A boolean value + """ + + def Convert(self, argument): + """ + convert the argument to a boolean (integer); raise ValueError on errors + """ + if type(argument) == str: + if argument.lower() in ['true', 't', '1']: + return 1 + elif argument.lower() in ['false', 'f', '0']: + return 0 + return int(argument) + + def Parse(self, argument): + val = self.Convert(argument) + return val + +class BooleanFlag(Flag): + """ + A basic boolean flag. Boolean flags do not take any arguments, and + their value is either 0 (false) or 1 (true). The false value is + specified on the command line by prepending the word 'no' to either + the long or short flag name. + + For example, if a Boolean flag was created whose long name was 'update' + and whose short name was 'x', then this flag could be explicitly unset + through either --noupdate or --nox. + """ + def __init__(self, name, default, help, short_name=None, **args): + p = BooleanParser() + g = ArgumentSerializer() + Flag.__init__(self, p, g, name, default, help, short_name, 1, **args) + if not self.help: self.help = "a boolean value" + +def DEFINE_boolean(name, default, help, flag_values=FLAGS, **args): + """ + This registers a boolean flag - one that does not take an argument. + If a user wants to specify a false value explicitly, the long option + beginning with 'no' must be used: i.e. --noflag + + This flag will have a value of None, 0 or 1. None is possible if + default=None and the user does not specify the flag on the command + line. + """ + DEFINE_flag(BooleanFlag(name, default, help, **args), flag_values) + +class HelpFlag(BooleanFlag): + """ + HelpFlag is a special boolean flag that prints usage information and + raises a SystemExit exception if it is ever found in the command + line arguments. Note this is called with allow_override=1, so other + apps can define their own --help flag, replacing this one, if they want. + """ + def __init__(self): + BooleanFlag.__init__(self, "help", 0, "show this help", + short_name="?", allow_override=1) + def Parse(self, arg): + if arg: + doc = sys.modules["__main__"].__doc__ + flags = str(FLAGS) + print doc or ("\nUSAGE: %s [flags]\n" % sys.argv[0]) + if flags: + print "flags:" + print flags + sys.exit(1) + +class HelpshortFlag(BooleanFlag): + """ + HelpshortFlag is a special boolean flag that prints usage + information for the "main" module, and rasies a SystemExit exception + if it is ever found in the command line arguments. Note this is + called with allow_override=1, so other apps can define their own + --helpshort flag, replacing this one, if they want. + """ + def __init__(self): + BooleanFlag.__init__(self, "helpshort", 0, + "show usage only for this module", allow_override=1) + def Parse(self, arg): + if arg: + doc = sys.modules["__main__"].__doc__ + flags = FLAGS.MainModuleHelp() + print doc or ("\nUSAGE: %s [flags]\n" % sys.argv[0]) + if flags: + print "flags:" + print flags + sys.exit(1) + + +############################### +################## FLOAT FLAGS +############################### + +class FloatParser(ArgumentParser): + """ + A floating point value; optionally bounded to a given upper and lower + bound. + """ + number_article = "a" + number_name = "number" + syntactic_help = " ".join((number_article, number_name)) + + def __init__(self, lower_bound=None, upper_bound=None): + self.lower_bound = lower_bound + self.upper_bound = upper_bound + sh = self.syntactic_help + if lower_bound != None and upper_bound != None: + sh = ("%s in the range [%s, %s]" % (sh, lower_bound, upper_bound)) + elif lower_bound == 1: + sh = "a positive %s" % self.number_name + elif upper_bound == -1: + sh = "a negative %s" % self.number_name + elif lower_bound == 0: + sh = "a non-negative %s" % self.number_name + elif upper_bound != None: + sh = "%s <= %s" % (self.number_name, upper_bound) + elif lower_bound != None: + sh = "%s >= %s" % (self.number_name, lower_bound) + self.syntactic_help = sh + + def Convert(self, argument): + """ + convert the argument to a float; raise ValueError on errors + """ + return float(argument) + + def Parse(self, argument): + val = self.Convert(argument) + if ((self.lower_bound != None and val < self.lower_bound) or + (self.upper_bound != None and val > self.upper_bound)): + raise ValueError, "%s is not %s" % (val, self.syntactic_help) + return val + +def DEFINE_float(name, default, help, lower_bound=None, upper_bound=None, + flag_values = FLAGS, **args): + """ + This registers a flag whose value must be a float. If lower_bound, + or upper_bound are set, then this flag must be within the given range. + """ + parser = FloatParser(lower_bound, upper_bound) + serializer = ArgumentSerializer() + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +############################### +################ INTEGER FLAGS +############################### + +class IntegerParser(FloatParser): + """ + An integer value; optionally bounded to a given upper or lower bound. + """ + number_article = "an" + number_name = "integer" + syntactic_help = " ".join((number_article, number_name)) + def Convert(self, argument): + __pychecker__ = 'no-returnvalues' + if type(argument) == str: + base = 10 + if len(argument) > 2 and argument[0] == "0" and argument[1] == "x": + base=16 + try: + return int(argument, base) + # ValueError is thrown when argument is a string, and overflows an int. + except ValueError: + return long(argument, base) + else: + try: + return int(argument) + # OverflowError is thrown when argument is numeric, and overflows an int. + except OverflowError: + return long(argument) + +def DEFINE_integer(name, default, help, lower_bound=None, upper_bound=None, + flag_values = FLAGS, **args): + """ + This registers a flag whose value must be an integer. If lower_bound, + or upper_bound are set, then this flag must be within the given range. + """ + parser = IntegerParser(lower_bound, upper_bound) + serializer = ArgumentSerializer() + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +############################### +################### ENUM FLAGS +############################### + +class EnumParser(ArgumentParser): + """ + A string enum value + """ + + def __init__(self, enum_values=None): + self.enum_values = enum_values + + def Parse(self, argument): + """ + If enum_values is not specified, any string is allowed + """ + if self.enum_values and argument not in self.enum_values: + raise ValueError, ("value should be one of <%s>" + % "|".join(self.enum_values)) + return argument + +class EnumFlag(Flag): + """ + A basic enum flag. The flag's value can be any string from the list + of enum_values. + """ + def __init__(self, name, default, help, enum_values=[], + short_name=None, **args): + p = EnumParser(enum_values) + g = ArgumentSerializer() + Flag.__init__(self, p, g, name, default, help, short_name, **args) + if not self.help: self.help = "an enum string" + self.help = "<%s>: %s" % ("|".join(enum_values), self.help) + +def DEFINE_enum(name, default, enum_values, help, flag_values=FLAGS, + **args): + """ + This registers a flag whose value can be a string from a set of + specified values. + """ + DEFINE_flag(EnumFlag(name, default, help, enum_values, ** args), + flag_values) + + +############################### +################### LIST FLAGS +############################### + +class BaseListParser(ArgumentParser): + """ + A base class for a string list parser. + To extend, inherit from this class, and call + + BaseListParser.__init__(self, token, name) + + where token is a character used to tokenize, and + name is a description of the separator + """ + + def __init__(self, token=None, name=None): + assert name + self._token = token + self._name = name + self.syntactic_help = "a %s separated list" % self._name + + def Parse(self, argument): + if argument == '': + return [] + else: + return [s.strip() for s in argument.split(self._token)] + + +class ListParser(BaseListParser): + """ + A string list parser (comma-separated) + """ + + def __init__(self): + BaseListParser.__init__(self, ',', 'comma') + +class WhitespaceSeparatedListParser(BaseListParser): + """ + A string list parser (whitespace-separated) + """ + + def __init__(self): + BaseListParser.__init__(self, None, 'whitespace') + + +def DEFINE_list(name, default, help, flag_values=FLAGS, **args): + """ + This registers a flag whose value is a list of strings, separated by commas + """ + parser = ListParser() + serializer = ListSerializer(',') + DEFINE(parser, name, default, help, flag_values, serializer, **args) + +def DEFINE_spaceseplist(name, default, help, flag_values=FLAGS, **args): + """ + This registers a flag whose value is a list of strings, separated by any + whitespace + """ + parser = WhitespaceSeparatedListParser() + serializer = ListSerializer(' ') + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +############################### +################## MULTI FLAGS +############################### + +class MultiFlag(Flag): + """ + MultiFlag is a specialized subclass of Flag that accumulates + multiple values in a list when a command-line option appears + multiple times. + + See the __doc__ for Flag for most behavior of this class. Only + differences in behavior are described here: + * the default value may be a single value -OR- a list of values + * the value of the flag is always a list, even if the option was only + supplied once, and even if the default value is a single value + """ + def __init__(self, *args, **kwargs): + Flag.__init__(self, *args, **kwargs) + + self.help = (self.help + + ';\n repeat this option to specify a list of values') + + def Parse(self, arguments): + """Parse one or more arguments with the installed parser. + + Arguments: + arguments: a single argument or a list of arguments (typically a list + of default values); single arguments will be converted internally into + a list containing one item + """ + if not isinstance(arguments, list): + # Default value may be a list of values. Most other arguments will not + # be, so convert them into a single-item list to make processing simpler + # below. + arguments = [ arguments ] + + if self.present: + # keep a backup reference to list of previously supplied option values + values = self.value + else: + # "erase" the defaults with an empty list + values = [] + + for item in arguments: + # have Flag superclass parse argument, overwriting self.value reference + Flag.Parse(self, item) # also increments self.present + values.append(self.value) + + # put list of option values back in member variable + self.value = values + + def Serialize(self): + if not self.serializer: + raise FlagsError, "Serializer not present for flag %s" % self.name + if self.value is None: + return '' + + s = '' + + multi_value = self.value + + for self.value in multi_value: + if s: s += ' ' + s += Flag.Serialize(self) + + self.value = multi_value + + return s + + +def DEFINE_multi(parser, serializer, name, default, help, flag_values=FLAGS, + **args): + """ + This creates a generic 'MultiFlag' object that parses its arguments with a + 'Parser' and registers it with a 'FlagValues' object. + + Developers who need to create their own 'Parser' classes for options which + can appear multiple times can call this module function to register their + flags. + """ + DEFINE_flag(MultiFlag(parser, serializer, name, default, help, **args), flag_values) + +def DEFINE_multistring(name, default, help, flag_values=FLAGS, **args): + """ + This registers a flag whose value can be a list of any strings. Use the flag + on the command line multiple times to place multiple string values into the + list. The 'default' may be a single string (which will be converted into a + single-element list) or a list of strings. + """ + parser = ArgumentParser() + serializer = ArgumentSerializer() + DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) + +def DEFINE_multi_int(name, default, help, lower_bound=None, upper_bound=None, + flag_values=FLAGS, **args): + """ + This registers a flag whose value can be a list of any integers. Use the + flag on the command line multiple times to place multiple integer values + into the list. The 'default' may be a single integer (which will be + converted into a single-element list) or a list of integers. + """ + parser = IntegerParser(lower_bound, upper_bound) + serializer = ArgumentSerializer() + DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) + + +# Now register the flags that we want to exist in all applications. +# These are all defined with allow_override=1, so user-apps can use +# these flagnames for their own purposes, if they want. +DEFINE_flag(HelpFlag()) +DEFINE_flag(HelpshortFlag()) diff --git a/contrib/gflags2man.py b/python/gflags2man.py index 9074559..4edac1c 100755 --- a/contrib/gflags2man.py +++ b/python/gflags2man.py @@ -1,6 +1,33 @@ -#!/usr/bin/python2.4 +#!/usr/bin/env python # -# Copyright 2006 Google Inc. All Rights Reserved. +# Copyright (c) 2007, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """gflags2man runs a Google flags base program and generates a man page. @@ -8,26 +35,29 @@ Run the program, parse the output, and then format that into a man page. Usage: - gflags2man program... + gflags2man <program> [program] ... """ +# TODO(csilvers): work with windows paths (\) as well as unix (/) + # This may seem a bit of an end run, but it: doesn't bloat flags, can # support python/java/C++, supports older executables, and can be # extended to other document formats. # Inspired by help2man. -__author__ = 'dchristian@google.com (Dan Christian)' +__author__ = 'Dan Christian' import os import re import sys import stat -import datetime +import time import subprocess -from google3.pyglib import app -from google3.pyglib import flags -from google3.pyglib import logging +import gflags + +_VERSION = '0.1' + def _GetDefaultDestDir(): home = os.environ.get('HOME', '') @@ -35,17 +65,29 @@ def _GetDefaultDestDir(): if home and os.path.exists(homeman): return homeman else: - return '/tmp' + return os.environ.get('TMPDIR', '/tmp') -FLAGS = flags.FLAGS -flags.DEFINE_string('dest_dir', _GetDefaultDestDir(), - 'Directory to write resulting manpage to.' - ' Specify \'-\' for stdout') -flags.DEFINE_string('help_flag', '--help', - 'Option to pass to target program in to get help') +FLAGS = gflags.FLAGS +gflags.DEFINE_string('dest_dir', _GetDefaultDestDir(), + 'Directory to write resulting manpage to.' + ' Specify \'-\' for stdout') +gflags.DEFINE_string('help_flag', '--help', + 'Option to pass to target program in to get help') +gflags.DEFINE_integer('v', 0, 'verbosity level to use for output') + +_MIN_VALID_USAGE_MSG = 9 # if fewer lines than this, help is suspect + + +class Logging: + """A super-simple logging class""" + def error(self, msg): print >>sys.stderr, "ERROR: ", msg + def warn(self, msg): print >>sys.stderr, "WARNING: ", msg + def info(self, msg): print msg + def debug(self, msg): self.vlog(1, msg) + def vlog(self, level, msg): + if FLAGS.v >= level: print msg +logging = Logging() -MIN_VALID_USAGE_MSG = 9 # minimum output likely to be valid -_version = '0.1' def GetRealPath(filename): """Given an executable filename, find in the PATH or find absolute path. @@ -55,7 +97,7 @@ def GetRealPath(filename): Absolute version of filename. None if filename could not be found locally, absolutely, or in PATH """ - if '/' == filename[0]: # already absolute + if os.path.isabs(filename): # already absolute return filename if filename.startswith('./') or filename.startswith('../'): # relative @@ -65,7 +107,7 @@ def GetRealPath(filename): for directory in path.split(':'): tryname = os.path.join(directory, filename) if os.path.exists(tryname): - if not directory or '/' != directory[0]: # directory is relative + if not os.path.isabs(directory): # relative directory return os.path.abspath(tryname) return tryname if os.path.exists(filename): @@ -88,31 +130,31 @@ class Flag(object): class ProgramInfo(object): - """All the information gleened from running a program with --help.""" + """All the information gleaned from running a program with --help.""" - # Match a module block start - # google3.pyglib.logging: + # Match a module block start, for python scripts --help + # "goopy.logging:" module_py_re = re.compile(r'(\S.+):$') # match the start of a flag listing - # -v,--verbosity: Logging verbosity + # " -v,--verbosity: Logging verbosity" flag_py_re = re.compile(r'\s+(-\S+):\s+(.*)$') - # (default: '0') + # " (default: '0')" flag_default_py_re = re.compile(r'\s+\(default:\s+\'(.*)\'\)$') - # (an integer) + # " (an integer)" flag_tips_py_re = re.compile(r'\s+\((.*)\)$') - # Match a module block start - # google3/base/commandlineflags + # Match a module block start, for c++ programs --help + # "google/base/commandlineflags" module_c_re = re.compile(r'\s+Flags from (\S.+):$') # match the start of a flag listing - # -v,--verbosity: Logging verbosity + # " -v,--verbosity: Logging verbosity" flag_c_re = re.compile(r'\s+(-\S+)\s+(.*)$') - # Match a module block start - # com.google.common.flags + # Match a module block start, for java programs --help + # "com.google.common.flags" module_java_re = re.compile(r'\s+Flags for (\S.+):$') # match the start of a flag listing - # -v,--verbosity: Logging verbosity + # " -v,--verbosity: Logging verbosity" flag_java_re = re.compile(r'\s+(-\S+)\s+(.*)$') def __init__(self, executable): @@ -121,29 +163,29 @@ class ProgramInfo(object): executable Program to execute (string) """ self.long_name = executable - self.name = os.path.basename(executable) # name + self.name = os.path.basename(executable) # name # Get name without extension (PAR files) - self.short_name, self.ext = os.path.splitext(self.name) - self.executable = GetRealPath(executable) # name of the program - self.output = [] # output from the program. List of lines. - self.desc = [] # top level description. List of lines - self.modules = {} # { section_name(string), [ flags ] } - self.module_list = [] # list of module names in their original order - self.date = datetime.date.today() # default date info + (self.short_name, self.ext) = os.path.splitext(self.name) + self.executable = GetRealPath(executable) # name of the program + self.output = [] # output from the program. List of lines. + self.desc = [] # top level description. List of lines + self.modules = {} # { section_name(string), [ flags ] } + self.module_list = [] # list of module names in their original order + self.date = time.localtime(time.time()) # default date info def Run(self): """Run it and collect output. Returns: - True If everything went well. - False If there were problems. + 1 (true) If everything went well. + 0 (false) If there were problems. """ if not self.executable: logging.error('Could not locate "%s"' % self.long_name) - return False + return 0 finfo = os.stat(self.executable) - self.date = datetime.date.fromtimestamp(finfo[stat.ST_MTIME]) + self.date = time.localtime(finfo[stat.ST_MTIME]) logging.info('Running: %s %s </dev/null 2>&1' % (self.executable, FLAGS.help_flag)) @@ -157,70 +199,71 @@ class ProgramInfo(object): stdin=open('/dev/null', 'r')) except OSError, msg: logging.error('Error executing "%s": %s' % (self.name, msg)) - return False + return 0 - #read output progressively so the pipe doesn't fill up (fileutil). + # read output progressively so the pipe doesn't fill up (fileutil). self.output = runstate.stdout.readlines() status = runstate.wait() logging.debug('Program exited with %s' % status) output = runstate.communicate()[0] if output: self.output = output.splitlines() - if len(self.output) < MIN_VALID_USAGE_MSG: + if len(self.output) < _MIN_VALID_USAGE_MSG: logging.error( 'Error: "%s %s" returned %d and only %d lines: %s' % (self.name, FLAGS.help_flag, status, len(self.output), output)) - return False - return True + return 0 + return 1 def Parse(self): """Parse program output.""" - cnt, lang = self.ParseDesc() - if cnt < 0: + (start_line, lang) = self.ParseDesc() + if start_line < 0: return if 'python' == lang: - self.ParsePythonFlags(cnt) + self.ParsePythonFlags(start_line) elif 'c' == lang: - self.ParseCFlags(cnt) + self.ParseCFlags(start_line) elif 'java' == lang: - self.ParseJavaFlags(cnt) + self.ParseJavaFlags(start_line) - def ParseDesc(self, cnt=0): + def ParseDesc(self, start_line=0): """Parse the initial description. This could be Python or C++. Returns: - (line_count, lang_type) - line_count Line to start parsing flags on (int) + (start_line, lang_type) + start_line Line to start parsing flags on (int) lang_type Either 'python' or 'c' (-1, '') if the flags start could not be found """ exec_mod_start = self.executable + ':' - after_blank = False - cnt = 0 - for cnt in range(cnt, len(self.output)): # collect top description - line = self.output[cnt].rstrip() + after_blank = 0 + start_line = 0 # ignore the passed-in arg for now (?) + for start_line in range(start_line, len(self.output)): # collect top description + line = self.output[start_line].rstrip() # Python flags start with 'flags:\n' if ('flags:' == line - and len(self.output) > cnt+1 and '' == self.output[cnt+1].rstrip()): - cnt += 2 + and len(self.output) > start_line+1 + and '' == self.output[start_line+1].rstrip()): + start_line += 2 logging.debug('Flags start (python): %s' % line) - return (cnt, 'python') + return (start_line, 'python') # SWIG flags just have the module name followed by colon. if exec_mod_start == line: logging.debug('Flags start (swig): %s' % line) - return (cnt, 'python') + return (start_line, 'python') # C++ flags begin after a blank line and with a constant string if after_blank and line.startswith(' Flags from '): logging.debug('Flags start (c): %s' % line) - return (cnt, 'c') + return (start_line, 'c') # java flags begin with a constant string if line == 'where flags are': logging.debug('Flags start (java): %s' % line) - cnt += 2 # skip "Standard flags:" - return (cnt, 'java') + start_line += 2 # skip "Standard flags:" + return (start_line, 'java') logging.debug('Desc: %s' % line) self.desc.append(line) @@ -230,12 +273,13 @@ class ProgramInfo(object): % self.long_name) return (-1, '') - def ParsePythonFlags(self, cnt=0): + def ParsePythonFlags(self, start_line=0): """Parse python/swig style flags.""" modname = None # name of current module + modlist = [] flag = None - for cnt in range(cnt, len(self.output)): # collect flags - line = self.output[cnt].rstrip() + for line_num in range(start_line, len(self.output)): # collect flags + line = self.output[line_num].rstrip() if not line: # blank continue @@ -278,12 +322,13 @@ class ProgramInfo(object): if flag: modlist.append(flag) - def ParseCFlags(self, cnt=0): + def ParseCFlags(self, start_line=0): """Parse C style flags.""" modname = None # name of current module + modlist = [] flag = None - for cnt in range(cnt, len(self.output)): # collect flags - line = self.output[cnt].rstrip() + for line_num in range(start_line, len(self.output)): # collect flags + line = self.output[line_num].rstrip() if not line: # blank lines terminate flags if flag: # save last flag modlist.append(flag) @@ -318,7 +363,7 @@ class ProgramInfo(object): if flag: modlist.append(flag) - def ParseJavaFlags(self, cnt=0): + def ParseJavaFlags(self, start_line=0): """Parse Java style flags (com.google.common.flags).""" # The java flags prints starts with a "Standard flags" "module" # that doesn't follow the standard module syntax. @@ -328,8 +373,8 @@ class ProgramInfo(object): modlist = self.modules[modname] flag = None - for cnt in range(cnt, len(self.output)): # collect flags - line = self.output[cnt].rstrip() + for line_num in range(start_line, len(self.output)): # collect flags + line = self.output[line_num].rstrip() logging.vlog(2, 'Line: "%s"' % line) if not line: # blank lines terminate module if flag: # save last flag @@ -371,9 +416,9 @@ class ProgramInfo(object): self.short_desc = '' return - for cnt in range(len(self.desc)): # replace full path with name - if self.desc[cnt].find(self.executable) >= 0: - self.desc[cnt] = self.desc[cnt].replace(self.executable, self.name) + for i in range(len(self.desc)): # replace full path with name + if self.desc[i].find(self.executable) >= 0: + self.desc[i] = self.desc[i].replace(self.executable, self.name) self.short_desc = self.desc[0] word_list = self.short_desc.split(' ') @@ -407,6 +452,11 @@ class GenerateDoc(object): self.Body() self.Footer() + def Open(self): raise NotImplementedError # define in subclass + def Header(self): raise NotImplementedError # define in subclass + def Body(self): raise NotImplementedError # define in subclass + def Footer(self): raise NotImplementedError # define in subclass + class GenerateMan(GenerateDoc): """Output a man page.""" @@ -431,10 +481,10 @@ class GenerateMan(GenerateDoc): def Header(self): self.fp.write( '.\\" DO NOT MODIFY THIS FILE! It was generated by gflags2man %s\n' - % _version) + % _VERSION) self.fp.write( '.TH %s "1" "%s" "%s" "User Commands"\n' - % (self.info.name, self.info.date.strftime('%x'), self.info.name)) + % (self.info.name, time.strftime('%x', self.info.date), self.info.name)) self.fp.write( '.SH NAME\n%s \\- %s\n' % (self.info.name, self.info.short_desc)) self.fp.write( @@ -455,30 +505,33 @@ class GenerateMan(GenerateDoc): mod = modname self.fp.write('\n.P\n.I %s\n' % mod) for flag in self.info.modules[modname]: - help = flag.help + help_string = flag.help if flag.default or flag.tips: - help += '\n.br\n' + help_string += '\n.br\n' if flag.default: - help += ' (default: \'%s\')' % flag.default + help_string += ' (default: \'%s\')' % flag.default if flag.tips: - help += ' (%s)' % flag.tips + help_string += ' (%s)' % flag.tips self.fp.write( - '.TP\n%s\n%s\n' % (flag.desc, help)) + '.TP\n%s\n%s\n' % (flag.desc, help_string)) def Footer(self): self.fp.write( '.SH COPYRIGHT\nCopyright \(co %s Google.\n' - % self.info.date.strftime('%Y')) - self.fp.write('Gflags2man.par created this page from "%s %s" output.\n' + % time.strftime('%Y', self.info.date)) + self.fp.write('Gflags2man created this page from "%s %s" output.\n' % (self.info.name, FLAGS.help_flag)) - self.fp.write('\nGflags2man.par was written by Dan Christian' - ' (dchristian@google.com). Note that the date on this' + self.fp.write('\nGflags2man was written by Dan Christian. ' + ' Note that the date on this' ' page is the modification date of %s.\n' % self.info.name) def main(argv): + argv = FLAGS(argv) # handles help as well if len(argv) <= 1: - app.usage(shorthelp=1) + print >>sys.stderr, __doc__ + print >>sys.stderr, "flags:" + print >>sys.stderr, str(FLAGS) return 1 for arg in argv[1:]: @@ -489,7 +542,7 @@ def main(argv): prog.Filter() doc = GenerateMan(prog, FLAGS.dest_dir) doc.Output() - + return 0 if __name__ == '__main__': - app.run() + main(sys.argv) diff --git a/python/gflags_unittest.py b/python/gflags_unittest.py new file mode 100755 index 0000000..d55a26a --- /dev/null +++ b/python/gflags_unittest.py @@ -0,0 +1,800 @@ +#!/usr/bin/env python + +# Copyright (c) 2007, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"Unittest for flags.py module" + +__pychecker__ = "no-local" # for unittest + + +import sys +import os +import shutil +import unittest + +# We use the name 'flags' internally in this test, for historical reasons. +# Don't do this yourself! :-) Just do 'import gflags; FLAGS=gflags.FLAGS; etc' +import gflags as flags +FLAGS=flags.FLAGS + +class FlagsUnitTest(unittest.TestCase): + "Flags Unit Test" + + def test_flags(self): + + ############################################## + # Test normal usage with no (expected) errors. + + # Define flags + number_test_framework_flags = len(FLAGS.RegisteredFlags()) + repeatHelp = "how many times to repeat (0-5)" + flags.DEFINE_integer("repeat", 4, repeatHelp, + lower_bound=0, short_name='r') + flags.DEFINE_string("name", "Bob", "namehelp") + flags.DEFINE_boolean("debug", 0, "debughelp") + flags.DEFINE_boolean("q", 1, "quiet mode") + flags.DEFINE_boolean("quack", 0, "superstring of 'q'") + flags.DEFINE_boolean("noexec", 1, "boolean flag with no as prefix") + flags.DEFINE_integer("x", 3, "how eXtreme to be") + flags.DEFINE_integer("l", 0x7fffffff00000000L, "how long to be") + assert FLAGS.repeat == 4, "integer default values not set:" + FLAGS.repeat + assert FLAGS.name == 'Bob', "default values not set:" + FLAGS.name + assert FLAGS.debug == 0, "boolean default values not set:" + FLAGS.debug + assert FLAGS.q == 1, "boolean default values not set:" + FLAGS.q + assert FLAGS.x == 3, "integer default values not set:" + FLAGS.x + assert FLAGS.l == 0x7fffffff00000000L, "integer default values not set:" + FLAGS.l + + flag_values = FLAGS.FlagValuesDict() + assert flag_values['repeat'] == 4 + assert flag_values['name'] == 'Bob' + assert flag_values['debug'] == 0 + assert flag_values['r'] == 4 # short for of repeat + assert flag_values['q'] == 1 + assert flag_values['quack'] == 0 + assert flag_values['x'] == 3 + assert flag_values['l'] == 0x7fffffff00000000L + + # Verify string form of defaults + assert FLAGS['repeat'].default_as_str == "'4'" + assert FLAGS['name'].default_as_str == "'Bob'" + assert FLAGS['debug'].default_as_str == "'false'" + assert FLAGS['q'].default_as_str == "'true'" + assert FLAGS['quack'].default_as_str == "'false'" + assert FLAGS['noexec'].default_as_str == "'true'" + assert FLAGS['x'].default_as_str == "'3'" + assert FLAGS['l'].default_as_str == "'9223372032559808512'" + + # Verify that the iterator for flags yields all the keys + keys = list(FLAGS) + keys.sort() + reg_flags = FLAGS.RegisteredFlags() + reg_flags.sort() + self.assertEqual(keys, reg_flags) + + # Parse flags + # .. empty command line + argv = ('./program',) + argv = FLAGS(argv) + assert len(argv) == 1, "wrong number of arguments pulled" + assert argv[0]=='./program', "program name not preserved" + + # .. non-empty command line + argv = ('./program', '--debug', '--name=Bob', '-q', '--x=8') + argv = FLAGS(argv) + assert len(argv) == 1, "wrong number of arguments pulled" + assert argv[0]=='./program', "program name not preserved" + assert FLAGS['debug'].present == 1 + FLAGS['debug'].present = 0 # Reset + assert FLAGS['name'].present == 1 + FLAGS['name'].present = 0 # Reset + assert FLAGS['q'].present == 1 + FLAGS['q'].present = 0 # Reset + assert FLAGS['x'].present == 1 + FLAGS['x'].present = 0 # Reset + + # Flags list + + assert len(FLAGS.RegisteredFlags()) == 9 + number_test_framework_flags + assert 'name' in FLAGS.RegisteredFlags() + assert 'debug' in FLAGS.RegisteredFlags() + assert 'repeat' in FLAGS.RegisteredFlags() + assert 'r' in FLAGS.RegisteredFlags() + assert 'q' in FLAGS.RegisteredFlags() + assert 'quack' in FLAGS.RegisteredFlags() + assert 'x' in FLAGS.RegisteredFlags() + assert 'l' in FLAGS.RegisteredFlags() + + # has_key + assert FLAGS.has_key('name') + assert not FLAGS.has_key('name2') + assert 'name' in FLAGS + assert 'name2' not in FLAGS + + # try deleting a flag + del FLAGS.r + assert len(FLAGS.RegisteredFlags()) == 8 + number_test_framework_flags + assert not 'r' in FLAGS.RegisteredFlags() + + # .. command line with extra stuff + argv = ('./program', '--debug', '--name=Bob', 'extra') + argv = FLAGS(argv) + assert len(argv) == 2, "wrong number of arguments pulled" + assert argv[0]=='./program', "program name not preserved" + assert argv[1]=='extra', "extra argument not preserved" + assert FLAGS['debug'].present == 1 + FLAGS['debug'].present = 0 # Reset + assert FLAGS['name'].present == 1 + FLAGS['name'].present = 0 # Reset + + # Test reset + argv = ('./program', '--debug') + argv = FLAGS(argv) + assert len(argv) == 1, "wrong number of arguments pulled" + assert argv[0] == './program', "program name not preserved" + assert FLAGS['debug'].present == 1 + assert FLAGS['debug'].value == True + FLAGS.Reset() + assert FLAGS['debug'].present == 0 + assert FLAGS['debug'].value == False + + # Test integer argument passing + argv = ('./program', '--x', '0x12345') + argv = FLAGS(argv) + # 0x12345 == 74565 + self.assertEquals(FLAGS.x, 74565) + self.assertEquals(type(FLAGS.x), int) + + argv = ('./program', '--x', '0x123456789A') + argv = FLAGS(argv) + # 0x123456789A == 78187493530L + self.assertEquals(FLAGS.x, 78187493530L) + self.assertEquals(type(FLAGS.x), long) + + # Treat 0-prefixed parameters as base-10, not base-8 + argv = ('./program', '--x', '012345') + argv = FLAGS(argv) + self.assertEquals(FLAGS.x, 12345) + self.assertEquals(type(FLAGS.x), int) + + argv = ('./program', '--x', '0123459') + argv = FLAGS(argv) + self.assertEquals(FLAGS.x, 123459) + self.assertEquals(type(FLAGS.x), int) + + argv = ('./program', '--x', '0x123efg') + try: + argv = FLAGS(argv) + raise AssertionError("failed to detect invalid hex argument") + except flags.IllegalFlagValue: + pass + + argv = ('./program', '--x', '0X123efg') + try: + argv = FLAGS(argv) + raise AssertionError("failed to detect invalid hex argument") + except flags.IllegalFlagValue: + pass + + # Test boolean argument parsing + flags.DEFINE_boolean("test0", None, "test boolean parsing") + argv = ('./program', '--notest0') + argv = FLAGS(argv) + assert FLAGS.test0 == 0 + + flags.DEFINE_boolean("test1", None, "test boolean parsing") + argv = ('./program', '--test1') + argv = FLAGS(argv) + assert FLAGS.test1 == 1 + + FLAGS.test0 = None + argv = ('./program', '--test0=false') + argv = FLAGS(argv) + assert FLAGS.test0 == 0 + + FLAGS.test1 = None + argv = ('./program', '--test1=true') + argv = FLAGS(argv) + assert FLAGS.test1 == 1 + + FLAGS.test0 = None + argv = ('./program', '--test0=0') + argv = FLAGS(argv) + assert FLAGS.test0 == 0 + + FLAGS.test1 = None + argv = ('./program', '--test1=1') + argv = FLAGS(argv) + assert FLAGS.test1 == 1 + + # Test booleans that already have 'no' as a prefix + FLAGS.noexec = None + argv = ('./program', '--nonoexec', '--name', 'Bob') + argv = FLAGS(argv) + assert FLAGS.noexec == 0 + + FLAGS.noexec = None + argv = ('./program', '--name', 'Bob', '--noexec') + argv = FLAGS(argv) + assert FLAGS.noexec == 1 + + # Test unassigned booleans + flags.DEFINE_boolean("testnone", None, "test boolean parsing") + argv = ('./program',) + argv = FLAGS(argv) + assert FLAGS.testnone == None + + # Test get with default + flags.DEFINE_boolean("testget1", None, "test parsing with defaults") + flags.DEFINE_boolean("testget2", None, "test parsing with defaults") + flags.DEFINE_boolean("testget3", None, "test parsing with defaults") + flags.DEFINE_integer("testget4", None, "test parsing with defaults") + argv = ('./program','--testget1','--notestget2') + argv = FLAGS(argv) + assert FLAGS.get('testget1', 'foo') == 1 + assert FLAGS.get('testget2', 'foo') == 0 + assert FLAGS.get('testget3', 'foo') == 'foo' + assert FLAGS.get('testget4', 'foo') == 'foo' + + # test list code + lists = [['hello','moo','boo','1'], + [],] + + flags.DEFINE_list('testlist', '', 'test lists parsing') + flags.DEFINE_spaceseplist('testspacelist', '', 'tests space lists parsing') + + for name, sep in (('testlist', ','), ('testspacelist', ' '), + ('testspacelist', '\n')): + for lst in lists: + argv = ('./program', '--%s=%s' % (name, sep.join(lst))) + argv = FLAGS(argv) + self.assertEquals(getattr(FLAGS, name), lst) + + # Test help text + flagsHelp = str(FLAGS) + assert flagsHelp.find("repeat") != -1, "cannot find flag in help" + assert flagsHelp.find(repeatHelp) != -1, "cannot find help string in help" + + # Test flag specified twice + argv = ('./program', '--repeat=4', '--repeat=2', '--debug', '--nodebug') + argv = FLAGS(argv) + self.assertEqual(FLAGS.get('repeat', None), 2) + self.assertEqual(FLAGS.get('debug', None), 0) + + # Test MultiFlag with single default value + flags.DEFINE_multistring('s_str', 'sing1', + 'string option that can occur multiple times', + short_name='s') + self.assertEqual(FLAGS.get('s_str', None), [ 'sing1', ]) + + # Test MultiFlag with list of default values + multi_string_defs = [ 'def1', 'def2', ] + flags.DEFINE_multistring('m_str', multi_string_defs, + 'string option that can occur multiple times', + short_name='m') + self.assertEqual(FLAGS.get('m_str', None), multi_string_defs) + + # Test flag specified multiple times with a MultiFlag + argv = ('./program', '--m_str=str1', '-m', 'str2') + argv = FLAGS(argv) + self.assertEqual(FLAGS.get('m_str', None), [ 'str1', 'str2', ]) + + # Test single-letter flags; should support both single and double dash + argv = ('./program', '-q', '-x8') + argv = FLAGS(argv) + self.assertEqual(FLAGS.get('q', None), 1) + self.assertEqual(FLAGS.get('x', None), 8) + + argv = ('./program', '--q', '--x', '9', '--noqu') + argv = FLAGS(argv) + self.assertEqual(FLAGS.get('q', None), 1) + self.assertEqual(FLAGS.get('x', None), 9) + # --noqu should match '--noquack since it's a unique prefix + self.assertEqual(FLAGS.get('quack', None), 0) + + argv = ('./program', '--noq', '--x=10', '--qu') + argv = FLAGS(argv) + self.assertEqual(FLAGS.get('q', None), 0) + self.assertEqual(FLAGS.get('x', None), 10) + self.assertEqual(FLAGS.get('quack', None), 1) + + #################################### + # Test flag serialization code: + + oldtestlist = FLAGS.testlist + oldtestspacelist = FLAGS.testspacelist + + argv = ('./program', + FLAGS['test0'].Serialize(), + FLAGS['test1'].Serialize(), + FLAGS['testnone'].Serialize(), + FLAGS['s_str'].Serialize()) + argv = FLAGS(argv) + self.assertEqual(FLAGS['test0'].Serialize(), '--notest0') + self.assertEqual(FLAGS['test1'].Serialize(), '--test1') + self.assertEqual(FLAGS['testnone'].Serialize(), '') + self.assertEqual(FLAGS['s_str'].Serialize(), '--s_str=sing1') + + testlist1 = ['aa', 'bb'] + testspacelist1 = ['aa', 'bb', 'cc'] + FLAGS.testlist = list(testlist1) + FLAGS.testspacelist = list(testspacelist1) + argv = ('./program', + FLAGS['testlist'].Serialize(), + FLAGS['testspacelist'].Serialize()) + argv = FLAGS(argv) + self.assertEqual(FLAGS.testlist, testlist1) + self.assertEqual(FLAGS.testspacelist, testspacelist1) + + testlist1 = ['aa some spaces', 'bb'] + testspacelist1 = ['aa', 'bb,some,commas,', 'cc'] + FLAGS.testlist = list(testlist1) + FLAGS.testspacelist = list(testspacelist1) + argv = ('./program', + FLAGS['testlist'].Serialize(), + FLAGS['testspacelist'].Serialize()) + argv = FLAGS(argv) + self.assertEqual(FLAGS.testlist, testlist1) + self.assertEqual(FLAGS.testspacelist, testspacelist1) + + FLAGS.testlist = oldtestlist + FLAGS.testspacelist = oldtestspacelist + + #################################### + # Test flag-update: + + def ArgsString(): + flagnames = FLAGS.RegisteredFlags() + flagnames.sort() + nonbool_flags = ['--%s %s' % (name, FLAGS.get(name, None)) + for name in flagnames + if not isinstance(FLAGS[name], flags.BooleanFlag)] + + truebool_flags = ['--%s' % (name) + for name in flagnames + if isinstance(FLAGS[name], flags.BooleanFlag) and + FLAGS.get(name, None)] + falsebool_flags = ['--no%s' % (name) + for name in flagnames + if isinstance(FLAGS[name], flags.BooleanFlag) and + not FLAGS.get(name, None)] + return ' '.join(nonbool_flags + truebool_flags + falsebool_flags) + + argv = ('./program', '--repeat=3', '--name=giants', '--nodebug') + FLAGS(argv) + self.assertEqual(FLAGS.get('repeat', None), 3) + self.assertEqual(FLAGS.get('name', None), 'giants') + self.assertEqual(FLAGS.get('debug', None), 0) + self.assertEqual(ArgsString(), + "--l 9223372032559808512 " + "--m ['str1', 'str2'] --m_str ['str1', 'str2'] " + "--name giants " + "--repeat 3 " + "--s ['sing1'] --s_str ['sing1'] " + "--testget4 None --testlist [] " + "--testspacelist [] --x 10 " + "--noexec --quack " + "--test1 " + "--testget1 --no? --nodebug --nohelp --nohelpshort " + "--noq --notest0 --notestget2 " + "--notestget3 --notestnone") + + argv = ('./program', '--debug', '--m_str=upd1', '-s', 'upd2') + FLAGS(argv) + self.assertEqual(FLAGS.get('repeat', None), 3) + self.assertEqual(FLAGS.get('name', None), 'giants') + self.assertEqual(FLAGS.get('debug', None), 1) + + # items appended to existing non-default value lists for --m/--m_str + # new value overwrites default value (not appended to it) for --s/--s_str + self.assertEqual(ArgsString(), + "--l 9223372032559808512 " + "--m ['str1', 'str2', 'upd1'] " + "--m_str ['str1', 'str2', 'upd1'] " + "--name giants " + "--repeat 3 " + "--s ['upd2'] --s_str ['upd2'] " + "--testget4 None --testlist [] " + "--testspacelist [] --x 10 " + "--debug --noexec --quack " + "--test1 " + "--testget1 --no? --nohelp --nohelpshort " + "--noq --notest0 --notestget2 " + "--notestget3 --notestnone") + + + #################################### + # Test all kind of error conditions. + + # Duplicate flag detection + try: + flags.DEFINE_boolean("run", 0, "runhelp", short_name='q') + raise AssertionError("duplicate flag detection failed") + except flags.DuplicateFlag, e: + pass + + try: + flags.DEFINE_boolean("zoom1", 0, "runhelp z1", short_name='z') + flags.DEFINE_boolean("zoom2", 0, "runhelp z2", short_name='z') + raise AssertionError("duplicate flag detection failed") + except flags.DuplicateFlag, e: + pass + + # Make sure allow_override works + try: + flags.DEFINE_boolean("dup1", 0, "runhelp d11", short_name='u', + allow_override=0) + flag = FLAGS.FlagDict()['dup1'] + self.assertEqual(flag.default, 0) + + flags.DEFINE_boolean("dup1", 1, "runhelp d12", short_name='u', + allow_override=1) + flag = FLAGS.FlagDict()['dup1'] + self.assertEqual(flag.default, 1) + except flags.DuplicateFlag, e: + raise AssertionError("allow_override did not permit a flag duplication") + + # Make sure allow_override works + try: + flags.DEFINE_boolean("dup2", 0, "runhelp d21", short_name='u', + allow_override=1) + flag = FLAGS.FlagDict()['dup2'] + self.assertEqual(flag.default, 0) + + flags.DEFINE_boolean("dup2", 1, "runhelp d22", short_name='u', + allow_override=0) + flag = FLAGS.FlagDict()['dup2'] + self.assertEqual(flag.default, 1) + except flags.DuplicateFlag, e: + raise AssertionError("allow_override did not permit a flag duplication") + + # Make sure allow_override doesn't work with None default + try: + flags.DEFINE_boolean("dup3", 0, "runhelp d31", short_name='u', + allow_override=0) + flag = FLAGS.FlagDict()['dup3'] + self.assertEqual(flag.default, 0) + + flags.DEFINE_boolean("dup3", None, "runhelp d32", short_name='u', + allow_override=1) + raise AssertionError('Cannot override a flag with a default of None') + except flags.DuplicateFlag, e: + pass + + # Make sure that when we override, the help string gets updated correctly + flags.DEFINE_boolean("dup3", 0, "runhelp d31", short_name='u', + allow_override=1) + flags.DEFINE_boolean("dup3", 1, "runhelp d32", short_name='u', + allow_override=1) + self.assert_(str(FLAGS).find('runhelp d31') == -1) + self.assert_(str(FLAGS).find('runhelp d32') != -1) + + # Integer out of bounds + try: + argv = ('./program', '--repeat=-4') + FLAGS(argv) + raise AssertionError('integer bounds exception not thrown:' + + str(FLAGS.repeat)) + except flags.IllegalFlagValue: + pass + + # Non-integer + try: + argv = ('./program', '--repeat=2.5') + FLAGS(argv) + raise AssertionError("malformed integer value exception not thrown") + except flags.IllegalFlagValue: + pass + + # Missing required arugment + try: + argv = ('./program', '--name') + FLAGS(argv) + raise AssertionError("Flag argument required exception not thrown") + except flags.FlagsError: + pass + + # Argument erroneously supplied for boolean + try: + argv = ('./program', '--debug=goofup') + FLAGS(argv) + raise AssertionError("No argument allowed exception not thrown") + except flags.FlagsError: + pass + + # Unknown argument --nosuchflag + try: + argv = ('./program', '--nosuchflag', '--name=Bob', 'extra') + FLAGS(argv) + raise AssertionError("Unknown argument exception not thrown") + except flags.FlagsError: + pass + + # Non-numeric argument for integer flag --repeat + try: + argv = ('./program', '--repeat', 'Bob', 'extra') + FLAGS(argv) + raise AssertionError("Illegal flag value exception not thrown") + except flags.IllegalFlagValue: + pass + + ################################################ + # Code to test the flagfile=<> loading behavior + ################################################ + def _SetupTestFiles(self): + """ Creates and sets up some dummy flagfile files with bogus flags""" + + # Figure out where to create temporary files + tmp_path = '/tmp/flags_unittest' + if os.path.exists(tmp_path): + shutil.rmtree(tmp_path) + os.makedirs(tmp_path) + + try: + tmp_flag_file_1 = open((tmp_path + '/UnitTestFile1.tst'), 'w') + tmp_flag_file_2 = open((tmp_path + '/UnitTestFile2.tst'), 'w') + tmp_flag_file_3 = open((tmp_path + '/UnitTestFile3.tst'), 'w') + except IOError, e_msg: + print e_msg + print 'FAIL\n File Creation problem in Unit Test' + sys.exit(1) + + # put some dummy flags in our test files + tmp_flag_file_1.write('#A Fake Comment\n') + tmp_flag_file_1.write('--UnitTestMessage1=tempFile1!\n') + tmp_flag_file_1.write('\n') + tmp_flag_file_1.write('--UnitTestNumber=54321\n') + tmp_flag_file_1.write('--noUnitTestBoolFlag\n') + file_list = [tmp_flag_file_1.name] + # this one includes test file 1 + tmp_flag_file_2.write('//A Different Fake Comment\n') + tmp_flag_file_2.write('--flagfile=%s\n' % tmp_flag_file_1.name) + tmp_flag_file_2.write('--UnitTestMessage2=setFromTempFile2\n') + tmp_flag_file_2.write('\t\t\n') + tmp_flag_file_2.write('--UnitTestNumber=6789a\n') + file_list.append(tmp_flag_file_2.name) + # this file points to itself + tmp_flag_file_3.write('--flagfile=%s\n' % tmp_flag_file_3.name) + tmp_flag_file_3.write('--UnitTestMessage1=setFromTempFile3\n') + tmp_flag_file_3.write('#YAFC\n') + tmp_flag_file_3.write('--UnitTestBoolFlag\n') + file_list.append(tmp_flag_file_3.name) + + tmp_flag_file_1.close() + tmp_flag_file_2.close() + tmp_flag_file_3.close() + + return file_list # these are just the file names + # end SetupFiles def + + def _RemoveTestFiles(self, tmp_file_list): + """Closes the files we just created. tempfile deletes them for us """ + for file_name in tmp_file_list: + try: + os.remove(file_name) + except OSError, e_msg: + print '%s\n, Problem deleting test file' % e_msg + #end RemoveTestFiles def + + def __DeclareSomeFlags(self): + flags.DEFINE_string('UnitTestMessage1', 'Foo!', 'You Add Here.') + flags.DEFINE_string('UnitTestMessage2', 'Bar!', 'Hello, Sailor!') + flags.DEFINE_boolean('UnitTestBoolFlag', 0, 'Some Boolean thing') + flags.DEFINE_integer('UnitTestNumber', 12345, 'Some integer', + lower_bound=0) + + def _UndeclareSomeFlags(self): + FLAGS.__delattr__('UnitTestMessage1') + FLAGS.__delattr__('UnitTestMessage2') + FLAGS.__delattr__('UnitTestBoolFlag') + FLAGS.__delattr__('UnitTestNumber') + + #### Flagfile Unit Tests #### + def testMethod_flagfiles_1(self): + """ Test trivial case with no flagfile based options. """ + self.__DeclareSomeFlags() + fake_cmd_line = 'fooScript --UnitTestBoolFlag' + fake_argv = fake_cmd_line.split(' ') + FLAGS(fake_argv) + self.assertEqual( FLAGS.UnitTestBoolFlag, 1) + self.assertEqual( fake_argv, FLAGS.ReadFlagsFromFiles(fake_argv)) + self._UndeclareSomeFlags() + # end testMethodOne + + def testMethod_flagfiles_2(self): + """Tests parsing one file + arguments off simulated argv""" + self.__DeclareSomeFlags() + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = 'fooScript --q --flagfile=%s' % tmp_files[0] + fake_argv = fake_cmd_line.split(' ') + + # We should see the original cmd line with the file's contents spliced in. + # Note that these will be in REVERSE order from order encountered in file + # This is done so arguements we encounter sooner will have priority. + expected_results = ['fooScript', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag', + '--q'] + test_results = FLAGS.ReadFlagsFromFiles(fake_argv) + self.assertEqual(expected_results, test_results) + self._RemoveTestFiles(tmp_files) + self._UndeclareSomeFlags() + # end testTwo def + + def testMethod_flagfiles_3(self): + """Tests parsing nested files + arguments of simulated argv""" + self.__DeclareSomeFlags() + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --UnitTestNumber=77 --flagfile=%s' + % tmp_files[1]) + fake_argv = fake_cmd_line.split(' ') + + expected_results = ['fooScript', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag', + '--UnitTestMessage2=setFromTempFile2', + '--UnitTestNumber=6789a', + '--UnitTestNumber=77'] + test_results = FLAGS.ReadFlagsFromFiles(fake_argv) + self.assertEqual(expected_results, test_results) + self._RemoveTestFiles(tmp_files) + self._UndeclareSomeFlags() + # end testThree def + + def testMethod_flagfiles_4(self): + """Tests parsing self referetial files + arguments of simulated argv. + This test should print a warning to stderr of some sort. + """ + self.__DeclareSomeFlags() + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --flagfile=%s --noUnitTestBoolFlag' + % tmp_files[2]) + fake_argv = fake_cmd_line.split(' ') + expected_results = ['fooScript', + '--UnitTestMessage1=setFromTempFile3', + '--UnitTestBoolFlag', + '--noUnitTestBoolFlag' ] + + test_results = FLAGS.ReadFlagsFromFiles(fake_argv) + self.assertEqual(expected_results, test_results) + self._RemoveTestFiles(tmp_files) + self._UndeclareSomeFlags() + + def test_flagfiles_user_path_expansion(self): + """Test that user directory referenced paths (ie. ~/foo) are correctly + expanded. This test depends on whatever account's running the unit test + to have read/write access to their own home directory, otherwise it'll + FAIL. + """ + self.__DeclareSomeFlags() + fake_flagfile_item_style_1 = '--flagfile=~/foo.file' + fake_flagfile_item_style_2 = '-flagfile=~/foo.file' + + expected_results = os.path.expanduser('~/foo.file') + + test_results = FLAGS.ExtractFilename(fake_flagfile_item_style_1) + self.assertEqual(expected_results, test_results) + + test_results = FLAGS.ExtractFilename(fake_flagfile_item_style_2) + self.assertEqual(expected_results, test_results) + + self._UndeclareSomeFlags() + + # end testFour def + + def test_no_touchy_non_flags(self): + """ + Test that the flags parser does not mutilate arguments which are + not supposed to be flags + """ + self.__DeclareSomeFlags() + fake_argv = ['fooScript', '--UnitTestBoolFlag', + 'command', '--command_arg1', '--UnitTestBoom', '--UnitTestB'] + argv = FLAGS(fake_argv) + self.assertEqual(argv, fake_argv[:1] + fake_argv[2:]) + self._UndeclareSomeFlags() + + def test_SetDefault(self): + """ + Test changing flag defaults. + """ + self.__DeclareSomeFlags() + # Test that SetDefault changes both the default and the value, + # and that the value is changed when one is given as an option. + FLAGS['UnitTestMessage1'].SetDefault('New value') + self.assertEqual(FLAGS.UnitTestMessage1, 'New value') + self.assertEqual(FLAGS['UnitTestMessage1'].default_as_str,"'New value'") + FLAGS([ 'dummyscript', '--UnitTestMessage1=Newer value' ]) + self.assertEqual(FLAGS.UnitTestMessage1, 'Newer value') + # Test that setting the default to None works correctly. + FLAGS['UnitTestNumber'].SetDefault(None) + self.assertEqual(FLAGS.UnitTestNumber, None) + self.assertEqual(FLAGS['UnitTestNumber'].default_as_str, None) + FLAGS([ 'dummyscript', '--UnitTestNumber=56' ]) + self.assertEqual(FLAGS.UnitTestNumber, 56) + # Test that setting invalid defaults raises exceptions + self.assertRaises(flags.IllegalFlagValue, + FLAGS['UnitTestNumber'].SetDefault, 'oops') + self.assertRaises(flags.IllegalFlagValue, + FLAGS['UnitTestNumber'].SetDefault, -1) + self.assertRaises(flags.IllegalFlagValue, + FLAGS['UnitTestBoolFlag'].SetDefault, 'oops') + + self._UndeclareSomeFlags() + + def testMethod_ShortestUniquePrefixes(self): + """ + Test FlagValues.ShortestUniquePrefixes + """ + flags.DEFINE_string('a', '', '') + flags.DEFINE_string('abc', '', '') + flags.DEFINE_string('common_a_string', '', '') + flags.DEFINE_boolean('common_b_boolean', 0, '') + flags.DEFINE_boolean('common_c_boolean', 0, '') + flags.DEFINE_boolean('common', 0, '') + flags.DEFINE_integer('commonly', 0, '') + flags.DEFINE_boolean('zz', 0, '') + flags.DEFINE_integer('nozz', 0, '') + + shorter_flags = FLAGS.ShortestUniquePrefixes(FLAGS.FlagDict()) + + expected_results = {'nocommon_b_boolean': 'nocommon_b', + 'common_c_boolean': 'common_c', + 'common_b_boolean': 'common_b', + 'a': 'a', + 'abc': 'ab', + 'zz': 'z', + 'nozz': 'nozz', + 'common_a_string': 'common_a', + 'commonly': 'commonl', + 'nocommon_c_boolean': 'nocommon_c', + 'nocommon': 'nocommon', + 'common': 'common'} + + for name, shorter in expected_results.iteritems(): + self.assertEquals(shorter_flags[name], shorter) + + FLAGS.__delattr__('a') + FLAGS.__delattr__('abc') + FLAGS.__delattr__('common_a_string') + FLAGS.__delattr__('common_b_boolean') + FLAGS.__delattr__('common_c_boolean') + FLAGS.__delattr__('common') + FLAGS.__delattr__('commonly') + FLAGS.__delattr__('zz') + FLAGS.__delattr__('nozz') + + +if __name__ == '__main__': + unittest.main() diff --git a/python/setup.py b/python/setup.py new file mode 100755 index 0000000..c139338 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,42 @@ +#!/usr/bin/python2.2 + +# Copyright (c) 2007, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from distutils.core import setup + +setup(name='gflags', + version='0.2', + description='Google Commandline Flags Module', + license='BSD', + author='Google Inc.', + author_email='opensource@google.com', + url='http://code.google.com/p/google-gflags', + py_modules=["gflags"], + data_files=["/usr/local/bin", "gflags2man.py"]) diff --git a/src/google/gflags.h.in b/src/google/gflags.h.in index 832a64f..13043f5 100644 --- a/src/google/gflags.h.in +++ b/src/google/gflags.h.in @@ -55,6 +55,7 @@ #define BASE_COMMANDLINEFLAGS_H__ #include <string> +#include <vector> // We care a lot about number of bits things take up. Unfortunately, // systems define their bit-specific ints in a lot of different ways. |