From 1fb46ce05bb6423d9064b8d274e7be601f16a49f Mon Sep 17 00:00:00 2001 From: Damien Martin-Guillerez Date: Tue, 11 Jul 2017 07:00:14 +0200 Subject: Revert "Bump python-gflags to version 3.1.0." This reverts commit d82e69cef3f52f27c418c515f8af6af57d55aaf4. This commit fails to build on Windows because of incorrect imports. --- third_party/py/gflags/BUILD | 5 +- third_party/py/gflags/CONTRIBUTING.md | 11 - third_party/py/gflags/ChangeLog | 66 - third_party/py/gflags/MANIFEST.in | 9 +- third_party/py/gflags/NEWS | 78 + third_party/py/gflags/PKG-INFO | 13 +- third_party/py/gflags/README.md | 6 +- third_party/py/gflags/__init__.py | 2864 +++++++++++++++++++- third_party/py/gflags/debian/rules | 0 third_party/py/gflags/gflags/__init__.py | 891 ------ third_party/py/gflags/gflags/_helpers.py | 430 --- third_party/py/gflags/gflags/_helpers_test.py | 177 -- third_party/py/gflags/gflags/argument_parser.py | 480 ---- third_party/py/gflags/gflags/exceptions.py | 133 - third_party/py/gflags/gflags/flag.py | 416 --- .../py/gflags/gflags/flags_formatting_test.py | 201 -- .../gflags/flags_modules_for_testing/__init__.py | 0 .../gflags/flags_modules_for_testing/module_bar.py | 135 - .../gflags/flags_modules_for_testing/module_baz.py | 43 - .../gflags/flags_modules_for_testing/module_foo.py | 141 - .../gflags/gflags/flags_unicode_literals_test.py | 26 - third_party/py/gflags/gflags/flagvalues.py | 1261 --------- .../py/gflags/gflags/third_party/__init__.py | 0 .../py/gflags/gflags/third_party/pep257/LICENSE | 4 - .../gflags/gflags/third_party/pep257/__init__.py | 37 - third_party/py/gflags/gflags/validators.py | 194 -- third_party/py/gflags/gflags2man.py | 0 third_party/py/gflags/gflags_validators.py | 187 ++ .../py/gflags/python_gflags.egg-info/PKG-INFO | 10 + .../py/gflags/python_gflags.egg-info/SOURCES.txt | 30 + .../python_gflags.egg-info/dependency_links.txt | 1 + .../py/gflags/python_gflags.egg-info/top_level.txt | 2 + third_party/py/gflags/setup.cfg | 5 + third_party/py/gflags/setup.py | 28 +- .../tests/flags_modules_for_testing/__init__.py | 0 .../tests/flags_modules_for_testing/module_bar.py | 135 + .../tests/flags_modules_for_testing/module_baz.py | 45 + .../tests/flags_modules_for_testing/module_foo.py | 141 + third_party/py/gflags/tests/gflags_googletest.py | 119 + third_party/py/gflags/tests/gflags_helpxml_test.py | 535 ++++ third_party/py/gflags/tests/gflags_unittest.py | 1949 +++++++++++++ .../py/gflags/tests/gflags_validators_test.py | 220 ++ 42 files changed, 6346 insertions(+), 4682 deletions(-) delete mode 100644 third_party/py/gflags/CONTRIBUTING.md create mode 100644 third_party/py/gflags/NEWS mode change 100644 => 100755 third_party/py/gflags/debian/rules delete mode 100644 third_party/py/gflags/gflags/__init__.py delete mode 100644 third_party/py/gflags/gflags/_helpers.py delete mode 100644 third_party/py/gflags/gflags/_helpers_test.py delete mode 100644 third_party/py/gflags/gflags/argument_parser.py delete mode 100644 third_party/py/gflags/gflags/exceptions.py delete mode 100644 third_party/py/gflags/gflags/flag.py delete mode 100644 third_party/py/gflags/gflags/flags_formatting_test.py delete mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/__init__.py delete mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py delete mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py delete mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py delete mode 100644 third_party/py/gflags/gflags/flags_unicode_literals_test.py delete mode 100644 third_party/py/gflags/gflags/flagvalues.py delete mode 100644 third_party/py/gflags/gflags/third_party/__init__.py delete mode 100644 third_party/py/gflags/gflags/third_party/pep257/LICENSE delete mode 100644 third_party/py/gflags/gflags/third_party/pep257/__init__.py delete mode 100644 third_party/py/gflags/gflags/validators.py mode change 100644 => 100755 third_party/py/gflags/gflags2man.py create mode 100755 third_party/py/gflags/gflags_validators.py create mode 100644 third_party/py/gflags/python_gflags.egg-info/PKG-INFO create mode 100644 third_party/py/gflags/python_gflags.egg-info/SOURCES.txt create mode 100644 third_party/py/gflags/python_gflags.egg-info/dependency_links.txt create mode 100644 third_party/py/gflags/python_gflags.egg-info/top_level.txt create mode 100644 third_party/py/gflags/setup.cfg mode change 100644 => 100755 third_party/py/gflags/setup.py create mode 100644 third_party/py/gflags/tests/flags_modules_for_testing/__init__.py create mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py create mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py create mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py create mode 100644 third_party/py/gflags/tests/gflags_googletest.py create mode 100755 third_party/py/gflags/tests/gflags_helpxml_test.py create mode 100755 third_party/py/gflags/tests/gflags_unittest.py create mode 100755 third_party/py/gflags/tests/gflags_validators_test.py (limited to 'third_party/py') diff --git a/third_party/py/gflags/BUILD b/third_party/py/gflags/BUILD index f0f526daf0..77f2722818 100644 --- a/third_party/py/gflags/BUILD +++ b/third_party/py/gflags/BUILD @@ -8,7 +8,10 @@ filegroup( py_library( name = "gflags", - srcs = glob(["**/*.py"]), + srcs = [ + "__init__.py", + "gflags_validators.py", + ], srcs_version = "PY2AND3", visibility = ["//visibility:public"], ) diff --git a/third_party/py/gflags/CONTRIBUTING.md b/third_party/py/gflags/CONTRIBUTING.md deleted file mode 100644 index 7e8cbf5c00..0000000000 --- a/third_party/py/gflags/CONTRIBUTING.md +++ /dev/null @@ -1,11 +0,0 @@ -Want to contribute? - -We regret that we are currently unable to accept contributions to python-gflags -due to some technical issues. - -If you have a problem you'd like to have solved, please open a -[GitHub issue](https://github.com/google/python-gflags/issues) and we'll try to -resolve it. - -Because we can't accept contributions right now, pull requests will be closed -without review. diff --git a/third_party/py/gflags/ChangeLog b/third_party/py/gflags/ChangeLog index 60dc799f5e..87732a2b97 100644 --- a/third_party/py/gflags/ChangeLog +++ b/third_party/py/gflags/ChangeLog @@ -1,69 +1,3 @@ -Tue Nov 01 00:00:01 2016 Google Inc. - * python-gflags: version 3.1.0. - * Python3 compatibility - * Removed UnrecognizedFlag exception. - * Replaced flags.DuplicateFlag with flags.DuplicateFlagError. - * Moved the validators.Error class to exceptions.ValidationError. - * Renamed IllegalFlagValue to IllegalFlagValueError. - * Removed MutualExclusionValidator class, in favor of flags.MarkFlagsAsMutualExclusive. - * Removed FlagValues.AddValidator method. - * Removed _helpers.GetMainModule. - * Use xml.dom.minidom to create XML strings, instead of manual crafting. - * Declared PEP8-style names. - * Added examples. - - - - * python-gflags: version 3.0.7. - * Removed the unused method ShortestUniquePrefixes. - * Removed _GetCallingModule function alias. - -Fri Aug 05 00:00:01 2016 Google Inc. - - * python-gflags: version 3.0.6 - * Declared pypi package classifiers. - * Added support for CLIF flag processing (not included in python-gflags repo - yet). - -Thu May 12 00:00:01 2016 Google Inc. - - * python-gflags: version 3.0.5 - * Added a warning when FLAGS.SetDefault is used after flags were parsed. - * Added new function: MarkFlagsAsRequired. - -Fri Apr 15 00:00:01 2016 Google Inc. - - * python-gflags: version 3.0.4 - * One more fix for setup.py - this time about third_party package. - -Mon Apr 11 00:00:01 2016 Google Inc. - - * python-gflags: version 3.0.3 - * Fixed setup.py. - * --noflag if argument is given is no longer allowed. - * Python3 compatibility: removed need for cgi import. - * Disallowed unparsed flag usage after FLAGS.Reset() - -Thu Feb 09 11:55:00 2016 Google Inc. - - * python-gflags: version 3.0.2 - * Fix MANIFEST.in to include all relevant files. - -Thu Feb 04 22:23:00 2016 Google Inc. - - * python-gflags: version 3.0.1 - * Some changes for python3 compatibility. - * Automatically generate ordering operations for Flag. - * Add optional comma compatibility to whitespace-separated list flags. - -Tue Jan 12 16:39:00 2016 Google Inc. - - * python-gflags: version 3.0.0. - * A lot of potentially backwards incompatible changes since 2.0. - * This version is NOT recommended to use in production. Some of the files and - documentation has been lost during export; this will be fixed in next - versions. - Wed Jan 18 13:57:39 2012 Google Inc. * python-gflags: version 2.0 diff --git a/third_party/py/gflags/MANIFEST.in b/third_party/py/gflags/MANIFEST.in index 59c4c5e077..17851bfa77 100644 --- a/third_party/py/gflags/MANIFEST.in +++ b/third_party/py/gflags/MANIFEST.in @@ -3,8 +3,8 @@ include COPYING include ChangeLog include MANIFEST.in include Makefile +include NEWS include README -include *.md include debian/README include debian/changelog include debian/compat @@ -12,5 +12,8 @@ include debian/control include debian/copyright include debian/docs include debian/rules -include *.py -recursive-include gflags * +include gflags.py +include gflags2man.py +include gflags_validators.py +include setup.py +recursive-include tests *.py diff --git a/third_party/py/gflags/NEWS b/third_party/py/gflags/NEWS new file mode 100644 index 0000000000..8aaa72bf30 --- /dev/null +++ b/third_party/py/gflags/NEWS @@ -0,0 +1,78 @@ +== 18 January 2012 == + +[Prependum:] I just realized I should have named the new version 2.0, +to reflect the new ownership and status as a community run project. +Not too late, I guess. I've just released python-gflags 2.0, which is +identical to python-gflags 1.8 except for the version number. + +I've just released python-gflags 1.8. This fixes a bug, allowing +modules defining flags to be re-imported without raising duplicate +flag errors. + +Administrative note: In the coming weeks, I'll be stepping down as +maintainer for the python-gflags project, and as part of that Google +is relinquishing ownership of the project; it will now be entirely +community run. The remaining +[http://python-gflags.googlecode.com/svn/tags/python-gflags-1.8/ChangeLog changes] +in this release reflect that shift. + + +=== 20 December 2011 === + +I've just released python-gflags 1.7. The major change here is +improved unicode support, in both flag default values and +help-strings. We've also made big steps toward making gflags work +with python 3.x (while keeping 2.4 compatibility), and improving +--help output in the common case where output is a tty. + +For a full list of changes since last release, see the +[http://python-gflags.googlecode.com/svn/tags/python-gflags-1.7/ChangeLog ChangeLog]. + +=== 29 July 2011 === + +I've just released python-gflags 1.6. This release has only minor +changes, including support for multi_float flags. The full list of +changes is in the +[http://python-gflags.googlecode.com/svn/tags/python-gflags-1.6/ChangeLog ChangeLog]. + +The major change with this release is procedural: I've changed the +internal tools used to integrate Google-supplied patches for gflags +into the opensource release. These new tools should result in more +frequent updates with better change descriptions. They will also +result in future `ChangeLog` entries being much more verbose (for +better or for worse). + +=== 26 January 2011 === + +I've just released python-gflags 1.5.1. I had improperly packaged +python-gflags 1.5, so it probably doesn't work. All users who have +updated to python-gflags 1.5 are encouraged to update again to 1.5.1. + +=== 24 January 2011 === + +I've just released python-gflags 1.5. This release adds support for +flag verifiers: small functions you can associate with flags, that are +called whenever the flag value is set or modified, and can verify that +the new value is legal. It also has other, minor changes, described +in the +[http://python-gflags.googlecode.com/svn/tags/python-gflags-1.5/ChangeLog ChangeLog]. + +=== 11 October 2010 === + +I've just released python-gflags 1.4. This release has only minor +changes from 1.3, including support for printing flags of a specific +module, allowing key-flags to work with special flags, somewhat better +error messaging, and +[http://python-gflags.googlecode.com/svn/tags/python-gflags-1.4/ChangeLog so forth]. +If 1.3 is working well for you, there's no particular reason to upgrade. + +=== 4 January 2010 === + +I just released python-gflags 1.3. This is the first python-gflags +release; it is version 1.3 because this code is forked from the 1.3 +release of google-gflags. + +I don't have a tarball or .deb file up quite yet, so for now you will +have to get the source files by browsing under the 'source' +tag. Downloadable files will be available soon. + diff --git a/third_party/py/gflags/PKG-INFO b/third_party/py/gflags/PKG-INFO index f290599483..faab7198f2 100644 --- a/third_party/py/gflags/PKG-INFO +++ b/third_party/py/gflags/PKG-INFO @@ -1,17 +1,10 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.0 Name: python-gflags -Version: 3.1.0 +Version: 2.0 Summary: Google Commandline Flags Module -Home-page: https://github.com/google/python-gflags +Home-page: http://code.google.com/p/python-gflags Author: Google Inc. and others Author-email: google-gflags@googlegroups.com License: BSD Description: UNKNOWN Platform: UNKNOWN -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Intended Audience :: Developers -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Requires: six diff --git a/third_party/py/gflags/README.md b/third_party/py/gflags/README.md index 786d817c67..da2a03fc1c 100644 --- a/third_party/py/gflags/README.md +++ b/third_party/py/gflags/README.md @@ -1,6 +1,6 @@ -[gflags](https://github.com/google/python-gflags) +[gflags](https://python-gflags.googlecode.com) -------- -* Version: 3.1.0 +* Version: 2.0 * License: New BSD License -* From: [https://pypi.python.org/packages/82/9c/7ed91459f01422d90a734afcf30de7df6b701b90a2e7c7a7d01fd580242d/python-gflags-3.1.0.tar.gz](https://pypi.python.org/packages/82/9c/7ed91459f01422d90a734afcf30de7df6b701b90a2e7c7a7d01fd580242d/python-gflags-3.1.0.tar.gz) +* From: [https://python-gflags.googlecode.com/files/python-gflags-2.0.tar.gz](https://python-gflags.googlecode.com/files/python-gflags-2.0.tar.gz) diff --git a/third_party/py/gflags/__init__.py b/third_party/py/gflags/__init__.py index 77e4285448..23a3135207 100644 --- a/third_party/py/gflags/__init__.py +++ b/third_party/py/gflags/__init__.py @@ -1 +1,2863 @@ -from gflags import * +#!/usr/bin/env python +# +# Copyright (c) 2002, 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 attributes of the 'FlagValues' object. + +Code can access the flag through a FlagValues object, for instance +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_bool or +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. + Example: --myspacesepflag "foo bar baz" + +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 prints a list of all the flags in a human-readable fashion + --helpshort prints a list of all key flags (see below). + --helpxml prints a list of all flags, in XML format. DO NOT parse + the output of --help and --helpshort. Instead, parse + the output of --helpxml. For more info, see + "OUTPUT FOR --helpxml" below. + --flagfile=foo read flags from file foo. + --undefok=f1,f2 ignore unrecognized option errors for f1,f2. + For boolean flags, you should use --undefok=boolflag, and + --boolflag and --noboolflag will be accepted. Do not use + --undefok=noboolflag. + -- as in getopt(), terminates flag-processing + + +FLAGS VALIDATORS: If your program: + - requires flag X to be specified + - needs flag Y to match a regular expression + - or requires any more general constraint to be satisfied +then validators are for you! + +Each validator represents a constraint over one flag, which is enforced +starting from the initial parsing of the flags and until the program +terminates. + +Also, lower_bound and upper_bound for numerical flags are enforced using flag +validators. + +Howto: +If you want to enforce a constraint over one flag, use + +gflags.RegisterValidator(flag_name, + checker, + message='Flag validation failed', + flag_values=FLAGS) + +After flag values are initially parsed, and after any change to the specified +flag, method checker(flag_value) will be executed. If constraint is not +satisfied, an IllegalFlagValue exception will be raised. See +RegisterValidator's docstring for a detailed explanation on how to construct +your own checker. + + +EXAMPLE USAGE: + +FLAGS = gflags.FLAGS + +gflags.DEFINE_integer('my_version', 0, 'Version number.') +gflags.DEFINE_string('filename', None, 'Input file name', short_name='f') + +gflags.RegisterValidator('my_version', + lambda value: value % 2 == 0, + message='--my_version must be divisible by 2') +gflags.MarkFlagAsRequired('filename') + + +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= 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: + + + 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', 'your name') + gflags.DEFINE_integer('age', None, 'your age in years', lower_bound=0) + gflags.DEFINE_boolean('debug', False, 'produces debugging output') + gflags.DEFINE_enum('gender', 'male', ['male', 'female'], '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 is not None: + print 'You are a %d year old %s' % (FLAGS.age, FLAGS.gender) + + if __name__ == '__main__': + main(sys.argv) + + +KEY FLAGS: + +As we already explained, each module gains access to all flags defined +by all the other modules it transitively imports. In the case of +non-trivial scripts, this means a lot of flags ... For documentation +purposes, it is good to identify the flags that are key (i.e., really +important) to a module. Clearly, the concept of "key flag" is a +subjective one. When trying to determine whether a flag is key to a +module or not, assume that you are trying to explain your module to a +potential user: which flags would you really like to mention first? + +We'll describe shortly how to declare which flags are key to a module. +For the moment, assume we know the set of key flags for each module. +Then, if you use the app.py module, you can use the --helpshort flag to +print only the help for the flags that are key to the main module, in a +human-readable format. + +NOTE: If you need to parse the flag help, do NOT use the output of +--help / --helpshort. That output is meant for human consumption, and +may be changed in the future. Instead, use --helpxml; flags that are +key for the main module are marked there with a yes element. + +The set of key flags for a module M is composed of: + +1. Flags defined by module M by calling a DEFINE_* function. + +2. Flags that module M explictly declares as key by using the function + + DECLARE_key_flag() + +3. Key flags of other modules that M specifies by using the function + + ADOPT_module_key_flags() + + This is a "bulk" declaration of key flags: each flag that is key for + becomes key for the current module too. + +Notice that if you do not use the functions described at points 2 and 3 +above, then --helpshort prints information only about the flags defined +by the main module of our script. In many cases, this behavior is good +enough. But if you move part of the main module code (together with the +related flags) into a different module, then it is nice to use +DECLARE_key_flag / ADOPT_module_key_flags and make sure --helpshort +lists all relevant flags (otherwise, your code refactoring may confuse +your users). + +Note: each of DECLARE_key_flag / ADOPT_module_key_flags has its own +pluses and minuses: DECLARE_key_flag is more targeted and may lead a +more focused --helpshort documentation. ADOPT_module_key_flags is good +for cases when an entire module is considered key to the current script. +Also, it does not require updates to client scripts when a new flag is +added to the module. + + +EXAMPLE USAGE 2 (WITH KEY FLAGS): + +Consider an application that contains the following three files (two +auxiliary modules and a main module) + +File libfoo.py: + + import gflags + + gflags.DEFINE_integer('num_replicas', 3, 'Number of replicas to start') + gflags.DEFINE_boolean('rpc2', True, 'Turn on the usage of RPC2.') + + ... some code ... + +File libbar.py: + + import gflags + + gflags.DEFINE_string('bar_gfs_path', '/gfs/path', + 'Path to the GFS files for libbar.') + gflags.DEFINE_string('email_for_bar_errors', 'bar-team@google.com', + 'Email address for bug reports about module libbar.') + gflags.DEFINE_boolean('bar_risky_hack', False, + 'Turn on an experimental and buggy optimization.') + + ... some code ... + +File myscript.py: + + import gflags + import libfoo + import libbar + + gflags.DEFINE_integer('num_iterations', 0, 'Number of iterations.') + + # Declare that all flags that are key for libfoo are + # key for this module too. + gflags.ADOPT_module_key_flags(libfoo) + + # Declare that the flag --bar_gfs_path (defined in libbar) is key + # for this module. + gflags.DECLARE_key_flag('bar_gfs_path') + + ... some code ... + +When myscript is invoked with the flag --helpshort, the resulted help +message lists information about all the key flags for myscript: +--num_iterations, --num_replicas, --rpc2, and --bar_gfs_path. + +Of course, myscript uses all the flags declared by it (in this case, +just --num_replicas) or by any of the modules it transitively imports +(e.g., the modules libfoo, libbar). E.g., it can access the value of +FLAGS.bar_risky_hack, even if --bar_risky_hack is not declared as a key +flag for myscript. + + +OUTPUT FOR --helpxml: + +The --helpxml flag generates output with the following structure: + + + + PROGRAM_BASENAME + MAIN_MODULE_DOCSTRING + ( + [yes] + DECLARING_MODULE + FLAG_NAME + FLAG_HELP_MESSAGE + DEFAULT_FLAG_VALUE + CURRENT_FLAG_VALUE + FLAG_TYPE + [OPTIONAL_ELEMENTS] + )* + + +Notes: + +1. The output is intentionally similar to the output generated by the +C++ command-line flag library. The few differences are due to the +Python flags that do not have a C++ equivalent (at least not yet), +e.g., DEFINE_list. + +2. New XML elements may be added in the future. + +3. DEFAULT_FLAG_VALUE is in serialized form, i.e., the string you can +pass for this flag on the command-line. E.g., for a flag defined +using DEFINE_list, this field may be foo,bar, not ['foo', 'bar']. + +4. CURRENT_FLAG_VALUE is produced using str(). This means that the +string 'false' will be represented in the same way as the boolean +False. Using repr() would have removed this ambiguity and simplified +parsing, but would have broken the compatibility with the C++ +command-line flags. + +5. OPTIONAL_ELEMENTS describe elements relevant for certain kinds of +flags: lower_bound, upper_bound (for flags that specify bounds), +enum_value (for enum flags), list_separator (for flags that consist of +a list of values, separated by a special token). + +6. We do not provide any example here: please use --helpxml instead. + +This module requires at least python 2.2.1 to run. +""" +from __future__ import print_function + +import cgi +import getopt +import os +import re +import string +import struct +import sys +# pylint: disable-msg=C6204 +try: + import fcntl +except ImportError: + fcntl = None +try: + # Importing termios will fail on non-unix platforms. + import termios +except ImportError: + termios = None + +import gflags_validators +# pylint: enable-msg=C6204 + + +# Are we running under pychecker? +_RUNNING_PYCHECKER = 'pychecker.python' in sys.modules + + +def _GetCallingModuleObjectAndName(): + """Returns the module that's calling into this module. + + We generally use this function to get the name of 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(): + globals_for_frame = sys._getframe(depth).f_globals + module, module_name = _GetModuleObjectAndName(globals_for_frame) + if module_name is not None: + return module, module_name + raise AssertionError("No module was found") + + +def _GetCallingModule(): + """Returns the name of the module that's calling into this module.""" + return _GetCallingModuleObjectAndName()[1] + + +def _GetThisModuleObjectAndName(): + """Returns: (module object, module name) for this module.""" + return _GetModuleObjectAndName(globals()) + + +# module exceptions: +class FlagsError(Exception): + """The base class for all flags errors.""" + pass + + +class DuplicateFlag(FlagsError): + """Raised if there is a flag naming conflict.""" + pass + +class CantOpenFlagFileError(FlagsError): + """Raised if flagfile fails to open: doesn't exist, wrong permissions, etc.""" + pass + + +class DuplicateFlagCannotPropagateNoneToSwig(DuplicateFlag): + """Special case of DuplicateFlag -- SWIG flag value can't be set to None. + + This can be raised when a duplicate flag is created. Even if allow_override is + True, we still abort if the new value is None, because it's currently + impossible to pass None default value back to SWIG. See FlagValues.SetDefault + for details. + """ + pass + + +class DuplicateFlagError(DuplicateFlag): + """A DuplicateFlag whose message cites the conflicting definitions. + + A DuplicateFlagError conveys more information than a DuplicateFlag, + namely the modules where the conflicting definitions occur. This + class was created to avoid breaking external modules which depend on + the existing DuplicateFlags interface. + """ + + def __init__(self, flagname, flag_values, other_flag_values=None): + """Create a DuplicateFlagError. + + Args: + flagname: Name of the flag being redefined. + flag_values: FlagValues object containing the first definition of + flagname. + other_flag_values: If this argument is not None, it should be the + FlagValues object where the second definition of flagname occurs. + If it is None, we assume that we're being called when attempting + to create the flag a second time, and we use the module calling + this one as the source of the second definition. + """ + self.flagname = flagname + first_module = flag_values.FindModuleDefiningFlag( + flagname, default='') + if other_flag_values is None: + second_module = _GetCallingModule() + else: + second_module = other_flag_values.FindModuleDefiningFlag( + flagname, default='') + msg = "The flag '%s' is defined twice. First from %s, Second from %s" % ( + self.flagname, first_module, second_module) + DuplicateFlag.__init__(self, msg) + + +class IllegalFlagValue(FlagsError): + """The flag command line argument is illegal.""" + pass + + +class UnrecognizedFlag(FlagsError): + """Raised if a flag is unrecognized.""" + pass + + +# An UnrecognizedFlagError conveys more information than an UnrecognizedFlag. +# Since there are external modules that create DuplicateFlags, the interface to +# DuplicateFlag shouldn't change. The flagvalue will be assigned the full value +# of the flag and its argument, if any, allowing handling of unrecognized flags +# in an exception handler. +# If flagvalue is the empty string, then this exception is an due to a +# reference to a flag that was not already defined. +class UnrecognizedFlagError(UnrecognizedFlag): + def __init__(self, flagname, flagvalue=''): + self.flagname = flagname + self.flagvalue = flagvalue + UnrecognizedFlag.__init__( + self, "Unknown command line flag '%s'" % flagname) + +# Global variable used by expvar +_exported_flags = {} +_help_width = 80 # width of help output + + +def GetHelpWidth(): + """Returns: an integer, the width of help lines that is used in TextWrap.""" + if (not sys.stdout.isatty()) or (termios is None) or (fcntl is None): + return _help_width + try: + data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234') + columns = struct.unpack('hh', data)[1] + # Emacs mode returns 0. + # Here we assume that any value below 40 is unreasonable + if columns >= 40: + return columns + # Returning an int as default is fine, int(int) just return the int. + return int(os.getenv('COLUMNS', _help_width)) + + except (TypeError, IOError, struct.error): + return _help_width + + +def CutCommonSpacePrefix(text): + """Removes a common space prefix from the lines of a multiline text. + + If the first line does not start with a space, it is left as it is and + only in the remaining lines a common space prefix is being searched + for. That means the first line will stay untouched. This is especially + useful to turn doc strings into help texts. This is because some + people prefer to have the doc comment start already after the + apostrophe and then align the following lines while others have the + apostrophes on a separate line. + + The function also drops trailing empty lines and ignores empty lines + following the initial content line while calculating the initial + common whitespace. + + Args: + text: text to work on + + Returns: + the resulting text + """ + text_lines = text.splitlines() + # Drop trailing empty lines + while text_lines and not text_lines[-1]: + text_lines = text_lines[:-1] + if text_lines: + # We got some content, is the first line starting with a space? + if text_lines[0] and text_lines[0][0].isspace(): + text_first_line = [] + else: + text_first_line = [text_lines.pop(0)] + # Calculate length of common leading whitespace (only over content lines) + common_prefix = os.path.commonprefix([line for line in text_lines if line]) + space_prefix_len = len(common_prefix) - len(common_prefix.lstrip()) + # If we have a common space prefix, drop it from all lines + if space_prefix_len: + for index in xrange(len(text_lines)): + if text_lines[index]: + text_lines[index] = text_lines[index][space_prefix_len:] + return '\n'.join(text_first_line + text_lines) + return '' + + +def TextWrap(text, length=None, indent='', firstline_indent=None, tabs=' '): + """Wraps a given text to a maximum line length and returns it. + + We turn lines that only contain whitespace into empty lines. We keep + new lines and tabs (e.g., we do not treat tabs as spaces). + + Args: + text: text to wrap + length: maximum length of a line, includes indentation + if this is None then use GetHelpWidth() + indent: indent for all but first line + firstline_indent: indent for first line; if None, fall back to indent + tabs: replacement for tabs + + Returns: + wrapped text + + Raises: + FlagsError: if indent not shorter than length + FlagsError: if firstline_indent not shorter than length + """ + # Get defaults where callee used None + if length is None: + length = GetHelpWidth() + if indent is None: + indent = '' + if len(indent) >= length: + raise FlagsError('Indent must be shorter than length') + # In line we will be holding the current line which is to be started + # with indent (or firstline_indent if available) and then appended + # with words. + if firstline_indent is None: + firstline_indent = '' + line = indent + else: + line = firstline_indent + if len(firstline_indent) >= length: + raise FlagsError('First line indent must be shorter than length') + + # If the callee does not care about tabs we simply convert them to + # spaces If callee wanted tabs to be single space then we do that + # already here. + if not tabs or tabs == ' ': + text = text.replace('\t', ' ') + else: + tabs_are_whitespace = not tabs.strip() + + line_regex = re.compile('([ ]*)(\t*)([^ \t]+)', re.MULTILINE) + + # Split the text into lines and the lines with the regex above. The + # resulting lines are collected in result[]. For each split we get the + # spaces, the tabs and the next non white space (e.g. next word). + result = [] + for text_line in text.splitlines(): + # Store result length so we can find out whether processing the next + # line gave any new content + old_result_len = len(result) + # Process next line with line_regex. For optimization we do an rstrip(). + # - process tabs (changes either line or word, see below) + # - process word (first try to squeeze on line, then wrap or force wrap) + # Spaces found on the line are ignored, they get added while wrapping as + # needed. + for spaces, current_tabs, word in line_regex.findall(text_line.rstrip()): + # If tabs weren't converted to spaces, handle them now + if current_tabs: + # If the last thing we added was a space anyway then drop + # it. But let's not get rid of the indentation. + if (((result and line != indent) or + (not result and line != firstline_indent)) and line[-1] == ' '): + line = line[:-1] + # Add the tabs, if that means adding whitespace, just add it at + # the line, the rstrip() code while shorten the line down if + # necessary + if tabs_are_whitespace: + line += tabs * len(current_tabs) + else: + # if not all tab replacement is whitespace we prepend it to the word + word = tabs * len(current_tabs) + word + # Handle the case where word cannot be squeezed onto current last line + if len(line) + len(word) > length and len(indent) + len(word) <= length: + result.append(line.rstrip()) + line = indent + word + word = '' + # No space left on line or can we append a space? + if len(line) + 1 >= length: + result.append(line.rstrip()) + line = indent + else: + line += ' ' + # Add word and shorten it up to allowed line length. Restart next + # line with indent and repeat, or add a space if we're done (word + # finished) This deals with words that cannot fit on one line + # (e.g. indent + word longer than allowed line length). + while len(line) + len(word) >= length: + line += word + result.append(line[:length]) + word = line[length:] + line = indent + # Default case, simply append the word and a space + if word: + line += word + ' ' + # End of input line. If we have content we finish the line. If the + # current line is just the indent but we had content in during this + # original line then we need to add an empty line. + if (result and line != indent) or (not result and line != firstline_indent): + result.append(line.rstrip()) + elif len(result) == old_result_len: + result.append('') + line = indent + + return '\n'.join(result) + + +def DocToHelp(doc): + """Takes a __doc__ string and reformats it as help.""" + + # Get rid of starting and ending white space. Using lstrip() or even + # strip() could drop more than maximum of first line and right space + # of last line. + doc = doc.strip() + + # Get rid of all empty lines + whitespace_only_line = re.compile('^[ \t]+$', re.M) + doc = whitespace_only_line.sub('', doc) + + # Cut out common space at line beginnings + doc = CutCommonSpacePrefix(doc) + + # Just like this module's comment, comments tend to be aligned somehow. + # In other words they all start with the same amount of white space + # 1) keep double new lines + # 2) keep ws after new lines if not empty line + # 3) all other new lines shall be changed to a space + # Solution: Match new lines between non white space and replace with space. + doc = re.sub('(?<=\S)\n(?=\S)', ' ', doc, re.M) + + return doc + + +def _GetModuleObjectAndName(globals_dict): + """Returns the module that defines a global environment, and its name. + + Args: + globals_dict: A dictionary that should correspond to an environment + providing the values of the globals. + + Returns: + A pair consisting of (1) module object and (2) module name (a + string). Returns (None, None) if the module could not be + identified. + """ + # The use of .items() (instead of .iteritems()) is NOT a mistake: if + # a parallel thread imports a module while we iterate over + # .iteritems() (not nice, but possible), we get a RuntimeError ... + # Hence, we use the slightly slower but safer .items(). + for name, module in sys.modules.items(): + if getattr(module, '__dict__', None) is globals_dict: + if name == '__main__': + # Pick a more informative name for the main module. + name = sys.argv[0] + return (module, name) + return (None, None) + + +def _GetMainModule(): + """Returns: string, name of the module from which execution started.""" + # First, try to use the same logic used by _GetCallingModuleObjectAndName(), + # i.e., call _GetModuleObjectAndName(). For that we first need to + # find the dictionary that the main module uses to store the + # globals. + # + # That's (normally) the same dictionary object that the deepest + # (oldest) stack frame is using for globals. + deepest_frame = sys._getframe(0) + while deepest_frame.f_back is not None: + deepest_frame = deepest_frame.f_back + globals_for_main_module = deepest_frame.f_globals + main_module_name = _GetModuleObjectAndName(globals_for_main_module)[1] + # The above strategy fails in some cases (e.g., tools that compute + # code coverage by redefining, among other things, the main module). + # If so, just use sys.argv[0]. We can probably always do this, but + # it's safest to try to use the same logic as _GetCallingModuleObjectAndName() + if main_module_name is None: + main_module_name = sys.argv[0] + return main_module_name + + +class FlagValues: + """Registry of '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 attribute of the registered 'Flag' objects can be accessed + as attributes 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. + + # Dictionary: flag name (string) -> Flag object. + self.__dict__['__flags'] = {} + # Dictionary: module name (string) -> list of Flag objects that are defined + # by that module. + self.__dict__['__flags_by_module'] = {} + # Dictionary: module id (int) -> list of Flag objects that are defined by + # that module. + self.__dict__['__flags_by_module_id'] = {} + # Dictionary: module name (string) -> list of Flag objects that are + # key for that module. + self.__dict__['__key_flags_by_module'] = {} + + # Set if we should use new style gnu_getopt rather than getopt when parsing + # the args. Only possible with Python 2.3+ + self.UseGnuGetOpt(False) + + def UseGnuGetOpt(self, use_gnu_getopt=True): + """Use GNU-style scanning. Allows mixing of flag and non-flag arguments. + + See http://docs.python.org/library/getopt.html#getopt.gnu_getopt + + Args: + use_gnu_getopt: wether or not to use GNU style scanning. + """ + self.__dict__['__use_gnu_getopt'] = use_gnu_getopt + + def IsGnuGetOpt(self): + return self.__dict__['__use_gnu_getopt'] + + def FlagDict(self): + return self.__dict__['__flags'] + + def FlagsByModuleDict(self): + """Returns the dictionary of module_name -> list of defined flags. + + Returns: + A dictionary. Its keys are module names (strings). Its values + are lists of Flag objects. + """ + return self.__dict__['__flags_by_module'] + + def FlagsByModuleIdDict(self): + """Returns the dictionary of module_id -> list of defined flags. + + Returns: + A dictionary. Its keys are module IDs (ints). Its values + are lists of Flag objects. + """ + return self.__dict__['__flags_by_module_id'] + + def KeyFlagsByModuleDict(self): + """Returns the dictionary of module_name -> list of key flags. + + Returns: + A dictionary. Its keys are module names (strings). Its values + are lists of Flag objects. + """ + return self.__dict__['__key_flags_by_module'] + + def _RegisterFlagByModule(self, module_name, flag): + """Records the module that defines a specific flag. + + We keep track of which flag is defined by which module so that we + can later sort the flags by module. + + Args: + module_name: A string, the name of a Python module. + flag: A Flag object, a flag that is key to the module. + """ + flags_by_module = self.FlagsByModuleDict() + flags_by_module.setdefault(module_name, []).append(flag) + + def _RegisterFlagByModuleId(self, module_id, flag): + """Records the module that defines a specific flag. + + Args: + module_id: An int, the ID of the Python module. + flag: A Flag object, a flag that is key to the module. + """ + flags_by_module_id = self.FlagsByModuleIdDict() + flags_by_module_id.setdefault(module_id, []).append(flag) + + def _RegisterKeyFlagForModule(self, module_name, flag): + """Specifies that a flag is a key flag for a module. + + Args: + module_name: A string, the name of a Python module. + flag: A Flag object, a flag that is key to the module. + """ + key_flags_by_module = self.KeyFlagsByModuleDict() + # The list of key flags for the module named module_name. + key_flags = key_flags_by_module.setdefault(module_name, []) + # Add flag, but avoid duplicates. + if flag not in key_flags: + key_flags.append(flag) + + def _GetFlagsDefinedByModule(self, module): + """Returns the list of flags defined by a module. + + Args: + module: A module object or a module name (a string). + + Returns: + A new list of Flag objects. Caller may update this list as he + wishes: none of those changes will affect the internals of this + FlagValue object. + """ + if not isinstance(module, str): + module = module.__name__ + + return list(self.FlagsByModuleDict().get(module, [])) + + def _GetKeyFlagsForModule(self, module): + """Returns the list of key flags for a module. + + Args: + module: A module object or a module name (a string) + + Returns: + A new list of Flag objects. Caller may update this list as he + wishes: none of those changes will affect the internals of this + FlagValue object. + """ + if not isinstance(module, str): + module = module.__name__ + + # Any flag is a key flag for the module that defined it. NOTE: + # key_flags is a fresh list: we can update it without affecting the + # internals of this FlagValues object. + key_flags = self._GetFlagsDefinedByModule(module) + + # Take into account flags explicitly declared as key for a module. + for flag in self.KeyFlagsByModuleDict().get(module, []): + if flag not in key_flags: + key_flags.append(flag) + return key_flags + + def FindModuleDefiningFlag(self, flagname, default=None): + """Return the name of the module defining this flag, or default. + + Args: + flagname: Name of the flag to lookup. + default: Value to return if flagname is not defined. Defaults + to None. + + Returns: + The name of the module which registered the flag with this name. + If no such module exists (i.e. no flag with this name exists), + we return default. + """ + for module, flags in self.FlagsByModuleDict().iteritems(): + for flag in flags: + if flag.name == flagname or flag.short_name == flagname: + return module + return default + + def FindModuleIdDefiningFlag(self, flagname, default=None): + """Return the ID of the module defining this flag, or default. + + Args: + flagname: Name of the flag to lookup. + default: Value to return if flagname is not defined. Defaults + to None. + + Returns: + The ID of the module which registered the flag with this name. + If no such module exists (i.e. no flag with this name exists), + we return default. + """ + for module_id, flags in self.FlagsByModuleIdDict().iteritems(): + for flag in flags: + if flag.name == flagname or flag.short_name == flagname: + return module_id + return default + + def AppendFlagValues(self, flag_values): + """Appends flags registered in another FlagValues instance. + + Args: + flag_values: registry to copy from + """ + for flag_name, flag in flag_values.FlagDict().iteritems(): + # Each flags with shortname appears here twice (once under its + # normal name, and again with its short name). To prevent + # problems (DuplicateFlagError) with double flag registration, we + # perform a check to make sure that the entry we're looking at is + # for its normal name. + if flag_name == flag.name: + try: + self[flag_name] = flag + except DuplicateFlagError: + raise DuplicateFlagError(flag_name, self, + other_flag_values=flag_values) + + def RemoveFlagValues(self, flag_values): + """Remove flags that were previously appended from another FlagValues. + + Args: + flag_values: registry containing flags to remove. + """ + for flag_name in flag_values.FlagDict(): + self.__delattr__(flag_name) + + def __setitem__(self, name, flag): + """Registers 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 (name in fl and not flag.allow_override and + not fl[name].allow_override and not _RUNNING_PYCHECKER): + module, module_name = _GetCallingModuleObjectAndName() + if (self.FindModuleDefiningFlag(name) == module_name and + id(module) != self.FindModuleIdDefiningFlag(name)): + # If the flag has already been defined by a module with the same name, + # but a different ID, we can stop here because it indicates that the + # module is simply being imported a subsequent time. + return + raise DuplicateFlagError(name, self) + short_name = flag.short_name + if short_name is not None: + if (short_name in fl and not flag.allow_override and + not fl[short_name].allow_override and not _RUNNING_PYCHECKER): + raise DuplicateFlagError(short_name, self) + fl[short_name] = flag + fl[name] = flag + global _exported_flags + _exported_flags[name] = flag + + def __getitem__(self, name): + """Retrieves the Flag object for the flag --name.""" + return self.FlagDict()[name] + + def __getattr__(self, name): + """Retrieves the 'value' attribute of the flag --name.""" + fl = self.FlagDict() + if name not in fl: + raise AttributeError(name) + return fl[name].value + + def __setattr__(self, name, value): + """Sets the 'value' attribute of the flag --name.""" + fl = self.FlagDict() + fl[name].value = value + self._AssertValidators(fl[name].validators) + return value + + def _AssertAllValidators(self): + all_validators = set() + for flag in self.FlagDict().itervalues(): + for validator in flag.validators: + all_validators.add(validator) + self._AssertValidators(all_validators) + + def _AssertValidators(self, validators): + """Assert if all validators in the list are satisfied. + + Asserts validators in the order they were created. + Args: + validators: Iterable(gflags_validators.Validator), validators to be + verified + Raises: + AttributeError: if validators work with a non-existing flag. + IllegalFlagValue: if validation fails for at least one validator + """ + for validator in sorted( + validators, key=lambda validator: validator.insertion_index): + try: + validator.Verify(self) + except gflags_validators.Error as e: + message = validator.PrintFlagsWithValues(self) + raise IllegalFlagValue('%s: %s' % (message, str(e))) + + def _FlagIsRegistered(self, flag_obj): + """Checks whether a Flag object is registered under some name. + + Note: this is non trivial: in addition to its normal name, a flag + may have a short name too. In self.FlagDict(), both the normal and + the short name are mapped to the same flag object. E.g., calling + only "del FLAGS.short_name" is not unregistering the corresponding + Flag object (it is still registered under the longer name). + + Args: + flag_obj: A Flag object. + + Returns: + A boolean: True iff flag_obj is registered under some name. + """ + flag_dict = self.FlagDict() + # Check whether flag_obj is registered under its long name. + name = flag_obj.name + if flag_dict.get(name, None) == flag_obj: + return True + # Check whether flag_obj is registered under its short name. + short_name = flag_obj.short_name + if (short_name is not None and + flag_dict.get(short_name, None) == flag_obj): + return True + # The flag cannot be registered under any other name, so we do not + # need to do a full search through the values of self.FlagDict(). + return False + + def __delattr__(self, flag_name): + """Deletes a previously-defined flag from a flag object. + + This method makes sure we can delete a flag by using + + del flag_values_object. + + E.g., + + gflags.DEFINE_integer('foo', 1, 'Integer flag.') + del gflags.FLAGS.foo + + Args: + flag_name: A string, the name of the flag to be deleted. + + Raises: + AttributeError: When there is no registered flag named flag_name. + """ + fl = self.FlagDict() + if flag_name not in fl: + raise AttributeError(flag_name) + + flag_obj = fl[flag_name] + del fl[flag_name] + + if not self._FlagIsRegistered(flag_obj): + # If the Flag object indicated by flag_name is no longer + # registered (please see the docstring of _FlagIsRegistered), then + # we delete the occurrences of the flag object in all our internal + # dictionaries. + self.__RemoveFlagFromDictByModule(self.FlagsByModuleDict(), flag_obj) + self.__RemoveFlagFromDictByModule(self.FlagsByModuleIdDict(), flag_obj) + self.__RemoveFlagFromDictByModule(self.KeyFlagsByModuleDict(), flag_obj) + + def __RemoveFlagFromDictByModule(self, flags_by_module_dict, flag_obj): + """Removes a flag object from a module -> list of flags dictionary. + + Args: + flags_by_module_dict: A dictionary that maps module names to lists of + flags. + flag_obj: A flag object. + """ + for unused_module, flags_in_module in flags_by_module_dict.iteritems(): + # while (as opposed to if) takes care of multiple occurrences of a + # flag in the list for the same module. + while flag_obj in flags_in_module: + flags_in_module.remove(flag_obj) + + def SetDefault(self, name, value): + """Changes the default value of the named flag object.""" + fl = self.FlagDict() + if name not in fl: + raise AttributeError(name) + fl[name].SetDefault(value) + self._AssertValidators(fl[name].validators) + + def __contains__(self, name): + """Returns 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 iter(self.FlagDict()) + + def __call__(self, argv): + """Parses flags from argv; stores parsed flags into 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 + + Args: + argv: argument list. Can be of any type that may be converted to a list. + + Returns: + The list of arguments not parsed as options, including argv[0] + + Raises: + FlagsError: on any parsing error + """ + # 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 = argv[:1] + self.ReadFlagsFromFiles(argv[1:], force_gnu=False) + + # 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) # list() makes a copy + 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 occurrences 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 += ":" + + longopts.append('undefok=') + undefok_flags = [] + + # In case --undefok is specified, loop to pick up unrecognized + # options one by one. + unrecognized_opts = [] + args = argv[1:] + while True: + try: + if self.__dict__['__use_gnu_getopt']: + optlist, unparsed_args = getopt.gnu_getopt(args, shortopts, longopts) + else: + optlist, unparsed_args = getopt.getopt(args, shortopts, longopts) + break + except getopt.GetoptError as e: + if not e.opt or e.opt in fl: + # Not an unrecognized option, re-raise the exception as a FlagsError + raise FlagsError(e) + # Remove offender from args and try again + for arg_index in range(len(args)): + if ((args[arg_index] == '--' + e.opt) or + (args[arg_index] == '-' + e.opt) or + (args[arg_index].startswith('--' + e.opt + '='))): + unrecognized_opts.append((e.opt, args[arg_index])) + args = args[0:arg_index] + args[arg_index+1:] + break + else: + # We should have found the option, so we don't expect to get + # here. We could assert, but raising the original exception + # might work better. + raise FlagsError(e) + + for name, arg in optlist: + if name == '--undefok': + flag_names = arg.split(',') + undefok_flags.extend(flag_names) + # For boolean flags, if --undefok=boolflag is specified, then we should + # also accept --noboolflag, in addition to --boolflag. + # Since we don't know the type of the undefok'd flag, this will affect + # non-boolean flags as well. + # NOTE: You shouldn't use --undefok=noboolflag, because then we will + # accept --nonoboolflag here. We are choosing not to do the conversion + # from noboolflag -> boolflag because of the ambiguity that flag names + # can start with 'no'. + undefok_flags.extend('no' + name for name in flag_names) + continue + if name.startswith('--'): + # long option + name = name[2:] + short_option = 0 + else: + # short option + name = name[1:] + short_option = 1 + if name in fl: + flag = fl[name] + if flag.boolean and short_option: arg = 1 + flag.Parse(arg) + + # If there were unrecognized options, raise an exception unless + # the options were named via --undefok. + for opt, value in unrecognized_opts: + if opt not in undefok_flags: + raise UnrecognizedFlagError(opt, value) + + if unparsed_args: + if self.__dict__['__use_gnu_getopt']: + # if using gnu_getopt just return the program name + remainder of argv. + ret_val = argv[:1] + unparsed_args + else: + # 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. + ret_val = argv[:1] + original_argv[-len(unparsed_args):] + else: + ret_val = argv[:1] + + self._AssertAllValidators() + return ret_val + + def Reset(self): + """Resets the values to the point before FLAGS(argv) was called.""" + for f in self.FlagDict().values(): + f.Unparse() + + def RegisteredFlags(self): + """Returns: a list of the names and short names of all registered flags.""" + return list(self.FlagDict()) + + def FlagValuesDict(self): + """Returns: a dictionary that maps flag names to flag 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): + """Generates a help string for all known flags.""" + return self.GetHelp() + + def GetHelp(self, prefix=''): + """Generates a help string for all known flags.""" + helplist = [] + + flags_by_module = self.FlagsByModuleDict() + if flags_by_module: + + modules = sorted(flags_by_module) + + # 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.__RenderOurModuleFlags(module, helplist) + + self.__RenderModuleFlags('gflags', + _SPECIAL_FLAGS.FlagDict().values(), + helplist) + + else: + # Just print one long list of flags. + self.__RenderFlagList( + self.FlagDict().values() + _SPECIAL_FLAGS.FlagDict().values(), + helplist, prefix) + + return '\n'.join(helplist) + + def __RenderModuleFlags(self, module, flags, output_lines, prefix=""): + """Generates a help string for a given module.""" + if not isinstance(module, str): + module = module.__name__ + output_lines.append('\n%s%s:' % (prefix, module)) + self.__RenderFlagList(flags, output_lines, prefix + " ") + + def __RenderOurModuleFlags(self, module, output_lines, prefix=""): + """Generates a help string for a given module.""" + flags = self._GetFlagsDefinedByModule(module) + if flags: + self.__RenderModuleFlags(module, flags, output_lines, prefix) + + def __RenderOurModuleKeyFlags(self, module, output_lines, prefix=""): + """Generates a help string for the key flags of a given module. + + Args: + module: A module object or a module name (a string). + output_lines: A list of strings. The generated help message + lines will be appended to this list. + prefix: A string that is prepended to each generated help line. + """ + key_flags = self._GetKeyFlagsForModule(module) + if key_flags: + self.__RenderModuleFlags(module, key_flags, output_lines, prefix) + + def ModuleHelp(self, module): + """Describe the key flags of a module. + + Args: + module: A module object or a module name (a string). + + Returns: + string describing the key flags of a module. + """ + helplist = [] + self.__RenderOurModuleKeyFlags(module, helplist) + return '\n'.join(helplist) + + def MainModuleHelp(self): + """Describe the key flags of the main module. + + Returns: + string describing the key flags of a module. + """ + return self.ModuleHelp(_GetMainModule()) + + def __RenderFlagList(self, flaglist, output_lines, prefix=" "): + fl = self.FlagDict() + special_fl = _SPECIAL_FLAGS.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 and special_fl.get(name, None) != flag: + # a different flag is using this name now + continue + # only print help once + if flag in flagset: 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 + flaghelp = TextWrap(flaghelp, indent=prefix+" ", + firstline_indent=prefix) + if flag.default_as_str: + flaghelp += "\n" + flaghelp += TextWrap("(default: %s)" % flag.default_as_str, + indent=prefix+" ") + if flag.parser.syntactic_help: + flaghelp += "\n" + flaghelp += TextWrap("(%s)" % flag.parser.syntactic_help, + indent=prefix+" ") + output_lines.append(flaghelp) + + def get(self, name, default): + """Returns the value of a flag (if not None) or a default value. + + Args: + name: A string, the name of a flag. + default: Default value to use if the flag 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: dictionary; maps 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): + """Checks whether flag_string contain a --flagfile= directive.""" + 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): + """Returns filename from a flagfile_str of form -[-]flagfile=filename. + + The cases of --flagfile foo and -flagfile foo shouldn't be hitting + this function, as they are dealt with in the level above this + function. + """ + 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) + + def __GetFlagFileLines(self, filename, parsed_file_list): + """Returns the useful (!=comments, etc) lines from a file with flags. + + Args: + filename: A string, the name of the flag file. + parsed_file_list: A list of the names of the files we have + already read. MUTATED BY THIS FUNCTION. + + Returns: + List of strings. See the note below. + + NOTE(springer): This function checks for a nested --flagfile= + tag and handles the lower file recursively. It returns a list of + 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 as e_msg: + raise CantOpenFlagFileError('ERROR:: Unable to open flagfile: %s' % e_msg) + + 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=" 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. + sys.stderr.write('Warning: Hit circular flagfile dependency: %s\n' % + (sub_filename,)) + else: + # Any line that's not a comment or a nested flagfile should get + # copied into 2nd position. This leaves earlier arguments + # further back in the list, thus giving them higher priority. + flag_line_list.append(line.strip()) + return flag_line_list + + def ReadFlagsFromFiles(self, argv, force_gnu=True): + """Processes command line args, but also allow args to be read from file. + + Args: + argv: A list of strings, usually sys.argv[1:], which may contain one or + more flagfile directives of the form --flagfile="./filename". + Note that the name of the program (sys.argv[0]) should be omitted. + force_gnu: If False, --flagfile parsing obeys normal flag semantics. + If True, --flagfile parsing instead follows gnu_getopt semantics. + *** WARNING *** force_gnu=False may become the future default! + + Returns: + + A new list which has the original list combined with what we read + from any flagfile(s). + + References: Global gflags.FLAG class instance. + + This function should be called before the normal FLAGS(argv) call. + This function scans the input list for a flag that looks like: + --flagfile=. Then it opens , 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): + --> Flags from the command line argv _should_ always take precedence! + --> A further "--flagfile=" 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. In this 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.extend( + self.__GetFlagFileLines(flag_filename, parsed_file_list)) + else: + new_argv.append(current_arg) + # Stop parsing after '--', like getopt and gnu_getopt. + if current_arg == '--': + break + # Stop parsing after a non-flag, like getopt. + if not current_arg.startswith('-'): + if not force_gnu and not self.__dict__['__use_gnu_getopt']: + break + + if rest_of_args: + new_argv.extend(rest_of_args) + + return new_argv + + def FlagsIntoString(self): + """Returns a string with the flags assignments from this FlagValues object. + + This function ignores flags whose value is None. Each flag + assignment is separated by a newline. + + NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString + from http://code.google.com/p/google-gflags + """ + 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 assignments from this FlagInfo object to a file. + + Output will be in the format of a flagfile. + + NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile + from http://code.google.com/p/google-gflags + """ + out_file = open(filename, 'a') + out_file.write(self.FlagsIntoString()) + out_file.close() + + def WriteHelpInXMLFormat(self, outfile=None): + """Outputs flag documentation in XML format. + + NOTE: We use element names that are consistent with those used by + the C++ command-line flag library, from + http://code.google.com/p/google-gflags + We also use a few new elements (e.g., ), but we do not + interfere / overlap with existing XML elements used by the C++ + library. Please maintain this consistency. + + Args: + outfile: File object we write to. Default None means sys.stdout. + """ + outfile = outfile or sys.stdout + + outfile.write('\n') + outfile.write('\n') + indent = ' ' + _WriteSimpleXMLElement(outfile, 'program', os.path.basename(sys.argv[0]), + indent) + + usage_doc = sys.modules['__main__'].__doc__ + if not usage_doc: + usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0] + else: + usage_doc = usage_doc.replace('%s', sys.argv[0]) + _WriteSimpleXMLElement(outfile, 'usage', usage_doc, indent) + + # Get list of key flags for the main module. + key_flags = self._GetKeyFlagsForModule(_GetMainModule()) + + # Sort flags by declaring module name and next by flag name. + flags_by_module = self.FlagsByModuleDict() + all_module_names = list(flags_by_module.keys()) + all_module_names.sort() + for module_name in all_module_names: + flag_list = [(f.name, f) for f in flags_by_module[module_name]] + flag_list.sort() + for unused_flag_name, flag in flag_list: + is_key = flag in key_flags + flag.WriteInfoInXMLFormat(outfile, module_name, + is_key=is_key, indent=indent) + + outfile.write('\n') + outfile.flush() + + def AddValidator(self, validator): + """Register new flags validator to be checked. + + Args: + validator: gflags_validators.Validator + Raises: + AttributeError: if validators work with a non-existing flag. + """ + for flag_name in validator.GetFlagsNames(): + flag = self.FlagDict()[flag_name] + flag.validators.append(validator) + +# end of FlagValues definition + + +# The global FlagValues instance +FLAGS = FlagValues() + + +def _StrOrUnicode(value): + """Converts value to a python string or, if necessary, unicode-string.""" + try: + return str(value) + except UnicodeEncodeError: + return unicode(value) + + +def _MakeXMLSafe(s): + """Escapes <, >, and & from s, and removes XML 1.0-illegal chars.""" + s = cgi.escape(s) # Escape <, >, and & + # Remove characters that cannot appear in an XML 1.0 document + # (http://www.w3.org/TR/REC-xml/#charsets). + # + # NOTE: if there are problems with current solution, one may move to + # XML 1.1, which allows such chars, if they're entity-escaped (&#xHH;). + s = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', s) + # Convert non-ascii characters to entities. Note: requires python >=2.3 + s = s.encode('ascii', 'xmlcharrefreplace') # u'\xce\x88' -> 'uΈ' + return s + + +def _WriteSimpleXMLElement(outfile, name, value, indent): + """Writes a simple XML element. + + Args: + outfile: File object we write the XML element to. + name: A string, the name of XML element. + value: A Python object, whose string representation will be used + as the value of the XML element. + indent: A string, prepended to each line of generated output. + """ + value_str = _StrOrUnicode(value) + if isinstance(value, bool): + # Display boolean values as the C++ flag library does: no caps. + value_str = value_str.lower() + safe_value_str = _MakeXMLSafe(value_str) + outfile.write('%s<%s>%s\n' % (indent, name, safe_value_str, name)) + + +class Flag: + """Information about a command-line 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 attribute 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 attribute. This enables other python modules to + safely use flags even if the __main__ module neglects to parse the + command line arguments. The .present attribute is cleared after + __init__ parsing. If the default value is set to None, then the + __init__ parsing step is skipped and the .value attribute 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 + + 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 + self.validators = [] + + self.SetDefault(default) + + def __hash__(self): + return hash(id(self)) + + def __eq__(self, other): + return self is other + + def __lt__(self, other): + if isinstance(other, Flag): + return id(self) < id(other) + return NotImplemented + + def __GetParsedValueAsString(self, value): + if value is None: + return None + if self.serializer: + return repr(self.serializer.Serialize(value)) + if self.boolean: + if value: + return repr('true') + else: + return repr('false') + return repr(_StrOrUnicode(value)) + + def Parse(self, argument): + try: + self.value = self.parser.Parse(argument) + except ValueError as e: # recast ValueError as IllegalFlagValue + raise IllegalFlagValue("flag --%s=%s: %s" % (self.name, argument, e)) + self.present += 1 + + def Unparse(self): + if self.default is None: + self.value = None + else: + 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): + """Changes the default value (and current value too) for this Flag.""" + # 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. + # TODO(olexiy): Users can directly call this method, bypassing all flags + # validators (we don't have FlagValues here, so we can not check + # validators). + # The simplest solution I see is to make this method private. + # Another approach would be to store reference to the corresponding + # FlagValues with each flag, but this seems to be an overkill. + if value is None and self.allow_override: + raise DuplicateFlagCannotPropagateNoneToSwig(self.name) + + self.default = value + self.Unparse() + self.default_as_str = self.__GetParsedValueAsString(self.value) + + def Type(self): + """Returns: a string that describes the type of this Flag.""" + # NOTE: we use strings, and not the types.*Type constants because + # our flags can have more exotic types, e.g., 'comma separated list + # of strings', 'whitespace separated list of strings', etc. + return self.parser.Type() + + def WriteInfoInXMLFormat(self, outfile, module_name, is_key=False, indent=''): + """Writes common info about this flag, in XML format. + + This is information that is relevant to all flags (e.g., name, + meaning, etc.). If you defined a flag that has some other pieces of + info, then please override _WriteCustomInfoInXMLFormat. + + Please do NOT override this method. + + Args: + outfile: File object we write to. + module_name: A string, the name of the module that defines this flag. + is_key: A boolean, True iff this flag is key for main module. + indent: A string that is prepended to each generated line. + """ + outfile.write(indent + '\n') + inner_indent = indent + ' ' + if is_key: + _WriteSimpleXMLElement(outfile, 'key', 'yes', inner_indent) + _WriteSimpleXMLElement(outfile, 'file', module_name, inner_indent) + # Print flag features that are relevant for all flags. + _WriteSimpleXMLElement(outfile, 'name', self.name, inner_indent) + if self.short_name: + _WriteSimpleXMLElement(outfile, 'short_name', self.short_name, + inner_indent) + if self.help: + _WriteSimpleXMLElement(outfile, 'meaning', self.help, inner_indent) + # The default flag value can either be represented as a string like on the + # command line, or as a Python object. We serialize this value in the + # latter case in order to remain consistent. + if self.serializer and not isinstance(self.default, str): + default_serialized = self.serializer.Serialize(self.default) + else: + default_serialized = self.default + _WriteSimpleXMLElement(outfile, 'default', default_serialized, inner_indent) + _WriteSimpleXMLElement(outfile, 'current', self.value, inner_indent) + _WriteSimpleXMLElement(outfile, 'type', self.Type(), inner_indent) + # Print extra flag features this flag may have. + self._WriteCustomInfoInXMLFormat(outfile, inner_indent) + outfile.write(indent + '\n') + + def _WriteCustomInfoInXMLFormat(self, outfile, indent): + """Writes extra info about this flag, in XML format. + + "Extra" means "not already printed by WriteInfoInXMLFormat above." + + Args: + outfile: File object we write to. + indent: A string that is prepended to each generated line. + """ + # Usually, the parser knows the extra details about the flag, so + # we just forward the call to it. + self.parser.WriteCustomInfoInXMLFormat(outfile, indent) +# End of Flag definition + + +class _ArgumentParserCache(type): + """Metaclass used to cache and share argument parsers among flags.""" + + _instances = {} + + def __call__(mcs, *args, **kwargs): + """Returns an instance of the argument parser cls. + + This method overrides behavior of the __new__ methods in + all subclasses of ArgumentParser (inclusive). If an instance + for mcs with the same set of arguments exists, this instance is + returned, otherwise a new instance is created. + + If any keyword arguments are defined, or the values in args + are not hashable, this method always returns a new instance of + cls. + + Args: + args: Positional initializer arguments. + kwargs: Initializer keyword arguments. + + Returns: + An instance of cls, shared or new. + """ + if kwargs: + return type.__call__(mcs, *args, **kwargs) + else: + instances = mcs._instances + key = (mcs,) + tuple(args) + try: + return instances[key] + except KeyError: + # No cache entry for key exists, create a new one. + return instances.setdefault(key, type.__call__(mcs, *args)) + except TypeError: + # An object in args cannot be hashed, always return + # a new instance. + return type.__call__(mcs, *args) + + +class ArgumentParser(object): + """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. + + Argument parser classes must be stateless, since instances are cached + and shared between flags. Initializer arguments are allowed, but all + member variables must be derived from initializer arguments only. + """ + __metaclass__ = _ArgumentParserCache + + syntactic_help = "" + + def Parse(self, argument): + """Default implementation: always returns its argument unmodified.""" + return argument + + def Type(self): + return 'string' + + def WriteCustomInfoInXMLFormat(self, outfile, indent): + pass + + +class ArgumentSerializer: + """Base class for generating string representations of a flag value.""" + + def Serialize(self, value): + return _StrOrUnicode(value) + + +class ListSerializer(ArgumentSerializer): + + def __init__(self, list_sep): + self.list_sep = list_sep + + def Serialize(self, value): + return self.list_sep.join([_StrOrUnicode(x) for x in value]) + + +# Flags validators + + +def RegisterValidator(flag_name, + checker, + message='Flag validation failed', + flag_values=FLAGS): + """Adds a constraint, which will be enforced during program execution. + + The constraint is validated when flags are initially parsed, and after each + change of the corresponding flag's value. + Args: + flag_name: string, name of the flag to be checked. + checker: method to validate the flag. + input - value of the corresponding flag (string, boolean, etc. + This value will be passed to checker by the library). See file's + docstring for examples. + output - Boolean. + Must return True if validator constraint is satisfied. + If constraint is not satisfied, it should either return False or + raise gflags_validators.Error(desired_error_message). + message: error text to be shown to the user if checker returns False. + If checker raises gflags_validators.Error, message from the raised + Error will be shown. + flag_values: FlagValues + Raises: + AttributeError: if flag_name is not registered as a valid flag name. + """ + flag_values.AddValidator(gflags_validators.SimpleValidator(flag_name, + checker, + message)) + + +def MarkFlagAsRequired(flag_name, flag_values=FLAGS): + """Ensure that flag is not None during program execution. + + Registers a flag validator, which will follow usual validator + rules. + Args: + flag_name: string, name of the flag + flag_values: FlagValues + Raises: + AttributeError: if flag_name is not registered as a valid flag name. + """ + RegisterValidator(flag_name, + lambda value: value is not None, + message='Flag --%s must be specified.' % flag_name, + flag_values=flag_values) + + +def _RegisterBoundsValidatorIfNeeded(parser, name, flag_values): + """Enforce lower and upper bounds for numeric flags. + + Args: + parser: NumericParser (either FloatParser or IntegerParser). Provides lower + and upper bounds, and help text to display. + name: string, name of the flag + flag_values: FlagValues + """ + if parser.lower_bound is not None or parser.upper_bound is not None: + + def Checker(value): + if value is not None and parser.IsOutsideBounds(value): + message = '%s is not %s' % (value, parser.syntactic_help) + raise gflags_validators.Error(message) + return True + + RegisterValidator(name, + Checker, + flag_values=flag_values) + + +# The DEFINE functions are explained in mode details in the module doc string. + + +def DEFINE(parser, name, default, help, flag_values=FLAGS, serializer=None, + **args): + """Registers a generic Flag object. + + NOTE: in the docstrings of all DEFINE* functions, "registers" is short + for "creates a new flag and registers it". + + Auxiliary function: clients should use the specialized DEFINE_ + function instead. + + Args: + parser: ArgumentParser that is used to parse the flag arguments. + name: A string, the flag name. + default: The default value of the flag. + help: A help string. + flag_values: FlagValues object the flag will be registered with. + serializer: ArgumentSerializer that serializes the flag value. + args: Dictionary with extra keyword args that are passes to the + Flag __init__. + """ + DEFINE_flag(Flag(parser, serializer, name, default, help, **args), + flag_values) + + +def DEFINE_flag(flag, flag_values=FLAGS): + """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 DEFINE_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 + # Tell flag_values who's defining the flag. + if isinstance(flag_values, FlagValues): + # Regarding the above isinstance test: some users pass funny + # values of flag_values (e.g., {}) in order to avoid the flag + # registration (in the past, there used to be a flag_values == + # FLAGS test here) and redefine flags with the same name (e.g., + # debug). To avoid breaking their code, we perform the + # registration only if flag_values is a real FlagValues object. + module, module_name = _GetCallingModuleObjectAndName() + flag_values._RegisterFlagByModule(module_name, flag) + flag_values._RegisterFlagByModuleId(id(module), flag) + + +def _InternalDeclareKeyFlags(flag_names, + flag_values=FLAGS, key_flag_values=None): + """Declares a flag as key for the calling module. + + Internal function. User code should call DECLARE_key_flag or + ADOPT_module_key_flags instead. + + Args: + flag_names: A list of strings that are names of already-registered + Flag objects. + flag_values: A FlagValues object that the flags listed in + flag_names have registered with (the value of the flag_values + argument from the DEFINE_* calls that defined those flags). + This should almost never need to be overridden. + key_flag_values: A FlagValues object that (among possibly many + other things) keeps track of the key flags for each module. + Default None means "same as flag_values". This should almost + never need to be overridden. + + Raises: + UnrecognizedFlagError: when we refer to a flag that was not + defined yet. + """ + key_flag_values = key_flag_values or flag_values + + module = _GetCallingModule() + + for flag_name in flag_names: + if flag_name not in flag_values: + raise UnrecognizedFlagError(flag_name) + flag = flag_values.FlagDict()[flag_name] + key_flag_values._RegisterKeyFlagForModule(module, flag) + + +def DECLARE_key_flag(flag_name, flag_values=FLAGS): + """Declares one flag as key to the current module. + + Key flags are flags that are deemed really important for a module. + They are important when listing help messages; e.g., if the + --helpshort command-line flag is used, then only the key flags of the + main module are listed (instead of all flags, as in the case of + --help). + + Sample usage: + + gflags.DECLARED_key_flag('flag_1') + + Args: + flag_name: A string, the name of an already declared flag. + (Redeclaring flags as key, including flags implicitly key + because they were declared in this module, is a no-op.) + flag_values: A FlagValues object. This should almost never + need to be overridden. + """ + if flag_name in _SPECIAL_FLAGS: + # Take care of the special flags, e.g., --flagfile, --undefok. + # These flags are defined in _SPECIAL_FLAGS, and are treated + # specially during flag parsing, taking precedence over the + # user-defined flags. + _InternalDeclareKeyFlags([flag_name], + flag_values=_SPECIAL_FLAGS, + key_flag_values=flag_values) + return + _InternalDeclareKeyFlags([flag_name], flag_values=flag_values) + + +def ADOPT_module_key_flags(module, flag_values=FLAGS): + """Declares that all flags key to a module are key to the current module. + + Args: + module: A module object. + flag_values: A FlagValues object. This should almost never need + to be overridden. + + Raises: + FlagsError: When given an argument that is a module name (a + string), instead of a module object. + """ + # NOTE(salcianu): an even better test would be if not + # isinstance(module, types.ModuleType) but I didn't want to import + # types for such a tiny use. + if isinstance(module, str): + raise FlagsError('Received module name %s; expected a module object.' + % module) + _InternalDeclareKeyFlags( + [f.name for f in flag_values._GetKeyFlagsForModule(module.__name__)], + flag_values=flag_values) + # If module is this flag module, take _SPECIAL_FLAGS into account. + if module == _GetThisModuleObjectAndName()[0]: + _InternalDeclareKeyFlags( + # As we associate flags with _GetCallingModuleObjectAndName(), the + # special flags defined in this module are incorrectly registered with + # a different module. So, we can't use _GetKeyFlagsForModule. + # Instead, we take all flags from _SPECIAL_FLAGS (a private + # FlagValues, where no other module should register flags). + [f.name for f in _SPECIAL_FLAGS.FlagDict().values()], + flag_values=_SPECIAL_FLAGS, + key_flag_values=flag_values) + + +# +# STRING FLAGS +# + + +def DEFINE_string(name, default, help, flag_values=FLAGS, **args): + """Registers a flag whose value can be any string.""" + parser = ArgumentParser() + serializer = ArgumentSerializer() + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +# +# BOOLEAN FLAGS +# + + +class BooleanParser(ArgumentParser): + """Parser of boolean values.""" + + def Convert(self, argument): + """Converts the argument to a boolean; raise ValueError on errors.""" + if type(argument) == str: + if argument.lower() in ['true', 't', '1']: + return True + elif argument.lower() in ['false', 'f', '0']: + return False + + bool_argument = bool(argument) + if argument == bool_argument: + # The argument is a valid boolean (True, False, 0, or 1), and not just + # something that always converts to bool (list, string, int, etc.). + return bool_argument + + raise ValueError('Non-boolean argument to boolean flag', argument) + + def Parse(self, argument): + val = self.Convert(argument) + return val + + def Type(self): + return 'bool' + + +class BooleanFlag(Flag): + """Basic boolean flag. + + Boolean flags do not take any arguments, and their value is either + True (1) or False (0). The false value is specified on the command + line by prepending the word 'no' to either the long or the 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() + Flag.__init__(self, p, None, 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): + """Registers a boolean flag. + + Such a boolean flag 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, True or False. 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) + + +# Match C++ API to unconfuse C++ people. +DEFINE_bool = DEFINE_boolean + + +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 HelpXMLFlag(BooleanFlag): + """Similar to HelpFlag, but generates output in XML format.""" + def __init__(self): + BooleanFlag.__init__(self, 'helpxml', False, + 'like --help, but generates XML output', + allow_override=1) + def Parse(self, arg): + if arg: + FLAGS.WriteHelpInXMLFormat(sys.stdout) + 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) + +# +# Numeric parser - base class for Integer and Float parsers +# + + +class NumericParser(ArgumentParser): + """Parser of numeric values. + + Parsed value may be bounded to a given upper and lower bound. + """ + + def IsOutsideBounds(self, val): + return ((self.lower_bound is not None and val < self.lower_bound) or + (self.upper_bound is not None and val > self.upper_bound)) + + def Parse(self, argument): + val = self.Convert(argument) + if self.IsOutsideBounds(val): + raise ValueError("%s is not %s" % (val, self.syntactic_help)) + return val + + def WriteCustomInfoInXMLFormat(self, outfile, indent): + if self.lower_bound is not None: + _WriteSimpleXMLElement(outfile, 'lower_bound', self.lower_bound, indent) + if self.upper_bound is not None: + _WriteSimpleXMLElement(outfile, 'upper_bound', self.upper_bound, indent) + + def Convert(self, argument): + """Default implementation: always returns its argument unmodified.""" + return argument + +# End of Numeric Parser + +# +# FLOAT FLAGS +# + + +class FloatParser(NumericParser): + """Parser of floating point values. + + Parsed value may be 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): + super(FloatParser, self).__init__() + self.lower_bound = lower_bound + self.upper_bound = upper_bound + sh = self.syntactic_help + if lower_bound is not None and upper_bound is not None: + sh = ("%s in the range [%s, %s]" % (sh, lower_bound, upper_bound)) + elif lower_bound == 0: + sh = "a non-negative %s" % self.number_name + elif upper_bound == 0: + sh = "a non-positive %s" % self.number_name + elif upper_bound is not None: + sh = "%s <= %s" % (self.number_name, upper_bound) + elif lower_bound is not None: + sh = "%s >= %s" % (self.number_name, lower_bound) + self.syntactic_help = sh + + def Convert(self, argument): + """Converts argument to a float; raises ValueError on errors.""" + return float(argument) + + def Type(self): + return 'float' +# End of FloatParser + + +def DEFINE_float(name, default, help, lower_bound=None, upper_bound=None, + flag_values=FLAGS, **args): + """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) + _RegisterBoundsValidatorIfNeeded(parser, name, flag_values=flag_values) + +# +# INTEGER FLAGS +# + + +class IntegerParser(NumericParser): + """Parser of an integer value. + + Parsed value may be bounded to a given upper and lower bound. + """ + number_article = "an" + number_name = "integer" + syntactic_help = " ".join((number_article, number_name)) + + def __init__(self, lower_bound=None, upper_bound=None): + super(IntegerParser, self).__init__() + self.lower_bound = lower_bound + self.upper_bound = upper_bound + sh = self.syntactic_help + if lower_bound is not None and upper_bound is not 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 == 0: + sh = "a non-positive %s" % self.number_name + elif upper_bound is not None: + sh = "%s <= %s" % (self.number_name, upper_bound) + elif lower_bound is not None: + sh = "%s >= %s" % (self.number_name, lower_bound) + self.syntactic_help = sh + + 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 + return int(argument, base) + else: + return int(argument) + + def Type(self): + return 'int' + + +def DEFINE_integer(name, default, help, lower_bound=None, upper_bound=None, + flag_values=FLAGS, **args): + """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) + _RegisterBoundsValidatorIfNeeded(parser, name, flag_values=flag_values) + + +# +# ENUM FLAGS +# + + +class EnumParser(ArgumentParser): + """Parser of a string enum value (a string value from a given set). + + If enum_values (see below) is not specified, any string is allowed. + """ + + def __init__(self, enum_values=None): + super(EnumParser, self).__init__() + self.enum_values = enum_values + + def Parse(self, argument): + 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 + + def Type(self): + return 'string enum' + + +class EnumFlag(Flag): + """Basic enum flag; its value can be any string from list of enum_values.""" + + def __init__(self, name, default, help, enum_values=None, + short_name=None, **args): + enum_values = enum_values or [] + 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 _WriteCustomInfoInXMLFormat(self, outfile, indent): + for enum_value in self.parser.enum_values: + _WriteSimpleXMLElement(outfile, 'enum_value', enum_value, indent) + + +def DEFINE_enum(name, default, enum_values, help, flag_values=FLAGS, + **args): + """Registers a flag whose value can be any string from enum_values.""" + DEFINE_flag(EnumFlag(name, default, help, enum_values, ** args), + flag_values) + + +# +# LIST FLAGS +# + + +class BaseListParser(ArgumentParser): + """Base class for a parser of lists of strings. + + To extend, inherit from this class; from the subclass __init__, 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 + super(BaseListParser, self).__init__() + self._token = token + self._name = name + self.syntactic_help = "a %s separated list" % self._name + + def Parse(self, argument): + if isinstance(argument, list): + return argument + elif argument == '': + return [] + else: + return [s.strip() for s in argument.split(self._token)] + + def Type(self): + return '%s separated list of strings' % self._name + + +class ListParser(BaseListParser): + """Parser for a comma-separated list of strings.""" + + def __init__(self): + BaseListParser.__init__(self, ',', 'comma') + + def WriteCustomInfoInXMLFormat(self, outfile, indent): + BaseListParser.WriteCustomInfoInXMLFormat(self, outfile, indent) + _WriteSimpleXMLElement(outfile, 'list_separator', repr(','), indent) + + +class WhitespaceSeparatedListParser(BaseListParser): + """Parser for a whitespace-separated list of strings.""" + + def __init__(self): + BaseListParser.__init__(self, None, 'whitespace') + + def WriteCustomInfoInXMLFormat(self, outfile, indent): + BaseListParser.WriteCustomInfoInXMLFormat(self, outfile, indent) + separators = list(string.whitespace) + separators.sort() + for ws_char in string.whitespace: + _WriteSimpleXMLElement(outfile, 'list_separator', repr(ws_char), indent) + + +def DEFINE_list(name, default, help, flag_values=FLAGS, **args): + """Registers a flag whose value is a comma-separated list of strings.""" + parser = ListParser() + serializer = ListSerializer(',') + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +def DEFINE_spaceseplist(name, default, help, flag_values=FLAGS, **args): + """Registers a flag whose value is a whitespace-separated list of strings. + + Any whitespace can be used as a separator. + """ + parser = WhitespaceSeparatedListParser() + serializer = ListSerializer(' ') + DEFINE(parser, name, default, help, flag_values, serializer, **args) + + +# +# MULTI FLAGS +# + + +class MultiFlag(Flag): + """A flag that can appear multiple time on the command-line. + + The value of such a flag is a list that contains the individual values + from all the appearances of that flag on the command-line. + + See the __doc__ for Flag for most behavior of this class. Only + differences in behavior are described here: + + * The default value may be either a single value or a list of values. + A single value is interpreted as the [value] singleton list. + + * 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 += ';\n repeat this option to specify a list of values' + + def Parse(self, arguments): + """Parses one or more arguments with the installed parser. + + Args: + arguments: a single argument or a list of arguments (typically a + list of default values); a single argument is 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 the 'value' attribute + 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 Type(self): + return 'multi ' + self.parser.Type() + + +def DEFINE_multi(parser, serializer, name, default, help, flag_values=FLAGS, + **args): + """Registers a generic MultiFlag that parses its args with a given parser. + + Auxiliary function. Normal users should NOT use it directly. + + 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): + """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): + """Registers a flag whose value can be a list of arbitrary 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) + + +def DEFINE_multi_float(name, default, help, lower_bound=None, upper_bound=None, + flag_values=FLAGS, **args): + """Registers a flag whose value can be a list of arbitrary floats. + + Use the flag on the command line multiple times to place multiple + float values into the list. The 'default' may be a single float + (which will be converted into a single-element list) or a list of + floats. + """ + parser = FloatParser(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()) +DEFINE_flag(HelpXMLFlag()) + +# Define special flags here so that help may be generated for them. +# NOTE: Please do NOT use _SPECIAL_FLAGS from outside this module. +_SPECIAL_FLAGS = FlagValues() + + +DEFINE_string( + 'flagfile', "", + "Insert flag definitions from the given file into the command line.", + _SPECIAL_FLAGS) + +DEFINE_string( + 'undefok', "", + "comma-separated list of flag names that it is okay to specify " + "on the command line even if the program does not define a flag " + "with that name. IMPORTANT: flags in this list that have " + "arguments MUST use the --flag=value format.", _SPECIAL_FLAGS) diff --git a/third_party/py/gflags/debian/rules b/third_party/py/gflags/debian/rules old mode 100644 new mode 100755 diff --git a/third_party/py/gflags/gflags/__init__.py b/third_party/py/gflags/gflags/__init__.py deleted file mode 100644 index b0025eaa39..0000000000 --- a/third_party/py/gflags/gflags/__init__.py +++ /dev/null @@ -1,891 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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 -# Contributions by: -# Amit Patel, Bogdan Cocosel, Daniel Dulitz, Eric Tiedemann, -# Eric Veach, Laurence Gonsalves, Matthew Springer, Craig Silverstein, -# Vladimir Rusinov - -"""This package is used to define and parse command line flags. - -This package 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. -""" - -import getopt -import os -import re -import sys -import types -import warnings - -import six - -import _helpers -import argument_parser -import exceptions -# _flag alias is to avoid 'redefined outer name' warnings. -import flag as _flag -import flagvalues -import validators as gflags_validators - - -# Add current module to disclaimed module ids. -_helpers.disclaim_module_ids.add(id(sys.modules[__name__])) - -# Function/class aliases. Lint complains about invalid-name for many of them, -# suppress warning for whole block: -# pylint: disable=invalid-name - -# Module exceptions: -# TODO(vrusinov): these should all be renamed to *Error, e.g. IllegalFlagValue -# should be removed in favour of IllegalFlagValueError. -FlagsError = exceptions.Error -Error = exceptions.Error -CantOpenFlagFileError = exceptions.CantOpenFlagFileError -DuplicateFlagError = exceptions.DuplicateFlagError -DuplicateFlagCannotPropagateNoneToSwig = ( - exceptions.DuplicateFlagCannotPropagateNoneToSwig) -IllegalFlagValue = exceptions.IllegalFlagValueError -IllegalFlagValueError = exceptions.IllegalFlagValueError -UnrecognizedFlagError = exceptions.UnrecognizedFlagError -ValidationError = exceptions.ValidationError - -# Public functions: -GetHelpWidth = _helpers.GetHelpWidth -TextWrap = _helpers.TextWrap -FlagDictToArgs = _helpers.FlagDictToArgs -DocToHelp = _helpers.DocToHelp - -# Public classes: -Flag = _flag.Flag -BooleanFlag = _flag.BooleanFlag -EnumFlag = _flag.EnumFlag -MultiFlag = _flag.MultiFlag - -FlagValues = flagvalues.FlagValues -ArgumentParser = argument_parser.ArgumentParser -BooleanParser = argument_parser.BooleanParser -EnumParser = argument_parser.EnumParser -ArgumentSerializer = argument_parser.ArgumentSerializer -FloatParser = argument_parser.FloatParser -IntegerParser = argument_parser.IntegerParser -BaseListParser = argument_parser.BaseListParser -ListParser = argument_parser.ListParser -ListSerializer = argument_parser.ListSerializer -CsvListSerializer = argument_parser.CsvListSerializer -WhitespaceSeparatedListParser = argument_parser.WhitespaceSeparatedListParser - -# pylint: enable=invalid-name - - - - -# The global FlagValues instance -FLAGS = FlagValues() - - -# Flags validators - - -def register_validator(flag_name, - checker, - message='Flag validation failed', - flag_values=FLAGS): - """Adds a constraint, which will be enforced during program execution. - - The constraint is validated when flags are initially parsed, and after each - change of the corresponding flag's value. - Args: - flag_name: str, Name of the flag to be checked. - checker: callable, A function to validate the flag. - input - A single positional argument: The value of the corresponding - flag (string, boolean, etc. This value will be passed to checker - by the library). - output - Boolean. - Must return True if validator constraint is satisfied. - If constraint is not satisfied, it should either return False or - raise gflags.ValidationError(desired_error_message). - message: Error text to be shown to the user if checker returns False. - If checker raises gflags.ValidationError, message from the raised - Error will be shown. - flag_values: An optional FlagValues instance to validate against. - Raises: - AttributeError: If flag_name is not registered as a valid flag name. - """ - v = gflags_validators.SingleFlagValidator(flag_name, checker, message) - _add_validator(flag_values, v) - - -def validator(flag_name, message='Flag validation failed', flag_values=FLAGS): - """A function decorator for defining a flag validator. - - Registers the decorated function as a validator for flag_name, e.g. - - @gflags.validator('foo') - def _CheckFoo(foo): - ... - - See register_validator() for the specification of checker function. - - Args: - flag_name: string, name of the flag to be checked. - message: error text to be shown to the user if checker returns False. - If checker raises gflags.ValidationError, message from the raised - Error will be shown. - flag_values: FlagValues - Returns: - A function decorator that registers its function argument as a validator. - Raises: - AttributeError: if flag_name is not registered as a valid flag name. - """ - - def decorate(function): - register_validator(flag_name, function, - message=message, - flag_values=flag_values) - return function - return decorate - - -def register_multi_flags_validator(flag_names, - multi_flags_checker, - message='Flags validation failed', - flag_values=FLAGS): - """Adds a constraint to multiple flags. - - The constraint is validated when flags are initially parsed, and after each - change of the corresponding flag's value. - - Args: - flag_names: [str], a list of the flag names to be checked. - multi_flags_checker: callable, a function to validate the flag. - input - dictionary, with keys() being flag_names, and value for each key - being the value of the corresponding flag (string, boolean, etc). - output - Boolean. Must return True if validator constraint is satisfied. - If constraint is not satisfied, it should either return False or - raise gflags.ValidationError. - message: Error text to be shown to the user if checker returns False. - If checker raises gflags.ValidationError, message from the raised error - will be shown. - flag_values: An optional FlagValues instance to validate against. - - Raises: - AttributeError: If a flag is not registered as a valid flag name. - """ - v = gflags_validators.MultiFlagsValidator( - flag_names, multi_flags_checker, message) - _add_validator(flag_values, v) - - -def multi_flags_validator(flag_names, - message='Flag validation failed', - flag_values=FLAGS): - """A function decorator for defining a multi-flag validator. - - Registers the decorated function as a validator for flag_names, e.g. - - @gflags.multi_flags_validator(['foo', 'bar']) - def _CheckFooBar(flags_dict): - ... - - See register_multi_flags_validator() for the specification of checker - function. - - Args: - flag_names: [str], a list of the flag names to be checked. - message: error text to be shown to the user if checker returns False. - If checker raises ValidationError, message from the raised - error will be shown. - flag_values: An optional FlagValues instance to validate against. - - Returns: - A function decorator that registers its function argument as a validator. - - Raises: - AttributeError: If a flag is not registered as a valid flag name. - """ - - def decorate(function): - register_multi_flags_validator(flag_names, - function, - message=message, - flag_values=flag_values) - return function - - return decorate - - -def mark_flag_as_required(flag_name, flag_values=FLAGS): - """Ensures that flag is not None during program execution. - - Registers a flag validator, which will follow usual validator rules. - Important note: validator will pass for any non-None value, such as False, - 0 (zero), '' (empty string) and so on. - - It is recommended to call this method like this: - - if __name__ == '__main__': - gflags.mark_flag_as_required('your_flag_name') - app.run() - - Because validation happens at app.run() we want to ensure required-ness - is enforced at that time. However, you generally do not want to force - users who import your code to have additional required flags for their - own binaries or tests. - - Args: - flag_name: string, name of the flag - flag_values: FlagValues - Raises: - AttributeError: if flag_name is not registered as a valid flag name. - """ - if flag_values[flag_name].default is not None: - # TODO(vrusinov): Turn this warning into an exception. - warnings.warn( - 'Flag %s has a non-None default value; therefore, ' - 'mark_flag_as_required will pass even if flag is not specified in the ' - 'command line!' % flag_name) - register_validator(flag_name, - lambda value: value is not None, - message='Flag --%s must be specified.' % flag_name, - flag_values=flag_values) - - -def mark_flags_as_required(flag_names, flag_values=FLAGS): - """Ensures that flags are not None during program execution. - - Recommended usage: - - if __name__ == '__main__': - gflags.mark_flags_as_required(['flag1', 'flag2', 'flag3']) - app.run() - - Args: - flag_names: list/tuple, names of the flags. - flag_values: FlagValues - Raises: - AttributeError: If any of flag name has not already been defined as a flag. - """ - for flag_name in flag_names: - mark_flag_as_required(flag_name, flag_values) - - -def mark_flags_as_mutual_exclusive(flag_names, required=False, - flag_values=FLAGS): - """Ensures that only one flag among flag_names is set. - - Args: - flag_names: [str], a list of the flag names to be checked. - required: Boolean, if set, exactly one of the flags must be set. - Otherwise, it is also valid for none of the flags to be set. - flag_values: An optional FlagValues instance to validate against. - """ - - def validate_mutual_exclusion(flags_dict): - flag_count = sum(1 for val in flags_dict.values() if val is not None) - if flag_count == 1 or (not required and flag_count == 0): - return True - message = ('%s one of (%s) must be specified.' % - ('Exactly' if required else 'At most', ', '.join(flag_names))) - raise ValidationError(message) - - register_multi_flags_validator( - flag_names, validate_mutual_exclusion, flag_values=flag_values) - - -def _add_validator(fv, validator_instance): - """Register new flags validator to be checked. - - Args: - fv: flagvalues.FlagValues - validator_instance: validators.Validator - Raises: - KeyError: if validators work with a non-existing flag. - """ - for flag_name in validator_instance.get_flags_names(): - fv[flag_name].validators.append(validator_instance) - - -def _register_bounds_validator_if_needed(parser, name, flag_values): - """Enforces lower and upper bounds for numeric flags. - - Args: - parser: NumericParser (either FloatParser or IntegerParser). Provides lower - and upper bounds, and help text to display. - name: string, name of the flag - flag_values: FlagValues - """ - if parser.lower_bound is not None or parser.upper_bound is not None: - - def checker(value): - if value is not None and parser.is_outside_bounds(value): - message = '%s is not %s' % (value, parser.syntactic_help) - raise ValidationError(message) - return True - - register_validator(name, checker, flag_values=flag_values) - - -# The DEFINE functions are explained in more details in the module doc string. - - -def DEFINE(parser, name, default, help, flag_values=FLAGS, serializer=None, # pylint: disable=redefined-builtin,invalid-name - module_name=None, **args): - """Registers a generic Flag object. - - NOTE: in the docstrings of all DEFINE* functions, "registers" is short - for "creates a new flag and registers it". - - Auxiliary function: clients should use the specialized DEFINE_ - function instead. - - Args: - parser: ArgumentParser that is used to parse the flag arguments. - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - serializer: ArgumentSerializer that serializes the flag value. - module_name: A string, the name of the Python module declaring this flag. - If not provided, it will be computed using the stack trace of this call. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - DEFINE_flag(Flag(parser, serializer, name, default, help, **args), - flag_values, module_name) - - -def DEFINE_flag(flag, flag_values=FLAGS, module_name=None): # pylint: disable=g-bad-name - """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 DEFINE_integer. But developers - who need to create Flag objects themselves should use this function - to register their flags. - - Args: - flag: A Flag object, a flag that is key to the module. - flag_values: FlagValues object with which the flag will be registered. - module_name: A string, the name of the Python module declaring this flag. - If not provided, it will be computed using the stack trace of this call. - """ - # copying the reference to flag_values prevents pychecker warnings - fv = flag_values - fv[flag.name] = flag - # Tell flag_values who's defining the flag. - if isinstance(flag_values, FlagValues): - # Regarding the above isinstance test: some users pass funny - # values of flag_values (e.g., {}) in order to avoid the flag - # registration (in the past, there used to be a flag_values == - # FLAGS test here) and redefine flags with the same name (e.g., - # debug). To avoid breaking their code, we perform the - # registration only if flag_values is a real FlagValues object. - if module_name: - module = sys.modules.get(module_name) - else: - module, module_name = _helpers.GetCallingModuleObjectAndName() - # TODO(vrusinov): _RegisterFlagByModule* should be public. - # pylint: disable=protected-access - flag_values._RegisterFlagByModule(module_name, flag) - flag_values._RegisterFlagByModuleId(id(module), flag) - # pylint: enable=protected-access - - -def _internal_declare_key_flags(flag_names, - flag_values=FLAGS, key_flag_values=None): - """Declares a flag as key for the calling module. - - Internal function. User code should call DECLARE_key_flag or - ADOPT_module_key_flags instead. - - Args: - flag_names: A list of strings that are names of already-registered - Flag objects. - flag_values: A FlagValues object that the flags listed in - flag_names have registered with (the value of the flag_values - argument from the DEFINE_* calls that defined those flags). - This should almost never need to be overridden. - key_flag_values: A FlagValues object that (among possibly many - other things) keeps track of the key flags for each module. - Default None means "same as flag_values". This should almost - never need to be overridden. - - Raises: - UnrecognizedFlagError: when we refer to a flag that was not - defined yet. - """ - key_flag_values = key_flag_values or flag_values - - module = _helpers.GetCallingModule() - - for flag_name in flag_names: - flag = flag_values.GetFlag(flag_name) - # TODO(vrusinov): _RegisterKeyFlagForModule should be public. - key_flag_values._RegisterKeyFlagForModule(module, flag) # pylint: disable=protected-access - - -def DECLARE_key_flag( # pylint: disable=g-bad-name - flag_name, flag_values=FLAGS): - """Declares one flag as key to the current module. - - Key flags are flags that are deemed really important for a module. - They are important when listing help messages; e.g., if the - --helpshort command-line flag is used, then only the key flags of the - main module are listed (instead of all flags, as in the case of - --helpfull). - - Sample usage: - - gflags.DECLARE_key_flag('flag_1') - - Args: - flag_name: A string, the name of an already declared flag. - (Redeclaring flags as key, including flags implicitly key - because they were declared in this module, is a no-op.) - flag_values: A FlagValues object. This should almost never - need to be overridden. - """ - if flag_name in _helpers.SPECIAL_FLAGS: - # Take care of the special flags, e.g., --flagfile, --undefok. - # These flags are defined in _SPECIAL_FLAGS, and are treated - # specially during flag parsing, taking precedence over the - # user-defined flags. - _internal_declare_key_flags([flag_name], - flag_values=_helpers.SPECIAL_FLAGS, - key_flag_values=flag_values) - return - _internal_declare_key_flags([flag_name], flag_values=flag_values) - - -def ADOPT_module_key_flags( # pylint: disable=g-bad-name - module, flag_values=FLAGS): - """Declares that all flags key to a module are key to the current module. - - Args: - module: A module object. - flag_values: A FlagValues object. This should almost never need - to be overridden. - - Raises: - Error: When given an argument that is a module name (a - string), instead of a module object. - """ - if not isinstance(module, types.ModuleType): - raise Error('Expected a module object, not %r.' % (module,)) - # TODO(vrusinov): _GetKeyFlagsForModule should be public. - _internal_declare_key_flags( - [f.name for f in flag_values._GetKeyFlagsForModule(module.__name__)], # pylint: disable=protected-access - flag_values=flag_values) - # If module is this flag module, take _helpers._SPECIAL_FLAGS into account. - if module == _helpers.GetModuleObjectAndName(globals())[0]: - _internal_declare_key_flags( - # As we associate flags with _GetCallingModuleObjectAndName(), the - # special flags defined in this module are incorrectly registered with - # a different module. So, we can't use _GetKeyFlagsForModule. - # Instead, we take all flags from _helpers._SPECIAL_FLAGS (a private - # FlagValues, where no other module should register flags). - [f.name for f in six.itervalues(_helpers.SPECIAL_FLAGS.FlagDict())], - flag_values=_helpers.SPECIAL_FLAGS, - key_flag_values=flag_values) - - -def DISCLAIM_key_flags(): # pylint: disable=g-bad-name - """Declares that the current module will not define any more key flags. - - Normally, the module that calls the DEFINE_xxx functions claims the - flag to be its key flag. This is undesirable for modules that - define additional DEFINE_yyy functions with its own flag parsers and - serializers, since that module will accidentally claim flags defined - by DEFINE_yyy as its key flags. After calling this function, the - module disclaims flag definitions thereafter, so the key flags will - be correctly attributed to the caller of DEFINE_yyy. - - After calling this function, the module will not be able to define - any more flags. This function will affect all FlagValues objects. - """ - globals_for_caller = sys._getframe(1).f_globals # pylint: disable=protected-access - module, _ = _helpers.GetModuleObjectAndName(globals_for_caller) - _helpers.disclaim_module_ids.add(id(module)) - - -# -# STRING FLAGS -# - - -def DEFINE_string( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, flag_values=FLAGS, **args): - """Registers a flag whose value can be any string.""" - parser = ArgumentParser() - serializer = ArgumentSerializer() - DEFINE(parser, name, default, help, flag_values, serializer, **args) - - -def DEFINE_boolean( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, flag_values=FLAGS, module_name=None, **args): - """Registers a boolean flag. - - Such a boolean flag 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, True or False. None is possible - if default=None and the user does not specify the flag on the command - line. - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - module_name: A string, the name of the Python module declaring this flag. - If not provided, it will be computed using the stack trace of this call. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - DEFINE_flag(BooleanFlag(name, default, help, **args), - flag_values, module_name) - - -# Match C++ API to unconfuse C++ people. -DEFINE_bool = DEFINE_boolean # pylint: disable=g-bad-name - - -def DEFINE_float( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, lower_bound=None, upper_bound=None, - flag_values=FLAGS, **args): # pylint: disable=g-bad-name - """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. - - Args: - name: str, flag name. - default: float, default flag value. - help: str, help message. - lower_bound: float, min value of the flag. - upper_bound: float, max value of the flag. - flag_values: FlagValues object with which the flag will be registered. - **args: additional arguments to pass to DEFINE. - """ - parser = FloatParser(lower_bound, upper_bound) - serializer = ArgumentSerializer() - DEFINE(parser, name, default, help, flag_values, serializer, **args) - _register_bounds_validator_if_needed(parser, name, flag_values=flag_values) - - -def DEFINE_integer( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, lower_bound=None, upper_bound=None, - flag_values=FLAGS, **args): - """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. - - Args: - name: str, flag name. - default: int, default flag value. - help: str, help message. - lower_bound: int, min value of the flag. - upper_bound: int, max value of the flag. - flag_values: FlagValues object with which the flag will be registered. - **args: additional arguments to pass to DEFINE. - """ - parser = IntegerParser(lower_bound, upper_bound) - serializer = ArgumentSerializer() - DEFINE(parser, name, default, help, flag_values, serializer, **args) - _register_bounds_validator_if_needed(parser, name, flag_values=flag_values) - - -def DEFINE_enum( # pylint: disable=g-bad-name,redefined-builtin - name, default, enum_values, help, flag_values=FLAGS, module_name=None, - **args): - """Registers a flag whose value can be any string from enum_values. - - Args: - name: A string, the flag name. - default: The default value of the flag. - enum_values: A list of strings with the possible values for the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - module_name: A string, the name of the Python module declaring this flag. - If not provided, it will be computed using the stack trace of this call. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - DEFINE_flag(EnumFlag(name, default, help, enum_values, ** args), - flag_values, module_name) - - -def DEFINE_list( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, flag_values=FLAGS, **args): - """Registers a flag whose value is a comma-separated list of strings. - - The flag value is parsed with a CSV parser. - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = ListParser() - serializer = CsvListSerializer(',') - DEFINE(parser, name, default, help, flag_values, serializer, **args) - - -def DEFINE_spaceseplist( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, comma_compat=False, flag_values=FLAGS, **args): - """Registers a flag whose value is a whitespace-separated list of strings. - - Any whitespace can be used as a separator. - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - comma_compat: bool - Whether to support comma as an additional separator. - If false then only whitespace is supported. This is intended only for - backwards compatibility with flags that used to be comma-separated. - flag_values: FlagValues object with which the flag will be registered. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = WhitespaceSeparatedListParser(comma_compat=comma_compat) - serializer = ListSerializer(' ') - DEFINE(parser, name, default, help, flag_values, serializer, **args) - - -def DEFINE_multi( # pylint: disable=g-bad-name,redefined-builtin - parser, serializer, name, default, help, flag_values=FLAGS, - module_name=None, **args): - """Registers a generic MultiFlag that parses its args with a given parser. - - Auxiliary function. Normal users should NOT use it directly. - - 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. - - Args: - parser: ArgumentParser that is used to parse the flag arguments. - serializer: ArgumentSerializer that serializes the flag value. - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - module_name: A string, the name of the Python module declaring this flag. - If not provided, it will be computed using the stack trace of this call. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - DEFINE_flag(MultiFlag(parser, serializer, name, default, help, **args), - flag_values, module_name) - - -def DEFINE_multistring( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, flag_values=FLAGS, **args): - """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. - - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = ArgumentParser() - serializer = ArgumentSerializer() - DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) - - -def DEFINE_multi_int( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, lower_bound=None, upper_bound=None, - flag_values=FLAGS, **args): - """Registers a flag whose value can be a list of arbitrary 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. - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - lower_bound: int, min values of the flag. - upper_bound: int, max values of the flag. - flag_values: FlagValues object with which the flag will be registered. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = IntegerParser(lower_bound, upper_bound) - serializer = ArgumentSerializer() - DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) - - -def DEFINE_multi_float( # pylint: disable=g-bad-name,redefined-builtin - name, default, help, lower_bound=None, upper_bound=None, - flag_values=FLAGS, **args): - """Registers a flag whose value can be a list of arbitrary floats. - - Use the flag on the command line multiple times to place multiple - float values into the list. The 'default' may be a single float - (which will be converted into a single-element list) or a list of - floats. - - Args: - name: A string, the flag name. - default: The default value of the flag. - help: A help string. - lower_bound: float, min values of the flag. - upper_bound: float, max values of the flag. - flag_values: FlagValues object with which the flag will be registered. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = FloatParser(lower_bound, upper_bound) - serializer = ArgumentSerializer() - DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) - - -def DEFINE_multi_enum( # pylint: disable=g-bad-name,redefined-builtin - name, default, enum_values, help, flag_values=FLAGS, case_sensitive=True, - **args): - """Registers a flag whose value can be a list strings from enum_values. - - Use the flag on the command line multiple times to place multiple - enum 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. - - Args: - name: A string, the flag name. - default: The default value of the flag. - enum_values: A list of strings with the possible values for the flag. - help: A help string. - flag_values: FlagValues object with which the flag will be registered. - case_sensitive: Whether or not the enum is to be case-sensitive. - **args: Dictionary with extra keyword args that are passed to the - Flag __init__. - """ - parser = EnumParser(enum_values, case_sensitive) - serializer = ArgumentSerializer() - DEFINE_multi(parser, serializer, name, default, help, flag_values, **args) - - -def DEFINE_alias(name, original_name, flag_values=FLAGS, module_name=None): # pylint: disable=g-bad-name - """Defines an alias flag for an existing one. - - Args: - name: A string, name of the alias flag. - original_name: A string, name of the original flag. - flag_values: FlagValues object with which the flag will be registered. - module_name: A string, the name of the module that defines this flag. - - Raises: - gflags.FlagError: - UnrecognizedFlagError: if the referenced flag doesn't exist. - DuplicateFlagError: if the alias name has been used by some existing flag. - """ - if original_name not in flag_values: - raise UnrecognizedFlagError(original_name) - flag = flag_values[original_name] - - class _Parser(ArgumentParser): - """The parser for the alias flag calls the original flag parser.""" - - def parse(self, argument): - flag.parse(argument) - return flag.value - - class _FlagAlias(Flag): - """Overrides Flag class so alias value is copy of original flag value.""" - - @property - def value(self): - return flag.value - - @value.setter - def value(self, value): - flag.value = value - - help_msg = 'Alias for --%s.' % flag.name - # If alias_name has been used, gflags.DuplicatedFlag will be raised. - DEFINE_flag(_FlagAlias(_Parser(), flag.serializer, name, flag.default, - help_msg, boolean=flag.boolean), - flag_values, module_name) - - -DEFINE_string( - 'flagfile', '', - 'Insert flag definitions from the given file into the command line.', - _helpers.SPECIAL_FLAGS) - -DEFINE_string( - 'undefok', '', - 'comma-separated list of flag names that it is okay to specify ' - 'on the command line even if the program does not define a flag ' - 'with that name. IMPORTANT: flags in this list that have ' - 'arguments MUST use the --flag=value format.', _helpers.SPECIAL_FLAGS) - - -# Old CamelCase functions. It's OK to use them, but those use cases will be -# migrated to PEP8 style functions in the future. -# pylint: disable=invalid-name -RegisterValidator = register_validator -Validator = validator -RegisterMultiFlagsValidator = register_multi_flags_validator -MultiFlagsValidator = multi_flags_validator -MarkFlagAsRequired = mark_flag_as_required -MarkFlagsAsRequired = mark_flags_as_required -MarkFlagsAsMutualExclusive = mark_flags_as_mutual_exclusive -# pylint: enable=invalid-name diff --git a/third_party/py/gflags/gflags/_helpers.py b/third_party/py/gflags/gflags/_helpers.py deleted file mode 100644 index f740506fdd..0000000000 --- a/third_party/py/gflags/gflags/_helpers.py +++ /dev/null @@ -1,430 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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. - - -"""Helper functions for //gflags.""" - -import collections -import os -import re -import struct -import sys -import textwrap -try: - import fcntl # pylint: disable=g-import-not-at-top -except ImportError: - fcntl = None -try: - # Importing termios will fail on non-unix platforms. - import termios # pylint: disable=g-import-not-at-top -except ImportError: - termios = None - -# pylint: disable=g-import-not-at-top -import third_party.pep257 as pep257 -import six - - -_DEFAULT_HELP_WIDTH = 80 # Default width of help output. -_MIN_HELP_WIDTH = 40 # Minimal "sane" width of help output. We assume that any - # value below 40 is unreasonable. - -# Define the allowed error rate in an input string to get suggestions. -# -# We lean towards a high threshold because we tend to be matching a phrase, -# and the simple algorithm used here is geared towards correcting word -# spellings. -# -# For manual testing, consider " --list" which produced a large number -# of spurious suggestions when we used "least_errors > 0.5" instead of -# "least_erros >= 0.5". -_SUGGESTION_ERROR_RATE_THRESHOLD = 0.50 - -# Characters that cannot appear or are highly discouraged in an XML 1.0 -# document. (See http://www.w3.org/TR/REC-xml/#charsets or -# https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0) -_ILLEGAL_XML_CHARS_REGEX = re.compile( - u'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]') - -# This is a set of module ids for the modules that disclaim key flags. -# This module is explicitly added to this set so that we never consider it to -# define key flag. -disclaim_module_ids = set([id(sys.modules[__name__])]) - - - -# Define special flags here so that help may be generated for them. -# NOTE: Please do NOT use SPECIAL_FLAGS from outside flags module. -# Initialized inside flagvalues.py. -SPECIAL_FLAGS = None - - -class _ModuleObjectAndName( - collections.namedtuple('_ModuleObjectAndName', 'module module_name')): - """Module object and name. - - Fields: - - module: object, module object. - - module_name: str, module name. - """ - - -def GetModuleObjectAndName(globals_dict): - """Returns the module that defines a global environment, and its name. - - Args: - globals_dict: A dictionary that should correspond to an environment - providing the values of the globals. - - Returns: - _ModuleObjectAndName - pair of module object & module name. - Returns (None, None) if the module could not be identified. - """ - name = globals_dict.get('__name__', None) - module = sys.modules.get(name, None) - # Pick a more informative name for the main module. - return _ModuleObjectAndName(module, - (sys.argv[0] if name == '__main__' else name)) - - -def GetCallingModuleObjectAndName(): - """Returns the module that's calling into this module. - - We generally use this function to get the name of the module calling a - DEFINE_foo... function. - - Returns: - The module object that called into this one. - - Raises: - AssertionError: if no calling module could be identified. - """ - range_func = range if sys.version_info[0] >= 3 else xrange - for depth in range_func(1, sys.getrecursionlimit()): - # sys._getframe is the right thing to use here, as it's the best - # way to walk up the call stack. - globals_for_frame = sys._getframe(depth).f_globals # pylint: disable=protected-access - module, module_name = GetModuleObjectAndName(globals_for_frame) - if id(module) not in disclaim_module_ids and module_name is not None: - return _ModuleObjectAndName(module, module_name) - raise AssertionError('No module was found') - - -def GetCallingModule(): - """Returns the name of the module that's calling into this module.""" - return GetCallingModuleObjectAndName().module_name - - -def StrOrUnicode(value): - """Converts a value to a python string. - - Behavior of this function is intentionally different in Python2/3. - - In Python2, the given value is attempted to convert to a str (byte string). - If it contains non-ASCII characters, it is converted to a unicode instead. - - In Python3, the given value is always converted to a str (unicode string). - - This behavior reflects the (bad) practice in Python2 to try to represent - a string as str as long as it contains ASCII characters only. - - Args: - value: An object to be converted to a string. - - Returns: - A string representation of the given value. See the description above - for its type. - """ - try: - return str(value) - except UnicodeEncodeError: - return unicode(value) # Python3 should never come here - - -def CreateXMLDOMElement(doc, name, value): - """Returns an XML DOM element with name and text value. - - Args: - doc: A minidom.Document, the DOM document it should create nodes from. - name: A string, the tag of XML element. - value: A Python object, whose string representation will be used - as the value of the XML element. Illegal or highly discouraged xml 1.0 - characters are stripped. - - Returns: - An instance of minidom.Element. - """ - s = StrOrUnicode(value) - if six.PY2 and not isinstance(s, unicode): - # Get a valid unicode string. - s = s.decode('utf-8', 'ignore') - if isinstance(value, bool): - # Display boolean values as the C++ flag library does: no caps. - s = s.lower() - # Remove illegal xml characters. - s = _ILLEGAL_XML_CHARS_REGEX.sub(u'', s) - - e = doc.createElement(name) - e.appendChild(doc.createTextNode(s)) - return e - - -def GetHelpWidth(): - """Returns: an integer, the width of help lines that is used in TextWrap.""" - if not sys.stdout.isatty() or termios is None or fcntl is None: - return _DEFAULT_HELP_WIDTH - try: - data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234') - columns = struct.unpack('hh', data)[1] - # Emacs mode returns 0. - # Here we assume that any value below 40 is unreasonable. - if columns >= _MIN_HELP_WIDTH: - return columns - # Returning an int as default is fine, int(int) just return the int. - return int(os.getenv('COLUMNS', _DEFAULT_HELP_WIDTH)) - - except (TypeError, IOError, struct.error): - return _DEFAULT_HELP_WIDTH - - -def GetFlagSuggestions(attempt, longopt_list): - """Get helpful similar matches for an invalid flag.""" - # Don't suggest on very short strings, or if no longopts are specified. - if len(attempt) <= 2 or not longopt_list: - return [] - - option_names = [v.split('=')[0] for v in longopt_list] - - # Find close approximations in flag prefixes. - # This also handles the case where the flag is spelled right but ambiguous. - distances = [(_DamerauLevenshtein(attempt, option[0:len(attempt)]), option) - for option in option_names] - distances.sort(key=lambda t: t[0]) - - least_errors, _ = distances[0] - # Don't suggest excessively bad matches. - if least_errors >= _SUGGESTION_ERROR_RATE_THRESHOLD * len(attempt): - return [] - - suggestions = [] - for errors, name in distances: - if errors == least_errors: - suggestions.append(name) - else: - break - return suggestions - - -def _DamerauLevenshtein(a, b): - """Damerau-Levenshtein edit distance from a to b.""" - memo = {} - - def Distance(x, y): - """Recursively defined string distance with memoization.""" - if (x, y) in memo: - return memo[x, y] - if not x: - d = len(y) - elif not y: - d = len(x) - else: - d = min( - Distance(x[1:], y) + 1, # correct an insertion error - Distance(x, y[1:]) + 1, # correct a deletion error - Distance(x[1:], y[1:]) + (x[0] != y[0])) # correct a wrong character - if len(x) >= 2 and len(y) >= 2 and x[0] == y[1] and x[1] == y[0]: - # Correct a transposition. - t = Distance(x[2:], y[2:]) + 1 - if d > t: - d = t - - memo[x, y] = d - return d - return Distance(a, b) - - -def TextWrap(text, length=None, indent='', firstline_indent=None): - """Wraps a given text to a maximum line length and returns it. - - It turns lines that only contain whitespace into empty lines, keeps new lines, - and expands tabs using 4 spaces. - - Args: - text: str, Text to wrap. - length: int, Maximum length of a line, includes indentation. - If this is None then use GetHelpWidth() - indent: str, Indent for all but first line. - firstline_indent: str, Indent for first line; if None, fall back to indent. - - Returns: - Wrapped text. - - Raises: - ValueError: if indent or firstline_indent not shorter than length. - """ - # Get defaults where callee used None - if length is None: - length = GetHelpWidth() - if indent is None: - indent = '' - if firstline_indent is None: - firstline_indent = indent - - if len(indent) >= length: - raise ValueError('Length of indent exceeds length') - if len(firstline_indent) >= length: - raise ValueError('Length of first line indent exceeds length') - - text = text.expandtabs(4) - - result = [] - # Create one wrapper for the first paragraph and one for subsequent - # paragraphs that does not have the initial wrapping. - wrapper = textwrap.TextWrapper( - width=length, initial_indent=firstline_indent, subsequent_indent=indent) - subsequent_wrapper = textwrap.TextWrapper( - width=length, initial_indent=indent, subsequent_indent=indent) - - # textwrap does not have any special treatment for newlines. From the docs: - # "...newlines may appear in the middle of a line and cause strange output. - # For this reason, text should be split into paragraphs (using - # str.splitlines() or similar) which are wrapped separately." - for paragraph in (p.strip() for p in text.splitlines()): - if paragraph: - result.extend(wrapper.wrap(paragraph)) - else: - result.append('') # Keep empty lines. - # Replace initial wrapper with wrapper for subsequent paragraphs. - wrapper = subsequent_wrapper - - return '\n'.join(result) - - -def FlagDictToArgs(flag_map): - """Convert a dict of values into process call parameters. - - This method is used to convert a dictionary into a sequence of parameters - for a binary that parses arguments using this module. - - Args: - flag_map: a mapping where the keys are flag names (strings). - values are treated according to their type: - * If value is None, then only the name is emitted. - * If value is True, then only the name is emitted. - * If value is False, then only the name prepended with 'no' is emitted. - * If value is a string then --name=value is emitted. - * If value is a collection, this will emit --name=value1,value2,value3. - * Everything else is converted to string an passed as such. - Yields: - sequence of string suitable for a subprocess execution. - """ - for key, value in six.iteritems(flag_map): - if value is None: - yield '--%s' % key - elif isinstance(value, bool): - if value: - yield '--%s' % key - else: - yield '--no%s' % key - elif isinstance(value, (bytes, type(u''))): - # We don't want strings to be handled like python collections. - yield '--%s=%s' % (key, value) - else: - # Now we attempt to deal with collections. - try: - yield '--%s=%s' % (key, ','.join(str(item) for item in value)) - except TypeError: - # Default case. - yield '--%s=%s' % (key, value) - - -def DocToHelp(doc): - """Takes a __doc__ string and reformats it as help.""" - - # Get rid of starting and ending white space. Using lstrip() or even - # strip() could drop more than maximum of first line and right space - # of last line. - doc = doc.strip() - - # Get rid of all empty lines. - whitespace_only_line = re.compile('^[ \t]+$', re.M) - doc = whitespace_only_line.sub('', doc) - - # Cut out common space at line beginnings. - doc = pep257.trim(doc) - - # Just like this module's comment, comments tend to be aligned somehow. - # In other words they all start with the same amount of white space. - # 1) keep double new lines; - # 2) keep ws after new lines if not empty line; - # 3) all other new lines shall be changed to a space; - # Solution: Match new lines between non white space and replace with space. - doc = re.sub(r'(?<=\S)\n(?=\S)', ' ', doc, flags=re.M) - - return doc - - -def IsRunningTest(): - """Tries to detect whether we are inside of the test.""" - modules = set(sys.modules) - test_modules = { - 'unittest', - 'unittest2', - 'pytest', - } - return bool(test_modules & modules) - - -# TODO(b/31830082): Migrate all users to PEP8-style methods and remove this. -def define_both_methods(class_name, class_dict, old_name, new_name): # pylint: disable=invalid-name - """Function to help CamelCase to PEP8 style class methods migration. - - For any class definition: - 1. Assert it does not define both old and new methods, - otherwise it does not work. - 2. If it defines the old method, create the same new method. - 3. If it defines the new method, create the same old method. - - Args: - class_name: the class name. - class_dict: the class dictionary. - old_name: old method's name. - new_name: new method's name. - - Raises: - AssertionError: raised when the class defines both the old_name and - new_name. - """ - assert old_name not in class_dict or new_name not in class_dict, ( - 'Class "{}" cannot define both "{}" and "{}" methods.'.format( - class_name, old_name, new_name)) - if old_name in class_dict: - class_dict[new_name] = class_dict[old_name] - elif new_name in class_dict: - class_dict[old_name] = class_dict[new_name] diff --git a/third_party/py/gflags/gflags/_helpers_test.py b/third_party/py/gflags/gflags/_helpers_test.py deleted file mode 100644 index 2a7c124837..0000000000 --- a/third_party/py/gflags/gflags/_helpers_test.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 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 helpers module.""" - -import sys - -import unittest - -import _helpers -from gflags.flags_modules_for_testing import module_bar -from gflags.flags_modules_for_testing import module_foo - - -class FlagSuggestionTest(unittest.TestCase): - - def setUp(self): - self.longopts = [ - 'fsplit-ivs-in-unroller=', - 'fsplit-wide-types=', - 'fstack-protector=', - 'fstack-protector-all=', - 'fstrict-aliasing=', - 'fstrict-overflow=', - 'fthread-jumps=', - 'ftracer', - 'ftree-bit-ccp', - 'ftree-builtin-call-dce', - 'ftree-ccp', - 'ftree-ch'] - - def testDamerauLevenshteinId(self): - self.assertEqual(0, _helpers._DamerauLevenshtein('asdf', 'asdf')) - - def testDamerauLevenshteinEmpty(self): - self.assertEqual(5, _helpers._DamerauLevenshtein('', 'kites')) - self.assertEqual(6, _helpers._DamerauLevenshtein('kitten', '')) - - def testDamerauLevenshteinCommutative(self): - self.assertEqual(2, _helpers._DamerauLevenshtein('kitten', 'kites')) - self.assertEqual(2, _helpers._DamerauLevenshtein('kites', 'kitten')) - - def testDamerauLevenshteinTransposition(self): - self.assertEqual(1, _helpers._DamerauLevenshtein('kitten', 'ktiten')) - - def testMispelledSuggestions(self): - suggestions = _helpers.GetFlagSuggestions('fstack_protector_all', - self.longopts) - self.assertEqual(['fstack-protector-all'], suggestions) - - def testAmbiguousPrefixSuggestion(self): - suggestions = _helpers.GetFlagSuggestions('fstack', self.longopts) - self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) - - def testMisspelledAmbiguousPrefixSuggestion(self): - suggestions = _helpers.GetFlagSuggestions('stack', self.longopts) - self.assertEqual(['fstack-protector', 'fstack-protector-all'], suggestions) - - def testCrazySuggestion(self): - suggestions = _helpers.GetFlagSuggestions('asdfasdgasdfa', self.longopts) - self.assertEqual([], suggestions) - - -class GetCallingModuleTest(unittest.TestCase): - """Test whether we correctly determine the module which defines the flag.""" - - def testGetCallingModule(self): - self.assertEqual(_helpers.GetCallingModule(), sys.argv[0]) - self.assertEqual( - module_foo.GetModuleName(), - 'gflags.flags_modules_for_testing.module_foo') - self.assertEqual( - module_bar.GetModuleName(), - 'gflags.flags_modules_for_testing.module_bar') - - # We execute the following exec statements for their side-effect - # (i.e., not raising an error). They emphasize the case that not - # all code resides in one of the imported modules: Python is a - # really dynamic language, where we can dynamically construct some - # code and execute it. - code = ('import _helpers\n' - 'module_name = _helpers.GetCallingModule()') - exec(code) # pylint: disable=exec-used - - # Next two exec statements executes code with a global environment - # that is different from the global environment of any imported - # module. - exec(code, {}) # pylint: disable=exec-used - # vars(self) returns a dictionary corresponding to the symbol - # table of the self object. dict(...) makes a distinct copy of - # this dictionary, such that any new symbol definition by the - # exec-ed code (e.g., import flags, module_name = ...) does not - # affect the symbol table of self. - exec(code, dict(vars(self))) # pylint: disable=exec-used - - # Next test is actually more involved: it checks not only that - # GetCallingModule does not crash inside exec code, it also checks - # that it returns the expected value: the code executed via exec - # code is treated as being executed by the current module. We - # check it twice: first time by executing exec from the main - # module, second time by executing it from module_bar. - global_dict = {} - exec(code, global_dict) # pylint: disable=exec-used - self.assertEqual(global_dict['module_name'], - sys.argv[0]) - - global_dict = {} - module_bar.ExecuteCode(code, global_dict) - self.assertEqual( - global_dict['module_name'], - 'gflags.flags_modules_for_testing.module_bar') - - def testGetCallingModuleWithIteritemsError(self): - # This test checks that GetCallingModule is using - # sys.modules.items(), instead of .iteritems(). - orig_sys_modules = sys.modules - - # Mock sys.modules: simulates error produced by importing a module - # in paralel with our iteration over sys.modules.iteritems(). - class SysModulesMock(dict): - - def __init__(self, original_content): - dict.__init__(self, original_content) - - def iteritems(self): - # Any dictionary method is fine, but not .iteritems(). - raise RuntimeError('dictionary changed size during iteration') - - sys.modules = SysModulesMock(orig_sys_modules) - try: - # _GetCallingModule should still work as expected: - self.assertEqual(_helpers.GetCallingModule(), sys.argv[0]) - self.assertEqual( - module_foo.GetModuleName(), - 'gflags.flags_modules_for_testing.module_foo') - finally: - sys.modules = orig_sys_modules - - -class IsRunningTestTest(unittest.TestCase): - - def testUnderTest(self): - self.assertTrue(_helpers.IsRunningTest()) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/third_party/py/gflags/gflags/argument_parser.py b/third_party/py/gflags/gflags/argument_parser.py deleted file mode 100644 index 9f7262b231..0000000000 --- a/third_party/py/gflags/gflags/argument_parser.py +++ /dev/null @@ -1,480 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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. - - -"""Contains base classes used to parse and convert arguments. - -Instead of importing this module directly, it's preferable to import the -flags package and use the aliases defined at the package level. -""" - -import csv -import io -import string - -import six - -import _helpers - - -class _ArgumentParserCache(type): - """Metaclass used to cache and share argument parsers among flags.""" - - _instances = {} - - def __new__(mcs, name, bases, dct): - _helpers.define_both_methods(name, dct, 'Parse', 'parse') - _helpers.define_both_methods(name, dct, 'Type', 'flag_type') - _helpers.define_both_methods(name, dct, 'Convert', 'convert') - return type.__new__(mcs, name, bases, dct) - - def __call__(cls, *args, **kwargs): - """Returns an instance of the argument parser cls. - - This method overrides behavior of the __new__ methods in - all subclasses of ArgumentParser (inclusive). If an instance - for cls with the same set of arguments exists, this instance is - returned, otherwise a new instance is created. - - If any keyword arguments are defined, or the values in args - are not hashable, this method always returns a new instance of - cls. - - Args: - *args: Positional initializer arguments. - **kwargs: Initializer keyword arguments. - - Returns: - An instance of cls, shared or new. - """ - if kwargs: - return type.__call__(cls, *args, **kwargs) - else: - instances = cls._instances - key = (cls,) + tuple(args) - try: - return instances[key] - except KeyError: - # No cache entry for key exists, create a new one. - return instances.setdefault(key, type.__call__(cls, *args)) - except TypeError: - # An object in args cannot be hashed, always return - # a new instance. - return type.__call__(cls, *args) - - -class ArgumentParser(six.with_metaclass(_ArgumentParserCache, object)): - """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. - - Argument parser classes must be stateless, since instances are cached - and shared between flags. Initializer arguments are allowed, but all - member variables must be derived from initializer arguments only. - """ - - syntactic_help = '' - - def parse(self, argument): - """Parses the string argument and returns the native value. - - By default it returns its argument unmodified. - - Args: - argument: string argument passed in the commandline. - - Raises: - ValueError: Raised when it fails to parse the argument. - - Returns: - The parsed value in native type. - """ - return argument - - def flag_type(self): - """Returns a string representing the type of the flag.""" - return 'string' - - def _custom_xml_dom_elements(self, doc): # pylint: disable=unused-argument - """Returns a list of XML DOM elements to add additional flag information. - - Args: - doc: A minidom.Document, the DOM document it should create nodes from. - - Returns: - A list of minidom.Element. - """ - return [] - - -class _ArgumentSerializerMeta(type): - - def __new__(mcs, name, bases, dct): - _helpers.define_both_methods(name, dct, 'Serialize', 'serialize') - return type.__new__(mcs, name, bases, dct) - - -class ArgumentSerializer(six.with_metaclass(_ArgumentSerializerMeta, object)): - """Base class for generating string representations of a flag value.""" - - def serialize(self, value): - return _helpers.StrOrUnicode(value) - - -class NumericParser(ArgumentParser): - """Parser of numeric values. - - Parsed value may be bounded to a given upper and lower bound. - """ - - def is_outside_bounds(self, val): - return ((self.lower_bound is not None and val < self.lower_bound) or - (self.upper_bound is not None and val > self.upper_bound)) - - def parse(self, argument): - val = self.convert(argument) - if self.is_outside_bounds(val): - raise ValueError('%s is not %s' % (val, self.syntactic_help)) - return val - - def _custom_xml_dom_elements(self, doc): - elements = [] - if self.lower_bound is not None: - elements.append(_helpers.CreateXMLDOMElement( - doc, 'lower_bound', self.lower_bound)) - if self.upper_bound is not None: - elements.append(_helpers.CreateXMLDOMElement( - doc, 'upper_bound', self.upper_bound)) - return elements - - def convert(self, argument): - """Default implementation: always returns its argument unmodified.""" - return argument - - -class FloatParser(NumericParser): - """Parser of floating point values. - - Parsed value may be 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): - super(FloatParser, self).__init__() - self.lower_bound = lower_bound - self.upper_bound = upper_bound - sh = self.syntactic_help - if lower_bound is not None and upper_bound is not None: - sh = ('%s in the range [%s, %s]' % (sh, lower_bound, upper_bound)) - elif lower_bound == 0: - sh = 'a non-negative %s' % self.number_name - elif upper_bound == 0: - sh = 'a non-positive %s' % self.number_name - elif upper_bound is not None: - sh = '%s <= %s' % (self.number_name, upper_bound) - elif lower_bound is not None: - sh = '%s >= %s' % (self.number_name, lower_bound) - self.syntactic_help = sh - - def convert(self, argument): - """Converts argument to a float; raises ValueError on errors.""" - return float(argument) - - def flag_type(self): - return 'float' - - -class IntegerParser(NumericParser): - """Parser of an integer value. - - Parsed value may be bounded to a given upper and lower bound. - """ - number_article = 'an' - number_name = 'integer' - syntactic_help = ' '.join((number_article, number_name)) - - def __init__(self, lower_bound=None, upper_bound=None): - super(IntegerParser, self).__init__() - self.lower_bound = lower_bound - self.upper_bound = upper_bound - sh = self.syntactic_help - if lower_bound is not None and upper_bound is not 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 == 0: - sh = 'a non-positive %s' % self.number_name - elif upper_bound is not None: - sh = '%s <= %s' % (self.number_name, upper_bound) - elif lower_bound is not None: - sh = '%s >= %s' % (self.number_name, lower_bound) - self.syntactic_help = sh - - def convert(self, argument): - if isinstance(argument, str): - base = 10 - if len(argument) > 2 and argument[0] == '0': - if argument[1] == 'o': - base = 8 - elif argument[1] == 'x': - base = 16 - return int(argument, base) - else: - return int(argument) - - def flag_type(self): - return 'int' - - -class BooleanParser(ArgumentParser): - """Parser of boolean values.""" - - def convert(self, argument): - """Converts the argument to a boolean; raise ValueError on errors.""" - if isinstance(argument, str): - if argument.lower() in ['true', 't', '1']: - return True - elif argument.lower() in ['false', 'f', '0']: - return False - - bool_argument = bool(argument) - if argument == bool_argument: - # The argument is a valid boolean (True, False, 0, or 1), and not just - # something that always converts to bool (list, string, int, etc.). - return bool_argument - - raise ValueError('Non-boolean argument to boolean flag', argument) - - def parse(self, argument): - val = self.convert(argument) - return val - - def flag_type(self): - return 'bool' - - -class EnumParser(ArgumentParser): - """Parser of a string enum value (a string value from a given set). - - If enum_values (see below) is not specified, any string is allowed. - """ - - def __init__(self, enum_values=None, case_sensitive=True): - """Initialize EnumParser. - - Args: - enum_values: Array of values in the enum. - case_sensitive: Whether or not the enum is to be case-sensitive. - """ - super(EnumParser, self).__init__() - self.enum_values = enum_values - self.case_sensitive = case_sensitive - - def parse(self, argument): - """Determine validity of argument and return the correct element of enum. - - If self.enum_values is empty, then all arguments are valid and argument - will be returned. - - Otherwise, if argument matches an element in enum, then the first - matching element will be returned. - - Args: - argument: The supplied flag value. - - Returns: - The matching element from enum_values, or argument if enum_values is - empty. - - Raises: - ValueError: enum_values was non-empty, but argument didn't match - anything in enum. - """ - if not self.enum_values: - return argument - elif self.case_sensitive: - if argument not in self.enum_values: - raise ValueError('value should be one of <%s>' % - '|'.join(self.enum_values)) - else: - return argument - else: - if argument.upper() not in [value.upper() for value in self.enum_values]: - raise ValueError('value should be one of <%s>' % - '|'.join(self.enum_values)) - else: - return [value for value in self.enum_values - if value.upper() == argument.upper()][0] - - def flag_type(self): - return 'string enum' - - -class ListSerializer(ArgumentSerializer): - - def __init__(self, list_sep): - self.list_sep = list_sep - - def serialize(self, value): - return self.list_sep.join([_helpers.StrOrUnicode(x) for x in value]) - - -class CsvListSerializer(ArgumentSerializer): - - def __init__(self, list_sep): - self.list_sep = list_sep - - def serialize(self, value): - """Serialize a list as a string, if possible, or as a unicode string.""" - if six.PY2: - # In Python2 csv.writer doesn't accept unicode, so we convert to UTF-8. - output = io.BytesIO() - csv.writer(output).writerow([unicode(x).encode('utf-8') for x in value]) - serialized_value = output.getvalue().decode('utf-8').strip() - else: - # In Python3 csv.writer expects a text stream. - output = io.StringIO() - csv.writer(output).writerow([str(x) for x in value]) - serialized_value = output.getvalue().strip() - - # We need the returned value to be pure ascii or Unicodes so that - # when the xml help is generated they are usefully encodable. - return _helpers.StrOrUnicode(serialized_value) - - -class BaseListParser(ArgumentParser): - """Base class for a parser of lists of strings. - - To extend, inherit from this class; from the subclass __init__, 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 - super(BaseListParser, self).__init__() - self._token = token - self._name = name - self.syntactic_help = 'a %s separated list' % self._name - - def parse(self, argument): - if isinstance(argument, list): - return argument - elif not argument: - return [] - else: - return [s.strip() for s in argument.split(self._token)] - - def flag_type(self): - return '%s separated list of strings' % self._name - - -class ListParser(BaseListParser): - """Parser for a comma-separated list of strings.""" - - def __init__(self): - BaseListParser.__init__(self, ',', 'comma') - - def parse(self, argument): - """Override to support full CSV syntax.""" - if isinstance(argument, list): - return argument - elif not argument: - return [] - else: - try: - return [s.strip() for s in list(csv.reader([argument], strict=True))[0]] - except csv.Error as e: - # Provide a helpful report for case like - # --listflag="$(printf 'hello,\nworld')" - # IOW, list flag values containing naked newlines. This error - # was previously "reported" by allowing csv.Error to - # propagate. - raise ValueError('Unable to parse the value %r as a %s: %s' - % (argument, self.flag_type(), e)) - - def _custom_xml_dom_elements(self, doc): - elements = super(ListParser, self)._custom_xml_dom_elements(doc) - elements.append(_helpers.CreateXMLDOMElement( - doc, 'list_separator', repr(','))) - return elements - - -class WhitespaceSeparatedListParser(BaseListParser): - """Parser for a whitespace-separated list of strings.""" - - def __init__(self, comma_compat=False): - """Initializer. - - Args: - comma_compat: bool - Whether to support comma as an additional separator. - If false then only whitespace is supported. This is intended only for - backwards compatibility with flags that used to be comma-separated. - """ - self._comma_compat = comma_compat - name = 'whitespace or comma' if self._comma_compat else 'whitespace' - BaseListParser.__init__(self, None, name) - - def parse(self, argument): - """Override to support comma compatibility.""" - if isinstance(argument, list): - return argument - elif not argument: - return [] - else: - if self._comma_compat: - argument = argument.replace(',', ' ') - return argument.split() - - def _custom_xml_dom_elements(self, doc): - elements = super(WhitespaceSeparatedListParser, self - )._custom_xml_dom_elements(doc) - separators = list(string.whitespace) - if self._comma_compat: - separators.append(',') - separators.sort() - for sep_char in separators: - elements.append(_helpers.CreateXMLDOMElement( - doc, 'list_separator', repr(sep_char))) - return elements diff --git a/third_party/py/gflags/gflags/exceptions.py b/third_party/py/gflags/gflags/exceptions.py deleted file mode 100644 index efaabb89fd..0000000000 --- a/third_party/py/gflags/gflags/exceptions.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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. - - -"""//gflags exceptions. - -Instead of importing this module directly, it's preferable to import the -flags package and use the aliases defined at the package level. -""" - -import sys - -import _helpers - - -# TODO(vrusinov): use DISCLAIM_key_flags when it's moved out of __init__. -_helpers.disclaim_module_ids.add(id(sys.modules[__name__])) - - -class Error(Exception): - """The base class for all flags errors.""" - - -# TODO(b/31596146): Remove FlagsError. -FlagsError = Error - - -class CantOpenFlagFileError(Error): - """Raised if flagfile fails to open: doesn't exist, wrong permissions, etc.""" - - -class DuplicateFlagCannotPropagateNoneToSwig(Error): - """Raised when redefining a SWIG flag and the default value is None. - - It's raised when redefining a SWIG flag with allow_override=True and the - default value is None. Because it's currently impossible to pass None default - value back to SWIG. See FlagValues.SetDefault for details. - """ - - -class DuplicateFlagError(Error): - """Raised if there is a flag naming conflict.""" - - @classmethod - def from_flag(cls, flagname, flag_values, other_flag_values=None): - """Create a DuplicateFlagError by providing flag name and values. - - Args: - flagname: Name of the flag being redefined. - flag_values: FlagValues object containing the first definition of - flagname. - other_flag_values: If this argument is not None, it should be the - FlagValues object where the second definition of flagname occurs. - If it is None, we assume that we're being called when attempting - to create the flag a second time, and we use the module calling - this one as the source of the second definition. - - Returns: - An instance of DuplicateFlagError. - """ - first_module = flag_values.FindModuleDefiningFlag( - flagname, default='') - if other_flag_values is None: - second_module = _helpers.GetCallingModule() - else: - second_module = other_flag_values.FindModuleDefiningFlag( - flagname, default='') - flag_summary = flag_values[flagname].help - msg = ("The flag '%s' is defined twice. First from %s, Second from %s. " - "Description from first occurrence: %s") % ( - flagname, first_module, second_module, flag_summary) - return cls(msg) - - -class IllegalFlagValueError(Error): - """Raised if the flag command line argument is illegal.""" - - -# TODO(yileiyang): Remove IllegalFlagValue. -IllegalFlagValue = IllegalFlagValueError - - -class UnrecognizedFlagError(Error): - """Raised if a flag is unrecognized. - - Attributes: - flagname: Name of the unrecognized flag. - flagvalue: Value of the flag, empty if the flag is not defined. - """ - - def __init__(self, flagname, flagvalue='', suggestions=None): - self.flagname = flagname - self.flagvalue = flagvalue - if suggestions: - tip = '. Did you mean: %s?' % ', '.join(suggestions) - else: - tip = '' - Error.__init__( - self, 'Unknown command line flag \'%s\'%s' % (flagname, tip)) - - -class UnparsedFlagAccessError(Error): - """Attempt to use flag from unparsed FlagValues.""" - - -class ValidationError(Error): - """Raised if flag validator constraint is not satisfied.""" diff --git a/third_party/py/gflags/gflags/flag.py b/third_party/py/gflags/gflags/flag.py deleted file mode 100644 index e176b5e35f..0000000000 --- a/third_party/py/gflags/gflags/flag.py +++ /dev/null @@ -1,416 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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. - -"""Contains Flag class - information about single command-line flag. - -Instead of importing this module directly, it's preferable to import the -flags package and use the aliases defined at the package level. -""" - -from functools import total_ordering - -import six - -import _helpers -import argument_parser -import exceptions - - -class _FlagMetaClass(type): - - def __new__(mcs, name, bases, dct): - _helpers.define_both_methods(name, dct, 'Parse', 'parse') - _helpers.define_both_methods(name, dct, 'Unparse', 'unparse') - _helpers.define_both_methods(name, dct, 'Serialize', 'serialize') - - # TODO(b/32385202): Migrate all users to use FlagValues.SetDefault and - # remove the public Flag.SetDefault method. - _helpers.define_both_methods(name, dct, 'SetDefault', '_set_default') - - _helpers.define_both_methods(name, dct, 'Type', 'flag_type') - return type.__new__(mcs, name, bases, dct) - - -@total_ordering -class Flag(six.with_metaclass(_FlagMetaClass, object)): - """Information about a command-line 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, and - newly defined flag overrides the old one. - .allow_cpp_override - the flag may be redefined in C++ without raising an - error, value "transfered" to C++, and the flag is - replaced by the C++ flag after init; - .allow_hide_cpp - the flag may be redefined despite hiding a C++ flag with - the same name; - .using_default_value - the flag value has not been set by user; - .allow_overwrite - the flag may be parsed more than once without raising - an error, the last set value will be used; - - 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 attribute is updated. If - this flag was already present, an Error is raised. - - Parse() is also called during __init__ to parse the default value and - initialize the .value attribute. This enables other python modules to - safely use flags even if the __main__ module neglects to parse the - command line arguments. The .present attribute is cleared after - __init__ parsing. If the default value is set to None, then the - __init__ parsing step is skipped and the .value attribute 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=False, allow_override=False, - allow_cpp_override=False, allow_hide_cpp=False, - allow_overwrite=True, parse_default=True): - self.name = name - - 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.allow_cpp_override = allow_cpp_override - self.allow_hide_cpp = allow_hide_cpp - self.allow_overwrite = allow_overwrite - - self.using_default_value = True - self._value = None - self.validators = [] - if allow_hide_cpp and allow_cpp_override: - raise exceptions.Error( - "Can't have both allow_hide_cpp (means use Python flag) and " - 'allow_cpp_override (means use C++ flag after InitGoogle)') - - if parse_default: - self._set_default(default) - else: - self.default = default - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - - def __hash__(self): - return hash(id(self)) - - def __eq__(self, other): - return self is other - - def __lt__(self, other): - if isinstance(other, Flag): - return id(self) < id(other) - return NotImplemented - - def _get_parsed_value_as_string(self, value): - """Get parsed flag value as string.""" - if value is None: - return None - if self.serializer: - return repr(self.serializer.serialize(value)) - if self.boolean: - if value: - return repr('true') - else: - return repr('false') - return repr(_helpers.StrOrUnicode(value)) - - def parse(self, argument): - """Parse string and set flag value. - - Args: - argument: String, value to be parsed for flag. - """ - if self.present and not self.allow_overwrite: - raise exceptions.IllegalFlagValueError( - 'flag --%s=%s: already defined as %s' % ( - self.name, argument, self.value)) - try: - self.value = self.parser.parse(argument) - except ValueError as e: # Recast ValueError as IllegalFlagValueError. - raise exceptions.IllegalFlagValueError( - 'flag --%s=%s: %s' % (self.name, argument, e)) - self.present += 1 - - def unparse(self): - if self.default is None: - self.value = None - else: - self.present = 0 - self.Parse(self.default) - self.using_default_value = True - 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 exceptions.Error( - 'Serializer not present for flag %s' % self.name) - return '--%s=%s' % (self.name, self.serializer.serialize(self.value)) - - def _set_default(self, value): - """Changes the default value (and current value too) for this Flag.""" - # 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 value is None and self.allow_override: - raise exceptions.DuplicateFlagCannotPropagateNoneToSwig(self.name) - - self.default = value - self.unparse() - self.default_as_str = self._get_parsed_value_as_string(self.value) - - def flag_type(self): - """Get type of flag. - - NOTE: we use strings, and not the types.*Type constants because - our flags can have more exotic types, e.g., 'comma separated list - of strings', 'whitespace separated list of strings', etc. - - Returns: - a string that describes the type of this Flag. - """ - return self.parser.flag_type() - - def _create_xml_dom_element(self, doc, module_name, is_key=False): - """Returns an XML element that contains this flag's information. - - This is information that is relevant to all flags (e.g., name, - meaning, etc.). If you defined a flag that has some other pieces of - info, then please override _ExtraXMLInfo. - - Please do NOT override this method. - - Args: - doc: A minidom.Document, the DOM document it should create nodes from. - module_name: A string, the name of the module that defines this flag. - is_key: A boolean, True iff this flag is key for main module. - - Returns: - A minidom.Element instance. - """ - element = doc.createElement('flag') - if is_key: - element.appendChild(_helpers.CreateXMLDOMElement(doc, 'key', 'yes')) - element.appendChild(_helpers.CreateXMLDOMElement(doc, 'file', module_name)) - # Adds flag features that are relevant for all flags. - element.appendChild(_helpers.CreateXMLDOMElement(doc, 'name', self.name)) - if self.short_name: - element.appendChild(_helpers.CreateXMLDOMElement( - doc, 'short_name', self.short_name)) - if self.help: - element.appendChild(_helpers.CreateXMLDOMElement( - doc, 'meaning', self.help)) - # The default flag value can either be represented as a string like on the - # command line, or as a Python object. We serialize this value in the - # latter case in order to remain consistent. - if self.serializer and not isinstance(self.default, str): - if self.default is not None: - default_serialized = self.serializer.serialize(self.default) - else: - default_serialized = '' - else: - default_serialized = self.default - element.appendChild(_helpers.CreateXMLDOMElement( - doc, 'default', default_serialized)) - element.appendChild(_helpers.CreateXMLDOMElement( - doc, 'current', self.value)) - element.appendChild(_helpers.CreateXMLDOMElement( - doc, 'type', self.flag_type())) - # Adds extra flag features this flag may have. - for e in self._extra_xml_dom_elements(doc): - element.appendChild(e) - return element - - def _extra_xml_dom_elements(self, doc): - """Returns extra info about this flag in XML. - - "Extra" means "not already included by _create_xml_dom_element above." - - Args: - doc: A minidom.Document, the DOM document it should create nodes from. - - Returns: - A list of minidom.Element. - """ - # Usually, the parser knows the extra details about the flag, so - # we just forward the call to it. - return self.parser._custom_xml_dom_elements(doc) # pylint: disable=protected-access - - -class BooleanFlag(Flag): - """Basic boolean flag. - - Boolean flags do not take any arguments, and their value is either - True (1) or False (0). The false value is specified on the command - line by prepending the word 'no' to either the long or the 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): # pylint: disable=redefined-builtin - p = argument_parser.BooleanParser() - Flag.__init__(self, p, None, name, default, help, short_name, 1, **args) - - -class EnumFlag(Flag): - """Basic enum flag; its value can be any string from list of enum_values.""" - - def __init__(self, name, default, help, enum_values=None, # pylint: disable=redefined-builtin - short_name=None, case_sensitive=True, **args): - enum_values = enum_values or [] - p = argument_parser.EnumParser(enum_values, case_sensitive) - g = argument_parser.ArgumentSerializer() - Flag.__init__(self, p, g, name, default, help, short_name, **args) - self.help = '<%s>: %s' % ('|'.join(enum_values), self.help) - - def _extra_xml_dom_elements(self, doc): - elements = [] - for enum_value in self.parser.enum_values: - elements.append(_helpers.CreateXMLDOMElement( - doc, 'enum_value', enum_value)) - return elements - - -class MultiFlag(Flag): - """A flag that can appear multiple time on the command-line. - - The value of such a flag is a list that contains the individual values - from all the appearances of that flag on the command-line. - - See the __doc__ for Flag for most behavior of this class. Only - differences in behavior are described here: - - * The default value may be either a single value or a list of values. - A single value is interpreted as the [value] singleton list. - - * 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 += ';\n repeat this option to specify a list of values' - - def parse(self, arguments): - """Parses one or more arguments with the installed parser. - - Args: - arguments: a single argument or a list of arguments (typically a - list of default values); a single argument is 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 the 'value' attribute - self.value = values - - def serialize(self): - if not self.serializer: - raise exceptions.Error( - '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 flag_type(self): - return 'multi ' + self.parser.flag_type() - - def _extra_xml_dom_elements(self, doc): - elements = [] - if hasattr(self.parser, 'enum_values'): - for enum_value in self.parser.enum_values: - elements.append(_helpers.CreateXMLDOMElement( - doc, 'enum_value', enum_value)) - return elements diff --git a/third_party/py/gflags/gflags/flags_formatting_test.py b/third_party/py/gflags/gflags/flags_formatting_test.py deleted file mode 100644 index d2d585cb72..0000000000 --- a/third_party/py/gflags/gflags/flags_formatting_test.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python -import unittest - -import gflags -import _helpers - -FLAGS = gflags.FLAGS - - -class FlagsUnitTest(unittest.TestCase): - """Flags formatting Unit Test.""" - - def testGetHelpWidth(self): - """Verify that GetHelpWidth() reflects _help_width.""" - default_help_width = _helpers._DEFAULT_HELP_WIDTH # Save. - self.assertEqual(80, _helpers._DEFAULT_HELP_WIDTH) - self.assertEqual(_helpers._DEFAULT_HELP_WIDTH, gflags.GetHelpWidth()) - _helpers._DEFAULT_HELP_WIDTH = 10 - self.assertEqual(_helpers._DEFAULT_HELP_WIDTH, gflags.GetHelpWidth()) - _helpers._DEFAULT_HELP_WIDTH = default_help_width # restore - - def testTextWrap(self): - """Test that wrapping works as expected. - - Also tests that it is using global gflags._help_width by default. - """ - default_help_width = _helpers._DEFAULT_HELP_WIDTH - _helpers._DEFAULT_HELP_WIDTH = 10 - - # Generate a string with length 40, no spaces - text = '' - expect = [] - for n in range(4): - line = str(n) - line += '123456789' - text += line - expect.append(line) - - # Verify we still break - wrapped = gflags.TextWrap(text).split('\n') - self.assertEqual(4, len(wrapped)) - self.assertEqual(expect, wrapped) - - wrapped = gflags.TextWrap(text, 80).split('\n') - self.assertEqual(1, len(wrapped)) - self.assertEqual([text], wrapped) - - # Normal case, breaking at word boundaries and rewriting new lines - input_value = 'a b c d e f g h' - expect = {1: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], - 2: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], - 3: ['a b', 'c d', 'e f', 'g h'], - 4: ['a b', 'c d', 'e f', 'g h'], - 5: ['a b c', 'd e f', 'g h'], - 6: ['a b c', 'd e f', 'g h'], - 7: ['a b c d', 'e f g h'], - 8: ['a b c d', 'e f g h'], - 9: ['a b c d e', 'f g h'], - 10: ['a b c d e', 'f g h'], - 11: ['a b c d e f', 'g h'], - 12: ['a b c d e f', 'g h'], - 13: ['a b c d e f g', 'h'], - 14: ['a b c d e f g', 'h'], - 15: ['a b c d e f g h']} - for width, exp in expect.items(): - self.assertEqual(exp, gflags.TextWrap(input_value, width).split('\n')) - - # We turn lines with only whitespace into empty lines - # We strip from the right up to the first new line - self.assertEqual('', gflags.TextWrap(' ')) - self.assertEqual('\n', gflags.TextWrap(' \n ')) - self.assertEqual('\n', gflags.TextWrap('\n\n')) - self.assertEqual('\n\n', gflags.TextWrap('\n\n\n')) - self.assertEqual('\n', gflags.TextWrap('\n ')) - self.assertEqual('a\n\nb', gflags.TextWrap('a\n \nb')) - self.assertEqual('a\n\n\nb', gflags.TextWrap('a\n \n \nb')) - self.assertEqual('a\nb', gflags.TextWrap(' a\nb ')) - self.assertEqual('\na\nb', gflags.TextWrap('\na\nb\n')) - self.assertEqual('\na\nb\n', gflags.TextWrap(' \na\nb\n ')) - self.assertEqual('\na\nb\n', gflags.TextWrap(' \na\nb\n\n')) - - # Double newline. - self.assertEqual('a\n\nb', gflags.TextWrap(' a\n\n b')) - - # We respect prefix - self.assertEqual(' a\n b\n c', gflags.TextWrap('a\nb\nc', 80, ' ')) - self.assertEqual('a\n b\n c', gflags.TextWrap('a\nb\nc', 80, ' ', '')) - - # tabs - self.assertEqual('a\n b c', - gflags.TextWrap('a\nb\tc', 80, ' ', '')) - self.assertEqual('a\n bb c', - gflags.TextWrap('a\nbb\tc', 80, ' ', '')) - self.assertEqual('a\n bbb c', - gflags.TextWrap('a\nbbb\tc', 80, ' ', '')) - self.assertEqual('a\n bbbb c', - gflags.TextWrap('a\nbbbb\tc', 80, ' ', '')) - self.assertEqual('a\n b\n c\n d', - gflags.TextWrap('a\nb\tc\td', 3, ' ', '')) - self.assertEqual('a\n b\n c\n d', - gflags.TextWrap('a\nb\tc\td', 4, ' ', '')) - self.assertEqual('a\n b\n c\n d', - gflags.TextWrap('a\nb\tc\td', 5, ' ', '')) - self.assertEqual('a\n b c\n d', - gflags.TextWrap('a\nb\tc\td', 6, ' ', '')) - self.assertEqual('a\n b c\n d', - gflags.TextWrap('a\nb\tc\td', 7, ' ', '')) - self.assertEqual('a\n b c\n d', - gflags.TextWrap('a\nb\tc\td', 8, ' ', '')) - self.assertEqual('a\n b c\n d', - gflags.TextWrap('a\nb\tc\td', 9, ' ', '')) - self.assertEqual('a\n b c d', - gflags.TextWrap('a\nb\tc\td', 10, ' ', '')) - - # multiple tabs - self.assertEqual('a c', - gflags.TextWrap('a\t\tc', 80, ' ', '')) - - _helpers._DEFAULT_HELP_WIDTH = default_help_width # restore - - def testDocToHelp(self): - self.assertEqual('', gflags.DocToHelp(' ')) - self.assertEqual('', gflags.DocToHelp(' \n ')) - self.assertEqual('a\n\nb', gflags.DocToHelp('a\n \nb')) - self.assertEqual('a\n\n\nb', gflags.DocToHelp('a\n \n \nb')) - self.assertEqual('a b', gflags.DocToHelp(' a\nb ')) - self.assertEqual('a b', gflags.DocToHelp('\na\nb\n')) - self.assertEqual('a\n\nb', gflags.DocToHelp('\na\n\nb\n')) - self.assertEqual('a b', gflags.DocToHelp(' \na\nb\n ')) - # Different first line, one line empty - erm double new line. - self.assertEqual('a b c\n\nd', gflags.DocToHelp('a\n b\n c\n\n d')) - self.assertEqual('a b\n c d', gflags.DocToHelp('a\n b\n \tc\n d')) - self.assertEqual('a b\n c\n d', - gflags.DocToHelp('a\n b\n \tc\n \td')) - - def testDocToHelp_FlagValues(self): - # !!!!!!!!!!!!!!!!!!!! - # The following doc string is taken as is directly from gflags.py:FlagValues - # The intention of this test is to verify 'live' performance - # !!!!!!!!!!!!!!!!!!!! - """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 - """ - doc = gflags.DocToHelp(self.testDocToHelp_FlagValues.__doc__) - # Test the general outline of the converted docs - lines = doc.splitlines() - self.assertEqual(17, len(lines)) - empty_lines = [index for index in range(len(lines)) if not lines[index]] - self.assertEqual([1, 3, 5, 8, 12, 15], empty_lines) - # test that some starting prefix is kept - flags_lines = [index for index in range(len(lines)) - if lines[index].startswith(' FLAGS')] - self.assertEqual([7, 10, 11], flags_lines) - # but other, especially common space has been removed - space_lines = [index for index in range(len(lines)) - if lines[index] and lines[index][0].isspace()] - self.assertEqual([7, 10, 11, 14], space_lines) - # No right space was kept - rspace_lines = [index for index in range(len(lines)) - if lines[index] != lines[index].rstrip()] - self.assertEqual([], rspace_lines) - # test double spaces are kept - self.assertEqual(True, lines[2].endswith('application: gflags.FLAGS')) - - def testTextWrapRaisesOnExcessiveIndent(self): - """Ensure an indent longer than line length raises.""" - self.assertRaises(ValueError, - gflags.TextWrap, 'dummy', length=10, indent=' ' * 10) - - def testTextWrapRaisesOnExcessiveFirstLine(self): - """Ensure a first line indent longer than line length raises.""" - self.assertRaises( - ValueError, - gflags.TextWrap, 'dummy', length=80, firstline_indent=' ' * 80) - - -if __name__ == '__main__': - unittest.main() diff --git a/third_party/py/gflags/gflags/flags_modules_for_testing/__init__.py b/third_party/py/gflags/gflags/flags_modules_for_testing/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py b/third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py deleted file mode 100644 index 4dddd005bb..0000000000 --- a/third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 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. - - -"""Auxiliary module for testing gflags.py. - -The purpose of this module is to define a few flags. We want to make -sure the unit tests for gflags.py involve more than one module. -""" - -__author__ = 'salcianu@google.com (Alex Salcianu)' - -import gflags -import _helpers - -FLAGS = gflags.FLAGS - - -def DefineFlags(flag_values=FLAGS): - """Defines some flags. - - Args: - flag_values: The FlagValues object we want to register the flags - with. - """ - # The 'tmod_bar_' prefix (short for 'test_module_bar') ensures there - # is no name clash with the existing flags. - gflags.DEFINE_boolean('tmod_bar_x', True, 'Boolean flag.', - flag_values=flag_values) - gflags.DEFINE_string('tmod_bar_y', 'default', 'String flag.', - flag_values=flag_values) - gflags.DEFINE_boolean('tmod_bar_z', False, - 'Another boolean flag from module bar.', - flag_values=flag_values) - gflags.DEFINE_integer('tmod_bar_t', 4, 'Sample int flag.', - flag_values=flag_values) - gflags.DEFINE_integer('tmod_bar_u', 5, 'Sample int flag.', - flag_values=flag_values) - gflags.DEFINE_integer('tmod_bar_v', 6, 'Sample int flag.', - flag_values=flag_values) - - -def RemoveOneFlag(flag_name, flag_values=FLAGS): - """Removes the definition of one flag from gflags.FLAGS. - - Note: if the flag is not defined in gflags.FLAGS, this function does - not do anything (in particular, it does not raise any exception). - - Motivation: We use this function for cleanup *after* a test: if - there was a failure during a test and not all flags were declared, - we do not want the cleanup code to crash. - - Args: - flag_name: A string, the name of the flag to delete. - flag_values: The FlagValues object we remove the flag from. - """ - if flag_name in flag_values.FlagDict(): - flag_values.__delattr__(flag_name) - - -def NamesOfDefinedFlags(): - """Returns: List of names of the flags declared in this module.""" - return ['tmod_bar_x', - 'tmod_bar_y', - 'tmod_bar_z', - 'tmod_bar_t', - 'tmod_bar_u', - 'tmod_bar_v'] - - -def RemoveFlags(flag_values=FLAGS): - """Deletes the flag definitions done by the above DefineFlags(). - - Args: - flag_values: The FlagValues object we remove the flags from. - """ - for flag_name in NamesOfDefinedFlags(): - RemoveOneFlag(flag_name, flag_values=flag_values) - - -def GetModuleName(): - """Uses GetCallingModule() to return the name of this module. - - For checking that _GetCallingModule works as expected. - - Returns: - A string, the name of this module. - """ - return _helpers.GetCallingModule() - - -def ExecuteCode(code, global_dict): - """Executes some code in a given global environment. - - For testing of _GetCallingModule. - - Args: - code: A string, the code to be executed. - global_dict: A dictionary, the global environment that code should - be executed in. - """ - # Indeed, using exec generates a lint warning. But some user code - # actually uses exec, and we have to test for it ... - exec(code, global_dict) # pylint: disable=exec-used - - -def DisclaimKeyFlags(): - """Disclaims flags declared in this module.""" - gflags.DISCLAIM_key_flags() diff --git a/third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py b/third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py deleted file mode 100644 index cb47832ba3..0000000000 --- a/third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 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. - -"""Auxiliary module for testing gflags.py. - -The purpose of this module is to test the behavior of flags that are defined -before main() executes. -""" - - - - -import gflags - -FLAGS = gflags.FLAGS - -gflags.DEFINE_boolean('tmod_baz_x', True, 'Boolean flag.') diff --git a/third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py b/third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py deleted file mode 100644 index 34e464d297..0000000000 --- a/third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# Copyright 2009 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. - -"""Auxiliary module for testing gflags.py. - -The purpose of this module is to define a few flags, and declare some -other flags as being important. We want to make sure the unit tests -for gflags.py involve more than one module. -""" - -__author__ = 'salcianu@google.com (Alex Salcianu)' - -import gflags -import _helpers -from gflags.flags_modules_for_testing import module_bar - -FLAGS = gflags.FLAGS - - -DECLARED_KEY_FLAGS = ['tmod_bar_x', 'tmod_bar_z', 'tmod_bar_t', - # Special (not user-defined) flag: - 'flagfile'] - - -def DefineFlags(flag_values=FLAGS): - """Defines a few flags.""" - module_bar.DefineFlags(flag_values=flag_values) - # The 'tmod_foo_' prefix (short for 'test_module_foo') ensures that we - # have no name clash with existing flags. - gflags.DEFINE_boolean('tmod_foo_bool', True, 'Boolean flag from module foo.', - flag_values=flag_values) - gflags.DEFINE_string('tmod_foo_str', 'default', 'String flag.', - flag_values=flag_values) - gflags.DEFINE_integer('tmod_foo_int', 3, 'Sample int flag.', - flag_values=flag_values) - - -def DeclareKeyFlags(flag_values=FLAGS): - """Declares a few key flags.""" - for flag_name in DECLARED_KEY_FLAGS: - gflags.DECLARE_key_flag(flag_name, flag_values=flag_values) - - -def DeclareExtraKeyFlags(flag_values=FLAGS): - """Declares some extra key flags.""" - gflags.ADOPT_module_key_flags(module_bar, flag_values=flag_values) - - -def NamesOfDefinedFlags(): - """Returns: list of names of flags defined by this module.""" - return ['tmod_foo_bool', 'tmod_foo_str', 'tmod_foo_int'] - - -def NamesOfDeclaredKeyFlags(): - """Returns: list of names of key flags for this module.""" - return NamesOfDefinedFlags() + DECLARED_KEY_FLAGS - - -def NamesOfDeclaredExtraKeyFlags(): - """Returns the list of names of additional key flags for this module. - - These are the flags that became key for this module only as a result - of a call to DeclareExtraKeyFlags() above. I.e., the flags declared - by module_bar, that were not already declared as key for this - module. - - Returns: - The list of names of additional key flags for this module. - """ - names_of_extra_key_flags = list(module_bar.NamesOfDefinedFlags()) - for flag_name in NamesOfDeclaredKeyFlags(): - while flag_name in names_of_extra_key_flags: - names_of_extra_key_flags.remove(flag_name) - return names_of_extra_key_flags - - -def RemoveFlags(flag_values=FLAGS): - """Deletes the flag definitions done by the above DefineFlags().""" - for flag_name in NamesOfDefinedFlags(): - module_bar.RemoveOneFlag(flag_name, flag_values=flag_values) - module_bar.RemoveFlags(flag_values=flag_values) - - -def GetModuleName(): - """Uses GetCallingModule() to return the name of this module. - - For checking that _GetCallingModule works as expected. - - Returns: - A string, the name of this module. - """ - return _helpers.GetCallingModule() - - -def DuplicateFlags(flagnames=None): - """Returns a new FlagValues object with the requested flagnames. - - Used to test DuplicateFlagError detection. - - Args: - flagnames: str, A list of flag names to create. - - Returns: - A FlagValues object with one boolean flag for each name in flagnames. - """ - flag_values = gflags.FlagValues() - for name in flagnames: - gflags.DEFINE_boolean(name, False, 'Flag named %s' % (name,), - flag_values=flag_values) - return flag_values - - -def DefineBarFlags(flag_values=FLAGS): - """Defines flags from module_bar.""" - module_bar.DefineFlags(flag_values) diff --git a/third_party/py/gflags/gflags/flags_unicode_literals_test.py b/third_party/py/gflags/gflags/flags_unicode_literals_test.py deleted file mode 100644 index cb20887c13..0000000000 --- a/third_party/py/gflags/gflags/flags_unicode_literals_test.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -"""Test the use of flags when from __future__ import unicode_literals is on.""" - -from __future__ import unicode_literals - -import unittest -import gflags - - -gflags.DEFINE_string('seen_in_crittenden', 'alleged mountain lion', - 'This tests if unicode input to these functions works.') - - -class FlagsUnicodeLiteralsTest(unittest.TestCase): - - def testUnicodeFlagNameAndValueAreGood(self): - alleged_mountain_lion = gflags.FLAGS.seen_in_crittenden - self.assertTrue( - isinstance(alleged_mountain_lion, type(u'')), - msg='expected flag value to be a {} not {}'.format( - type(u''), type(alleged_mountain_lion))) - self.assertEqual(alleged_mountain_lion, u'alleged mountain lion') - - -if __name__ == '__main__': - unittest.main() diff --git a/third_party/py/gflags/gflags/flagvalues.py b/third_party/py/gflags/gflags/flagvalues.py deleted file mode 100644 index a47b40308f..0000000000 --- a/third_party/py/gflags/gflags/flagvalues.py +++ /dev/null @@ -1,1261 +0,0 @@ -#!/usr/bin/env python -# Copyright 2002 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. - -"""Flagvalues module - Registry of 'Flag' objects. - -Instead of importing this module directly, it's preferable to import the -flags package and use the aliases defined at the package level. -""" - -import hashlib -import logging -import os -import struct -import sys -import traceback -import warnings -from xml.dom import minidom - -import six - -import _helpers -import exceptions -import flag as _flag - -# Add flagvalues module to disclaimed module ids. -_helpers.disclaim_module_ids.add(id(sys.modules[__name__])) - -# The MOE directives in this file cause the docstring indentation -# linter to go nuts. -# pylint: disable=g-doc-bad-indent - -# Environment variable that controls whether to allow unparsed flag access. -# Do not rely on, it will be removed later. -_UNPARSED_FLAG_ACCESS_ENV_NAME = 'GFLAGS_ALLOW_UNPARSED_FLAG_ACCESS' - -# Percentage of the flag names for which unparsed flag access will fail by -# default. -_UNPARSED_ACCESS_DISABLED_PERCENT = 0 - -# b/32278439 will change flag parsing to use GNU-style scanning by default. -# This environment variable allows users to force setting the default parsing -# style. Do NOT rely on it. It will be removed as part of b/32278439. -_USE_GNU_GET_OPT_ENV_NAME = 'GFLAGS_USE_GNU_GET_OPT' - - - - -class FlagValues(object): - """Registry of '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 attribute of the registered 'Flag' objects can be accessed - as attributes 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. - - # Dictionary: flag name (string) -> Flag object. - self.__dict__['__flags'] = {} - - # Set: name of hidden flag (string). - # Holds flags that should not be directly accessible from Python. - self.__dict__['__hiddenflags'] = set() - - # Dictionary: module name (string) -> list of Flag objects that are defined - # by that module. - self.__dict__['__flags_by_module'] = {} - # Dictionary: module id (int) -> list of Flag objects that are defined by - # that module. - self.__dict__['__flags_by_module_id'] = {} - # Dictionary: module name (string) -> list of Flag objects that are - # key for that module. - self.__dict__['__key_flags_by_module'] = {} - - # Bool: True if flags were parsed. - self.__dict__['__flags_parsed'] = False - - # Bool: True if Reset() was called. - self.__dict__['__reset_called'] = False - - # None or Method(name, value) to call from __setattr__ for an unknown flag. - self.__dict__['__set_unknown'] = None - - if _USE_GNU_GET_OPT_ENV_NAME in os.environ: - self.__dict__['__use_gnu_getopt'] = ( - os.environ[_USE_GNU_GET_OPT_ENV_NAME] == '1') - else: - # By default don't use the GNU-style scanning when parsing the args. - self.__dict__['__use_gnu_getopt'] = False - - def UseGnuGetOpt(self, use_gnu_getopt=True): - """Use GNU-style scanning. Allows mixing of flag and non-flag arguments. - - See http://docs.python.org/library/getopt.html#getopt.gnu_getopt - - Args: - use_gnu_getopt: wether or not to use GNU style scanning. - """ - self.__dict__['__use_gnu_getopt'] = use_gnu_getopt - - def IsGnuGetOpt(self): - return self.__dict__['__use_gnu_getopt'] - - def FlagDict(self): - return self.__dict__['__flags'] - - def FlagsByModuleDict(self): - """Returns the dictionary of module_name -> list of defined flags. - - Returns: - A dictionary. Its keys are module names (strings). Its values - are lists of Flag objects. - """ - return self.__dict__['__flags_by_module'] - - def FlagsByModuleIdDict(self): - """Returns the dictionary of module_id -> list of defined flags. - - Returns: - A dictionary. Its keys are module IDs (ints). Its values - are lists of Flag objects. - """ - return self.__dict__['__flags_by_module_id'] - - def KeyFlagsByModuleDict(self): - """Returns the dictionary of module_name -> list of key flags. - - Returns: - A dictionary. Its keys are module names (strings). Its values - are lists of Flag objects. - """ - return self.__dict__['__key_flags_by_module'] - - def _RegisterFlagByModule(self, module_name, flag): - """Records the module that defines a specific flag. - - We keep track of which flag is defined by which module so that we - can later sort the flags by module. - - Args: - module_name: A string, the name of a Python module. - flag: A Flag object, a flag that is key to the module. - """ - flags_by_module = self.FlagsByModuleDict() - flags_by_module.setdefault(module_name, []).append(flag) - - def _RegisterFlagByModuleId(self, module_id, flag): - """Records the module that defines a specific flag. - - Args: - module_id: An int, the ID of the Python module. - flag: A Flag object, a flag that is key to the module. - """ - flags_by_module_id = self.FlagsByModuleIdDict() - flags_by_module_id.setdefault(module_id, []).append(flag) - - def _RegisterKeyFlagForModule(self, module_name, flag): - """Specifies that a flag is a key flag for a module. - - Args: - module_name: A string, the name of a Python module. - flag: A Flag object, a flag that is key to the module. - """ - key_flags_by_module = self.KeyFlagsByModuleDict() - # The list of key flags for the module named module_name. - key_flags = key_flags_by_module.setdefault(module_name, []) - # Add flag, but avoid duplicates. - if flag not in key_flags: - key_flags.append(flag) - - def _FlagIsRegistered(self, flag_obj): - """Checks whether a Flag object is registered under long name or short name. - - Args: - flag_obj: A Flag object. - - Returns: - A boolean: True iff flag_obj is registered under long name or short name. - """ - flag_dict = self.FlagDict() - # Check whether flag_obj is registered under its long name. - name = flag_obj.name - if flag_dict.get(name, None) == flag_obj: - return True - # Check whether flag_obj is registered under its short name. - short_name = flag_obj.short_name - if (short_name is not None and - flag_dict.get(short_name, None) == flag_obj): - return True - return False - - def _CleanupUnregisteredFlagFromModuleDicts(self, flag_obj): - """Cleanup unregistered flags from all module -> [flags] dictionaries. - - If flag_obj is registered under either its long name or short name, it - won't be removed from the dictionaries. - - Args: - flag_obj: A flag object. - """ - if self._FlagIsRegistered(flag_obj): - return - for flags_by_module_dict in (self.FlagsByModuleDict(), - self.FlagsByModuleIdDict(), - self.KeyFlagsByModuleDict()): - for flags_in_module in six.itervalues(flags_by_module_dict): - # While (as opposed to if) takes care of multiple occurrences of a - # flag in the list for the same module. - while flag_obj in flags_in_module: - flags_in_module.remove(flag_obj) - - def _GetFlagsDefinedByModule(self, module): - """Returns the list of flags defined by a module. - - Args: - module: A module object or a module name (a string). - - Returns: - A new list of Flag objects. Caller may update this list as he - wishes: none of those changes will affect the internals of this - FlagValue object. - """ - if not isinstance(module, str): - module = module.__name__ - - return list(self.FlagsByModuleDict().get(module, [])) - - def _GetKeyFlagsForModule(self, module): - """Returns the list of key flags for a module. - - Args: - module: A module object or a module name (a string) - - Returns: - A new list of Flag objects. Caller may update this list as he - wishes: none of those changes will affect the internals of this - FlagValue object. - """ - if not isinstance(module, str): - module = module.__name__ - - # Any flag is a key flag for the module that defined it. NOTE: - # key_flags is a fresh list: we can update it without affecting the - # internals of this FlagValues object. - key_flags = self._GetFlagsDefinedByModule(module) - - # Take into account flags explicitly declared as key for a module. - for flag in self.KeyFlagsByModuleDict().get(module, []): - if flag not in key_flags: - key_flags.append(flag) - return key_flags - - def FindModuleDefiningFlag(self, flagname, default=None): - """Return the name of the module defining this flag, or default. - - Args: - flagname: Name of the flag to lookup. - default: Value to return if flagname is not defined. Defaults - to None. - - Returns: - The name of the module which registered the flag with this name. - If no such module exists (i.e. no flag with this name exists), - we return default. - """ - registered_flag = self.FlagDict().get(flagname) - if registered_flag is None: - return default - for module, flags in six.iteritems(self.FlagsByModuleDict()): - for flag in flags: - # It must compare the flag with the one in FlagDict. This is because a - # flag might be overridden only for its long name (or short name), - # and only its short name (or long name) is considered registered. - if (flag.name == registered_flag.name and - flag.short_name == registered_flag.short_name): - return module - return default - - def FindModuleIdDefiningFlag(self, flagname, default=None): - """Return the ID of the module defining this flag, or default. - - Args: - flagname: Name of the flag to lookup. - default: Value to return if flagname is not defined. Defaults - to None. - - Returns: - The ID of the module which registered the flag with this name. - If no such module exists (i.e. no flag with this name exists), - we return default. - """ - registered_flag = self.FlagDict().get(flagname) - if registered_flag is None: - return default - for module_id, flags in six.iteritems(self.FlagsByModuleIdDict()): - for flag in flags: - # It must compare the flag with the one in FlagDict. This is because a - # flag might be overridden only for its long name (or short name), - # and only its short name (or long name) is considered registered. - if (flag.name == registered_flag.name and - flag.short_name == registered_flag.short_name): - return module_id - return default - - def _RegisterUnknownFlagSetter(self, setter): - """Allow set default values for undefined flags. - - Args: - setter: Method(name, value) to call to __setattr__ an unknown flag. - Must raise NameError or ValueError for invalid name/value. - """ - self.__dict__['__set_unknown'] = setter - - def _SetUnknownFlag(self, name, value): - """Returns value if setting flag |name| to |value| returned True. - - Args: - name: Name of the flag to set. - value: Value to set. - - Returns: - Flag value on successful call. - - Raises: - UnrecognizedFlagError - IllegalFlagValueError - """ - setter = self.__dict__['__set_unknown'] - if setter: - try: - setter(name, value) - return value - except (TypeError, ValueError): # Flag value is not valid. - raise exceptions.IllegalFlagValueError('"{1}" is not valid for --{0}' - .format(name, value)) - except NameError: # Flag name is not valid. - pass - raise exceptions.UnrecognizedFlagError(name, value) - - def AppendFlagValues(self, flag_values): - """Appends flags registered in another FlagValues instance. - - Args: - flag_values: registry to copy from - """ - for flag_name, flag in six.iteritems(flag_values.FlagDict()): - # Each flags with shortname appears here twice (once under its - # normal name, and again with its short name). To prevent - # problems (DuplicateFlagError) with double flag registration, we - # perform a check to make sure that the entry we're looking at is - # for its normal name. - if flag_name == flag.name: - try: - self[flag_name] = flag - except exceptions.DuplicateFlagError: - raise exceptions.DuplicateFlagError.from_flag( - flag_name, self, other_flag_values=flag_values) - - def RemoveFlagValues(self, flag_values): - """Remove flags that were previously appended from another FlagValues. - - Args: - flag_values: registry containing flags to remove. - """ - for flag_name in flag_values.FlagDict(): - self.__delattr__(flag_name) - - def __setitem__(self, name, flag): - """Registers a new flag variable.""" - fl = self.FlagDict() - if not isinstance(flag, _flag.Flag): - raise exceptions.IllegalFlagValueError(flag) - if str is bytes and isinstance(name, unicode): - # When using Python 2 with unicode_literals, allow it but encode it - # into the bytes type we require. - name = name.encode('utf-8') - if not isinstance(name, type('')): - raise exceptions.Error('Flag name must be a string') - if not name: - raise exceptions.Error('Flag name cannot be empty') - if name in fl and not flag.allow_override and not fl[name].allow_override: - module, module_name = _helpers.GetCallingModuleObjectAndName() - if (self.FindModuleDefiningFlag(name) == module_name and - id(module) != self.FindModuleIdDefiningFlag(name)): - # If the flag has already been defined by a module with the same name, - # but a different ID, we can stop here because it indicates that the - # module is simply being imported a subsequent time. - return - raise exceptions.DuplicateFlagError.from_flag(name, self) - short_name = flag.short_name - # If a new flag overrides an old one, we need to cleanup the old flag's - # modules if it's not registered. - flags_to_cleanup = set() - if short_name is not None: - if (short_name in fl and not flag.allow_override and - not fl[short_name].allow_override): - raise exceptions.DuplicateFlagError.from_flag(short_name, self) - if short_name in fl and fl[short_name] != flag: - flags_to_cleanup.add(fl[short_name]) - fl[short_name] = flag - if (name not in fl # new flag - or fl[name].using_default_value - or not flag.using_default_value): - if name in fl and fl[name] != flag: - flags_to_cleanup.add(fl[name]) - fl[name] = flag - for f in flags_to_cleanup: - self._CleanupUnregisteredFlagFromModuleDicts(f) - - def __dir__(self): - """Returns list of names of all defined flags. - - Useful for TAB-completion in ipython. - - Returns: - list(str) - """ - return sorted(self.__dict__['__flags']) - - # TODO(olexiy): Call GetFlag() to raise UnrecognizedFlagError if name is - # unknown. - def __getitem__(self, name): - """Retrieves the Flag object for the flag --name.""" - return self.FlagDict()[name] - - def GetFlag(self, name): - """Same as __getitem__, but raises a specific error.""" - res = self.FlagDict().get(name) - if res is None: - raise exceptions.UnrecognizedFlagError(name) - return res - - def HideFlag(self, name): - """Mark the flag --name as hidden.""" - self.__dict__['__hiddenflags'].add(name) - - def _IsUnparsedFlagAccessAllowed(self, name): - """Determine whether to allow unparsed flag access or not.""" - if _UNPARSED_FLAG_ACCESS_ENV_NAME in os.environ: - # We've been told explicitly what to do. - allow_unparsed_flag_access = ( - os.getenv(_UNPARSED_FLAG_ACCESS_ENV_NAME) == '1') - elif self.__dict__['__reset_called']: - # Raise exception if .Reset() was called. This mostly happens in tests. - allow_unparsed_flag_access = False - elif _helpers.IsRunningTest(): - # Staged "rollout", based on name of the flag so that we don't break - # everyone. Hashing the flag is a way of choosing a random but - # consistent subset of flags to lock down which we can make larger - # over time. - name_bytes = name.encode('utf8') if not isinstance(name, bytes) else name - flag_percentile = ( - struct.unpack(' - - E.g., - - gflags.DEFINE_integer('foo', 1, 'Integer flag.') - del gflags.FLAGS.foo - - If a flag is also registered by its the other name (long name or short - name), the other name won't be deleted. - - Args: - flag_name: A string, the name of the flag to be deleted. - - Raises: - AttributeError: When there is no registered flag named flag_name. - """ - fl = self.FlagDict() - if flag_name not in fl: - raise AttributeError(flag_name) - - flag_obj = fl[flag_name] - del fl[flag_name] - - self._CleanupUnregisteredFlagFromModuleDicts(flag_obj) - - def _RemoveAllFlagAppearances(self, name): - """Removes flag with name for all appearances. - - A flag can be registered with its long name and an optional short name. - This method removes both of them. This is different than __delattr__. - - Args: - name: Either flag's long name or short name. - - Raises: - UnrecognizedFlagError: When flag name is not found. - """ - flag_dict = self.FlagDict() - if name not in flag_dict: - raise exceptions.UnrecognizedFlagError(name) - flag = flag_dict[name] - names_to_remove = {name} - names_to_remove.add(flag.name) - if flag.short_name: - names_to_remove.add(flag.short_name) - for n in names_to_remove: - self.__delattr__(n) - - def SetDefault(self, name, value): - """Changes the default value (and current value) of the named flag object. - - Call this method at the top level of a module to avoid overwriting the value - passed at the command line. - - Args: - name: A string, the name of the flag to modify. - value: The new default value. - - Raises: - UnrecognizedFlagError: When there is no registered flag named name. - IllegalFlagValueError: When value is not valid. - """ - fl = self.FlagDict() - if name not in fl: - self._SetUnknownFlag(name, value) - return - if self.IsParsed(): - logging.warn( - 'FLAGS.SetDefault called on flag "%s" after flag parsing. Call this ' - 'method at the top level of a module to avoid overwriting the value ' - 'passed at the command line.', - name) - fl[name]._set_default(value) # pylint: disable=protected-access - self._AssertValidators(fl[name].validators) - - def __contains__(self, name): - """Returns 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 iter(self.FlagDict()) - - def __call__(self, argv, known_only=False): - """Parses flags from argv; stores parsed flags into this FlagValues object. - - All unparsed arguments are returned. - - Args: - argv: argument list. Can be of any type that may be converted to a list. - known_only: parse and remove known flags, return rest untouched. - - Returns: - The list of arguments not parsed as options, including argv[0]. - - Raises: - Error: on any parsing error. - ValueError: on flag value parsing error. - """ - if not argv: - # Unfortunately, the old parser used to accept an empty argv, and some - # users rely on that behaviour. Allow it as a special case for now. - self.MarkAsParsed() - self._AssertAllValidators() - return [] - - # This pre parses the argv list for --flagfile=<> options. - program_name = argv[0] - args = self.ReadFlagsFromFiles(argv[1:], force_gnu=False) - - # Parse the arguments. - unknown_flags, unparsed_args, undefok = self._ParseArgs(args, known_only) - - # Handle unknown flags by raising UnrecognizedFlagError. - # Note some users depend on us raising this particular error. - for name, value in unknown_flags: - if name in undefok: - continue - - suggestions = _helpers.GetFlagSuggestions( - name, self.RegisteredFlags()) - raise exceptions.UnrecognizedFlagError( - name, value, suggestions=suggestions) - - self.MarkAsParsed() - self._AssertAllValidators() - return [program_name] + unparsed_args - - def _ParseArgs(self, args, known_only): - """Helper function to do the main argument parsing. - - This function goes through args and does the bulk of the flag parsing. - It will find the corresponding flag in our flag dictionary, and call its - .parse() method on the flag value. - - Args: - args: List of strings with the arguments to parse. - known_only: parse and remove known flags, return rest in unparsed_args - - Returns: - A tuple with the following: - unknown_flags: List of (flag name, arg) for flags we don't know about. - unparsed_args: List of arguments we did not parse. - undefok: Set of flags that were given via --undefok. - - Raises: - Error: on any parsing error. - ValueError: on flag value parsing error. - """ - unknown_flags, unparsed_args, undefok = [], [], set() - - flag_dict = self.FlagDict() - args = iter(args) - for arg in args: - value = None - - def GetValue(): - # pylint: disable=cell-var-from-loop - try: - return next(args) if value is None else value - except StopIteration: - raise exceptions.Error('Missing value for flag ' + arg) - - if not arg.startswith('-'): - # A non-argument: default is break, GNU is skip. - unparsed_args.append(arg) - if self.IsGnuGetOpt(): - continue - else: - break - - if arg == '--': - if known_only: - unparsed_args.append(arg) - break - - if '=' in arg: - name, value = arg.lstrip('-').split('=', 1) - else: - name, value = arg.lstrip('-'), None - - if not name: - # The argument is all dashes (including one dash). - unparsed_args.append(arg) - if self.IsGnuGetOpt(): - continue - else: - break - - # --undefok is a special case. - if name == 'undefok': - if known_only: - unparsed_args.append(arg) - value = GetValue() - undefok.update(v.strip() for v in value.split(',')) - undefok.update('no' + v.strip() for v in value.split(',')) - continue - - flag = flag_dict.get(name) - if flag: - value = (flag.boolean and value is None) or GetValue() - elif name.startswith('no') and len(name) > 2: - # Boolean flags can take the form of --noflag, with no value. - noflag = flag_dict.get(name[2:]) - if noflag and noflag.boolean: - if value is not None: - raise ValueError(arg + ' does not take an argument') - flag = noflag - value = False - - - if flag: - flag.parse(value) - flag.using_default_value = False - elif known_only: - unparsed_args.append(arg) - else: - unknown_flags.append((name, arg)) - - unparsed_args.extend(args) - return unknown_flags, unparsed_args, undefok - - def IsParsed(self): - """Whether flags were parsed.""" - return self.__dict__['__flags_parsed'] - - def MarkAsParsed(self): - """Explicitly mark parsed. - - Use this when the caller knows that this FlagValues has been parsed as if - a __call__() invocation has happened. This is only a public method for - use by things like appcommands which do additional command like parsing. - """ - self.__dict__['__flags_parsed'] = True - - def Reset(self): - """Resets the values to the point before FLAGS(argv) was called.""" - for f in self.FlagDict().values(): - f.unparse() - # We log this message before marking flags as unparsed to avoid a - # problem when the logging library causes flags access. - logging.info('Reset() called; flags access will now raise errors.') - self.__dict__['__flags_parsed'] = False - self.__dict__['__reset_called'] = True - - def RegisteredFlags(self): - """Returns: a list of the names and short names of all registered flags.""" - return list(self.FlagDict()) - - def FlagValuesDict(self): - """Returns: a dictionary that maps flag names to flag 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): - """Generates a help string for all known flags.""" - return self.GetHelp() - - def GetHelp(self, prefix='', include_special_flags=True): - """Generates a help string for all known flags. - - Args: - prefix: str, per-line output prefix. - include_special_flags: bool, whether to include description of - _SPECIAL_FLAGS, i.e. --flagfile and --undefok. - - Returns: - str, formatted help message. - """ - # TODO(vrusinov): this function needs a test. - helplist = [] - - flags_by_module = self.FlagsByModuleDict() - if flags_by_module: - modules = sorted(flags_by_module) - - # Print the help for the main module first, if possible. - main_module = sys.argv[0] - if main_module in modules: - modules.remove(main_module) - modules = [main_module] + modules - - for module in modules: - self.__RenderOurModuleFlags(module, helplist) - if include_special_flags: - self.__RenderModuleFlags('gflags', - _helpers.SPECIAL_FLAGS.FlagDict().values(), - helplist) - else: - # Just print one long list of flags. - values = self.FlagDict().values() - if include_special_flags: - values.append(_helpers.SPECIAL_FLAGS.FlagDict().values()) - self.__RenderFlagList(values, helplist, prefix) - - return '\n'.join(helplist) - - def __RenderModuleFlags(self, module, flags, output_lines, prefix=''): - """Generates a help string for a given module.""" - if not isinstance(module, str): - module = module.__name__ - output_lines.append('\n%s%s:' % (prefix, module)) - self.__RenderFlagList(flags, output_lines, prefix + ' ') - - def __RenderOurModuleFlags(self, module, output_lines, prefix=''): - """Generates a help string for a given module.""" - flags = self._GetFlagsDefinedByModule(module) - if flags: - self.__RenderModuleFlags(module, flags, output_lines, prefix) - - def __RenderOurModuleKeyFlags(self, module, output_lines, prefix=''): - """Generates a help string for the key flags of a given module. - - Args: - module: A module object or a module name (a string). - output_lines: A list of strings. The generated help message - lines will be appended to this list. - prefix: A string that is prepended to each generated help line. - """ - key_flags = self._GetKeyFlagsForModule(module) - if key_flags: - self.__RenderModuleFlags(module, key_flags, output_lines, prefix) - - def ModuleHelp(self, module): - """Describe the key flags of a module. - - Args: - module: A module object or a module name (a string). - - Returns: - string describing the key flags of a module. - """ - helplist = [] - self.__RenderOurModuleKeyFlags(module, helplist) - return '\n'.join(helplist) - - def MainModuleHelp(self): - """Describe the key flags of the main module. - - Returns: - string describing the key flags of a module. - """ - return self.ModuleHelp(sys.argv[0]) - - def __RenderFlagList(self, flaglist, output_lines, prefix=' '): - fl = self.FlagDict() - special_fl = _helpers.SPECIAL_FLAGS.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 and special_fl.get(name, None) != flag: - # a different flag is using this name now - continue - # only print help once - if flag in flagset: 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 - flaghelp = _helpers.TextWrap( - flaghelp, indent=prefix+' ', firstline_indent=prefix) - if flag.default_as_str: - flaghelp += '\n' - flaghelp += _helpers.TextWrap( - '(default: %s)' % flag.default_as_str, indent=prefix+' ') - if flag.parser.syntactic_help: - flaghelp += '\n' - flaghelp += _helpers.TextWrap( - '(%s)' % flag.parser.syntactic_help, indent=prefix+' ') - output_lines.append(flaghelp) - - def get_flag_value(self, name, default): # pylint: disable=invalid-name - """Returns the value of a flag (if not None) or a default value. - - Args: - name: A string, the name of a flag. - default: Default value to use if the flag value is None. - - Returns: - Requested flag value or default. - """ - - 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 - - # TODO(b/32098517): Remove this. - get = get_flag_value - - def __IsFlagFileDirective(self, flag_string): - """Checks whether flag_string contain a --flagfile= directive.""" - 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): - """Returns filename from a flagfile_str of form -[-]flagfile=filename. - - The cases of --flagfile foo and -flagfile foo shouldn't be hitting - this function, as they are dealt with in the level above this - function. - - Args: - flagfile_str: flagfile string. - - Returns: - str filename from a flagfile_str of form -[-]flagfile=filename. - - Raises: - Error: when illegal --flagfile provided. - """ - 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 exceptions.Error( - 'Hit illegal --flagfile type: %s' % flagfile_str) - - def __GetFlagFileLines(self, filename, parsed_file_stack=None): - """Returns the useful (!=comments, etc) lines from a file with flags. - - Args: - filename: A string, the name of the flag file. - parsed_file_stack: A list of the names of the files that we have - recursively encountered at the current depth. MUTATED BY THIS FUNCTION - (but the original value is preserved upon successfully returning from - function call). - - Returns: - List of strings. See the note below. - - NOTE(springer): This function checks for a nested --flagfile= - tag and handles the lower file recursively. It returns a list of - all the lines that _could_ contain command flags. This is - EVERYTHING except whitespace lines and comments (lines starting - with '#' or '//'). - """ - if parsed_file_stack is None: - parsed_file_stack = [] - # We do a little safety check for reparsing a file we've already encountered - # at a previous depth. - if filename in parsed_file_stack: - sys.stderr.write('Warning: Hit circular flagfile dependency. Ignoring' - ' flagfile: %s\n' % (filename,)) - return [] - else: - parsed_file_stack.append(filename) - - 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 as e_msg: - raise exceptions.CantOpenFlagFileError( - 'ERROR:: Unable to open flagfile: %s' % e_msg) - - with file_obj: - line_list = file_obj.readlines() - - # 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=" flag in the current file. - # If we find one, recursively parse down into that file. - elif self.__IsFlagFileDirective(line): - sub_filename = self.ExtractFilename(line) - included_flags = self.__GetFlagFileLines( - sub_filename, parsed_file_stack=parsed_file_stack) - flag_line_list.extend(included_flags) - else: - # Any line that's not a comment or a nested flagfile should get - # copied into 2nd position. This leaves earlier arguments - # further back in the list, thus giving them higher priority. - flag_line_list.append(line.strip()) - - parsed_file_stack.pop() - return flag_line_list - - def ReadFlagsFromFiles(self, argv, force_gnu=True): - """Processes command line args, but also allow args to be read from file. - - Args: - argv: A list of strings, usually sys.argv[1:], which may contain one or - more flagfile directives of the form --flagfile="./filename". - Note that the name of the program (sys.argv[0]) should be omitted. - force_gnu: If False, --flagfile parsing obeys normal flag semantics. - If True, --flagfile parsing instead follows gnu_getopt semantics. - *** WARNING *** force_gnu=False may become the future default! - - Returns: - A new list which has the original list combined with what we read - from any flagfile(s). - - Raises: - IllegalFlagValueError: when --flagfile provided with no argument. - - References: Global gflags.FLAG class instance. - - This function should be called before the normal FLAGS(argv) call. - This function scans the input list for a flag that looks like: - --flagfile=. Then it opens , reads all valid key - and value pairs and inserts them into the input list in exactly the - place where the --flagfile arg is found. - - 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): - --> For duplicate flags, the last one we hit should "win". - --> Since flags that appear later win, a flagfile's settings can be "weak" - if the --flagfile comes at the beginning of the argument sequence, - and it can be "strong" if the --flagfile comes at the end. - --> A further "--flagfile=" CAN be nested in a flagfile. - It will be expanded in exactly the spot where it is found. - --> In a flagfile, a line beginning with # or // is a comment. - --> Entirely blank lines _should_ be ignored. - """ - 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. In this case the - # next arg really is part of this one. - if current_arg == '--flagfile' or current_arg == '-flagfile': - if not rest_of_args: - raise exceptions.IllegalFlagValueError( - '--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.extend(self.__GetFlagFileLines(flag_filename)) - else: - new_argv.append(current_arg) - # Stop parsing after '--', like getopt and gnu_getopt. - if current_arg == '--': - break - # Stop parsing after a non-flag, like getopt. - if not current_arg.startswith('-'): - if not force_gnu and not self.__dict__['__use_gnu_getopt']: - break - else: - if ('=' not in current_arg and - rest_of_args and not rest_of_args[0].startswith('-')): - # If this is an occurence of a legitimate --x y, skip the value - # so that it won't be mistaken for a standalone arg. - fl = self.FlagDict() - name = current_arg.lstrip('-') - if name in fl and not fl[name].boolean: - current_arg = rest_of_args[0] - rest_of_args = rest_of_args[1:] - new_argv.append(current_arg) - - if rest_of_args: - new_argv.extend(rest_of_args) - - return new_argv - - def FlagsIntoString(self): - """Returns a string with the flags assignments from this FlagValues object. - - This function ignores flags whose value is None. Each flag - assignment is separated by a newline. - - NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString - from http://code.google.com/p/google-gflags - - Returns: - string with the flags assignments from this FlagValues object. - """ - 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 assignments from this FlagInfo object to a file. - - Output will be in the format of a flagfile. - - NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile - from http://code.google.com/p/google-gflags - - Args: - filename: string, name of the file. - """ - with open(filename, 'a') as out_file: - out_file.write(self.FlagsIntoString()) - - def WriteHelpInXMLFormat(self, outfile=None): - """Outputs flag documentation in XML format. - - NOTE: We use element names that are consistent with those used by - the C++ command-line flag library, from - http://code.google.com/p/google-gflags - We also use a few new elements (e.g., ), but we do not - interfere / overlap with existing XML elements used by the C++ - library. Please maintain this consistency. - - Args: - outfile: File object we write to. Default None means sys.stdout. - """ - doc = minidom.Document() - all_flag = doc.createElement('AllFlags') - doc.appendChild(all_flag) - - all_flag.appendChild(_helpers.CreateXMLDOMElement( - doc, 'program', os.path.basename(sys.argv[0]))) - - usage_doc = sys.modules['__main__'].__doc__ - if not usage_doc: - usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0] - else: - usage_doc = usage_doc.replace('%s', sys.argv[0]) - all_flag.appendChild(_helpers.CreateXMLDOMElement(doc, 'usage', usage_doc)) - - # Get list of key flags for the main module. - key_flags = self._GetKeyFlagsForModule(sys.argv[0]) - - # Sort flags by declaring module name and next by flag name. - flags_by_module = self.FlagsByModuleDict() - all_module_names = list(flags_by_module.keys()) - all_module_names.sort() - for module_name in all_module_names: - flag_list = [(f.name, f) for f in flags_by_module[module_name]] - flag_list.sort() - for unused_flag_name, flag in flag_list: - is_key = flag in key_flags - all_flag.appendChild(flag._create_xml_dom_element( # pylint: disable=protected-access - doc, module_name, is_key=is_key)) - - outfile = outfile or sys.stdout - if six.PY2: - outfile.write(doc.toprettyxml(indent=' ', encoding='utf-8')) - else: - outfile.write( - doc.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')) - outfile.flush() - - -_helpers.SPECIAL_FLAGS = FlagValues() diff --git a/third_party/py/gflags/gflags/third_party/__init__.py b/third_party/py/gflags/gflags/third_party/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/third_party/py/gflags/gflags/third_party/pep257/LICENSE b/third_party/py/gflags/gflags/third_party/pep257/LICENSE deleted file mode 100644 index 9bf5acf7fb..0000000000 --- a/third_party/py/gflags/gflags/third_party/pep257/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -From "PEP 257 -- Docstring Conventions": - -Copyright -This document has been placed in the public domain. diff --git a/third_party/py/gflags/gflags/third_party/pep257/__init__.py b/third_party/py/gflags/gflags/third_party/pep257/__init__.py deleted file mode 100644 index e9443f8afc..0000000000 --- a/third_party/py/gflags/gflags/third_party/pep257/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -def trim(docstring): - """Removes indentation from triple-quoted strings. - - This is the function specified in PEP 257 to handle docstrings: - http://www.python.org/dev/peps/pep-0257/ - """ - if not docstring: - return '' - - # Since Python 3 does not support sys.maxint so we use and arbitrary - # large integer instead. - maxint = 1 << 32 - - # Convert tabs to spaces (following the normal Python rules) - # and split into a list of lines: - lines = docstring.expandtabs().splitlines() - - # Determine minimum indentation (first line doesn't count): - indent = maxint - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < maxint: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - # Return a single string: - return '\n'.join(trimmed) diff --git a/third_party/py/gflags/gflags/validators.py b/third_party/py/gflags/gflags/validators.py deleted file mode 100644 index 79d10f92a5..0000000000 --- a/third_party/py/gflags/gflags/validators.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python -# Copyright 2014 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. - -"""Module to enforce different constraints on flags. - -Instead of importing this module directly, it's preferable to import the -flags package and use the aliases defined at the package level. - -A validator represents an invariant, enforced over a one or more flags. -See 'FLAGS VALIDATORS' in the flags module's docstring for a usage manual. -""" - -__author__ = 'olexiy@google.com (Olexiy Oryeshko)' - - -import exceptions - - -# TODO(yileiyang): Remove this. -Error = exceptions.ValidationError # pylint: disable=invalid-name - - -class Validator(object): - """Base class for flags validators. - - Users should NOT overload these classes, and use gflags.Register... - methods instead. - """ - - # Used to assign each validator an unique insertion_index - validators_count = 0 - - def __init__(self, checker, message): - """Constructor to create all validators. - - Args: - checker: function to verify the constraint. - Input of this method varies, see SingleFlagValidator and - multi_flags_validator for a detailed description. - message: string, error message to be shown to the user - """ - self.checker = checker - self.message = message - Validator.validators_count += 1 - # Used to assert validators in the order they were registered (CL/18694236) - self.insertion_index = Validator.validators_count - - def verify(self, flag_values): - """Verify that constraint is satisfied. - - flags library calls this method to verify Validator's constraint. - Args: - flag_values: gflags.FlagValues, containing all flags - Raises: - Error: if constraint is not satisfied. - """ - param = self._get_input_to_checker_function(flag_values) - if not self.checker(param): - raise exceptions.ValidationError(self.message) - - def get_flags_names(self): - """Return the names of the flags checked by this validator. - - Returns: - [string], names of the flags - """ - raise NotImplementedError('This method should be overloaded') - - def print_flags_with_values(self, flag_values): - raise NotImplementedError('This method should be overloaded') - - def _get_input_to_checker_function(self, flag_values): - """Given flag values, construct the input to be given to checker. - - Args: - flag_values: gflags.FlagValues, containing all flags. - Returns: - Return type depends on the specific validator. - """ - raise NotImplementedError('This method should be overloaded') - - -class SingleFlagValidator(Validator): - """Validator behind register_validator() method. - - Validates that a single flag passes its checker function. The checker function - takes the flag value and returns True (if value looks fine) or, if flag value - is not valid, either returns False or raises an Exception. - """ - - def __init__(self, flag_name, checker, message): - """Constructor. - - Args: - flag_name: string, name of the flag. - checker: function to verify the validator. - input - value of the corresponding flag (string, boolean, etc). - output - Boolean. Must return True if validator constraint is satisfied. - If constraint is not satisfied, it should either return False or - raise Error. - message: string, error message to be shown to the user if validator's - condition is not satisfied - """ - super(SingleFlagValidator, self).__init__(checker, message) - self.flag_name = flag_name - - def get_flags_names(self): - return [self.flag_name] - - def print_flags_with_values(self, flag_values): - return 'flag --%s=%s' % (self.flag_name, flag_values[self.flag_name].value) - - def _get_input_to_checker_function(self, flag_values): - """Given flag values, construct the input to be given to checker. - - Args: - flag_values: gflags.FlagValues - Returns: - value of the corresponding flag. - """ - return flag_values[self.flag_name].value - - -class MultiFlagsValidator(Validator): - """Validator behind register_multi_flags_validator method. - - Validates that flag values pass their common checker function. The checker - function takes flag values and returns True (if values look fine) or, - if values are not valid, either returns False or raises an Exception. - """ - - def __init__(self, flag_names, checker, message): - """Constructor. - - Args: - flag_names: [string], containing names of the flags used by checker. - checker: function to verify the validator. - input - dictionary, with keys() being flag_names, and value for each - key being the value of the corresponding flag (string, boolean, etc). - output - Boolean. Must return True if validator constraint is satisfied. - If constraint is not satisfied, it should either return False or - raise Error. - message: string, error message to be shown to the user if validator's - condition is not satisfied - """ - super(MultiFlagsValidator, self).__init__(checker, message) - self.flag_names = flag_names - - def _get_input_to_checker_function(self, flag_values): - """Given flag values, construct the input to be given to checker. - - Args: - flag_values: gflags.FlagValues - Returns: - dictionary, with keys() being self.lag_names, and value for each key - being the value of the corresponding flag (string, boolean, etc). - """ - return dict([key, flag_values[key].value] for key in self.flag_names) - - def print_flags_with_values(self, flag_values): - prefix = 'flags ' - flags_with_values = [] - for key in self.flag_names: - flags_with_values.append('%s=%s' % (key, flag_values[key].value)) - return prefix + ', '.join(flags_with_values) - - def get_flags_names(self): - return self.flag_names diff --git a/third_party/py/gflags/gflags2man.py b/third_party/py/gflags/gflags2man.py old mode 100644 new mode 100755 diff --git a/third_party/py/gflags/gflags_validators.py b/third_party/py/gflags/gflags_validators.py new file mode 100755 index 0000000000..d83058d50f --- /dev/null +++ b/third_party/py/gflags/gflags_validators.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# Copyright (c) 2010, 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. + +"""Module to enforce different constraints on flags. + +A validator represents an invariant, enforced over a one or more flags. +See 'FLAGS VALIDATORS' in gflags.py's docstring for a usage manual. +""" + +__author__ = 'olexiy@google.com (Olexiy Oryeshko)' + + +class Error(Exception): + """Thrown If validator constraint is not satisfied.""" + + +class Validator(object): + """Base class for flags validators. + + Users should NOT overload these classes, and use gflags.Register... + methods instead. + """ + + # Used to assign each validator an unique insertion_index + validators_count = 0 + + def __init__(self, checker, message): + """Constructor to create all validators. + + Args: + checker: function to verify the constraint. + Input of this method varies, see SimpleValidator and + DictionaryValidator for a detailed description. + message: string, error message to be shown to the user + """ + self.checker = checker + self.message = message + Validator.validators_count += 1 + # Used to assert validators in the order they were registered (CL/18694236) + self.insertion_index = Validator.validators_count + + def Verify(self, flag_values): + """Verify that constraint is satisfied. + + flags library calls this method to verify Validator's constraint. + Args: + flag_values: gflags.FlagValues, containing all flags + Raises: + Error: if constraint is not satisfied. + """ + param = self._GetInputToCheckerFunction(flag_values) + if not self.checker(param): + raise Error(self.message) + + def GetFlagsNames(self): + """Return the names of the flags checked by this validator. + + Returns: + [string], names of the flags + """ + raise NotImplementedError('This method should be overloaded') + + def PrintFlagsWithValues(self, flag_values): + raise NotImplementedError('This method should be overloaded') + + def _GetInputToCheckerFunction(self, flag_values): + """Given flag values, construct the input to be given to checker. + + Args: + flag_values: gflags.FlagValues, containing all flags. + Returns: + Return type depends on the specific validator. + """ + raise NotImplementedError('This method should be overloaded') + + +class SimpleValidator(Validator): + """Validator behind RegisterValidator() method. + + Validates that a single flag passes its checker function. The checker function + takes the flag value and returns True (if value looks fine) or, if flag value + is not valid, either returns False or raises an Exception.""" + def __init__(self, flag_name, checker, message): + """Constructor. + + Args: + flag_name: string, name of the flag. + checker: function to verify the validator. + input - value of the corresponding flag (string, boolean, etc). + output - Boolean. Must return True if validator constraint is satisfied. + If constraint is not satisfied, it should either return False or + raise Error. + message: string, error message to be shown to the user if validator's + condition is not satisfied + """ + super(SimpleValidator, self).__init__(checker, message) + self.flag_name = flag_name + + def GetFlagsNames(self): + return [self.flag_name] + + def PrintFlagsWithValues(self, flag_values): + return 'flag --%s=%s' % (self.flag_name, flag_values[self.flag_name].value) + + def _GetInputToCheckerFunction(self, flag_values): + """Given flag values, construct the input to be given to checker. + + Args: + flag_values: gflags.FlagValues + Returns: + value of the corresponding flag. + """ + return flag_values[self.flag_name].value + + +class DictionaryValidator(Validator): + """Validator behind RegisterDictionaryValidator method. + + Validates that flag values pass their common checker function. The checker + function takes flag values and returns True (if values look fine) or, + if values are not valid, either returns False or raises an Exception. + """ + def __init__(self, flag_names, checker, message): + """Constructor. + + Args: + flag_names: [string], containing names of the flags used by checker. + checker: function to verify the validator. + input - dictionary, with keys() being flag_names, and value for each + key being the value of the corresponding flag (string, boolean, etc). + output - Boolean. Must return True if validator constraint is satisfied. + If constraint is not satisfied, it should either return False or + raise Error. + message: string, error message to be shown to the user if validator's + condition is not satisfied + """ + super(DictionaryValidator, self).__init__(checker, message) + self.flag_names = flag_names + + def _GetInputToCheckerFunction(self, flag_values): + """Given flag values, construct the input to be given to checker. + + Args: + flag_values: gflags.FlagValues + Returns: + dictionary, with keys() being self.lag_names, and value for each key + being the value of the corresponding flag (string, boolean, etc). + """ + return dict([key, flag_values[key].value] for key in self.flag_names) + + def PrintFlagsWithValues(self, flag_values): + prefix = 'flags ' + flags_with_values = [] + for key in self.flag_names: + flags_with_values.append('%s=%s' % (key, flag_values[key].value)) + return prefix + ', '.join(flags_with_values) + + def GetFlagsNames(self): + return self.flag_names diff --git a/third_party/py/gflags/python_gflags.egg-info/PKG-INFO b/third_party/py/gflags/python_gflags.egg-info/PKG-INFO new file mode 100644 index 0000000000..faab7198f2 --- /dev/null +++ b/third_party/py/gflags/python_gflags.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: python-gflags +Version: 2.0 +Summary: Google Commandline Flags Module +Home-page: http://code.google.com/p/python-gflags +Author: Google Inc. and others +Author-email: google-gflags@googlegroups.com +License: BSD +Description: UNKNOWN +Platform: UNKNOWN diff --git a/third_party/py/gflags/python_gflags.egg-info/SOURCES.txt b/third_party/py/gflags/python_gflags.egg-info/SOURCES.txt new file mode 100644 index 0000000000..e6068dfde1 --- /dev/null +++ b/third_party/py/gflags/python_gflags.egg-info/SOURCES.txt @@ -0,0 +1,30 @@ +AUTHORS +COPYING +ChangeLog +MANIFEST.in +Makefile +NEWS +README +gflags.py +gflags2man.py +gflags_validators.py +setup.py +debian/README +debian/changelog +debian/compat +debian/control +debian/copyright +debian/docs +debian/rules +python_gflags.egg-info/PKG-INFO +python_gflags.egg-info/SOURCES.txt +python_gflags.egg-info/dependency_links.txt +python_gflags.egg-info/top_level.txt +tests/gflags_googletest.py +tests/gflags_helpxml_test.py +tests/gflags_unittest.py +tests/gflags_validators_test.py +tests/flags_modules_for_testing/__init__.py +tests/flags_modules_for_testing/module_bar.py +tests/flags_modules_for_testing/module_baz.py +tests/flags_modules_for_testing/module_foo.py \ No newline at end of file diff --git a/third_party/py/gflags/python_gflags.egg-info/dependency_links.txt b/third_party/py/gflags/python_gflags.egg-info/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/third_party/py/gflags/python_gflags.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/third_party/py/gflags/python_gflags.egg-info/top_level.txt b/third_party/py/gflags/python_gflags.egg-info/top_level.txt new file mode 100644 index 0000000000..93c1fcdc74 --- /dev/null +++ b/third_party/py/gflags/python_gflags.egg-info/top_level.txt @@ -0,0 +1,2 @@ +gflags +gflags_validators diff --git a/third_party/py/gflags/setup.cfg b/third_party/py/gflags/setup.cfg new file mode 100644 index 0000000000..861a9f5542 --- /dev/null +++ b/third_party/py/gflags/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/third_party/py/gflags/setup.py b/third_party/py/gflags/setup.py old mode 100644 new mode 100755 index 663121a7d4..573db2d410 --- a/third_party/py/gflags/setup.py +++ b/third_party/py/gflags/setup.py @@ -1,5 +1,7 @@ #!/usr/bin/env python -# Copyright 2007 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 @@ -27,26 +29,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Setup module for python-gflags.""" - -from distutils.core import setup +from setuptools import setup setup(name='python-gflags', - version='3.1.0', + version='2.0', description='Google Commandline Flags Module', license='BSD', author='Google Inc. and others', author_email='google-gflags@googlegroups.com', - url='https://github.com/google/python-gflags', - packages=['gflags', 'gflags.third_party', 'gflags.third_party.pep257'], - data_files=[('bin', ['gflags2man.py'])], - requires=['six'], - classifiers=[ - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - ] - ) + url='http://code.google.com/p/python-gflags', + py_modules=["gflags", "gflags_validators"], + data_files=[("bin", ["gflags2man.py"])], + include_package_data=True, + ) diff --git a/third_party/py/gflags/tests/flags_modules_for_testing/__init__.py b/third_party/py/gflags/tests/flags_modules_for_testing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py b/third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py new file mode 100755 index 0000000000..230627f23a --- /dev/null +++ b/third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, 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. + + +"""Auxiliary module for testing gflags.py. + +The purpose of this module is to define a few flags. We want to make +sure the unit tests for gflags.py involve more than one module. +""" + +__author__ = 'salcianu@google.com (Alex Salcianu)' + +__pychecker__ = 'no-local' # for unittest + +import gflags + +FLAGS = gflags.FLAGS + + +def DefineFlags(flag_values=FLAGS): + """Defines some flags. + + Args: + flag_values: The FlagValues object we want to register the flags + with. + """ + # The 'tmod_bar_' prefix (short for 'test_module_bar') ensures there + # is no name clash with the existing flags. + gflags.DEFINE_boolean('tmod_bar_x', True, 'Boolean flag.', + flag_values=flag_values) + gflags.DEFINE_string('tmod_bar_y', 'default', 'String flag.', + flag_values=flag_values) + gflags.DEFINE_boolean('tmod_bar_z', False, + 'Another boolean flag from module bar.', + flag_values=flag_values) + gflags.DEFINE_integer('tmod_bar_t', 4, 'Sample int flag.', + flag_values=flag_values) + gflags.DEFINE_integer('tmod_bar_u', 5, 'Sample int flag.', + flag_values=flag_values) + gflags.DEFINE_integer('tmod_bar_v', 6, 'Sample int flag.', + flag_values=flag_values) + + +def RemoveOneFlag(flag_name, flag_values=FLAGS): + """Removes the definition of one flag from gflags.FLAGS. + + Note: if the flag is not defined in gflags.FLAGS, this function does + not do anything (in particular, it does not raise any exception). + + Motivation: We use this function for cleanup *after* a test: if + there was a failure during a test and not all flags were declared, + we do not want the cleanup code to crash. + + Args: + flag_name: A string, the name of the flag to delete. + flag_values: The FlagValues object we remove the flag from. + """ + if flag_name in flag_values.FlagDict(): + flag_values.__delattr__(flag_name) + + +def NamesOfDefinedFlags(): + """Returns: List of names of the flags declared in this module.""" + return ['tmod_bar_x', + 'tmod_bar_y', + 'tmod_bar_z', + 'tmod_bar_t', + 'tmod_bar_u', + 'tmod_bar_v'] + + +def RemoveFlags(flag_values=FLAGS): + """Deletes the flag definitions done by the above DefineFlags(). + + Args: + flag_values: The FlagValues object we remove the flags from. + """ + for flag_name in NamesOfDefinedFlags(): + RemoveOneFlag(flag_name, flag_values=flag_values) + + +def GetModuleName(): + """Uses gflags._GetCallingModule() to return the name of this module. + + For checking that _GetCallingModule works as expected. + + Returns: + A string, the name of this module. + """ + # Calling the protected _GetCallingModule generates a lint warning, + # but we do not have any other alternative to test that function. + return gflags._GetCallingModule() + + +def ExecuteCode(code, global_dict): + """Executes some code in a given global environment. + + For testing of _GetCallingModule. + + Args: + code: A string, the code to be executed. + global_dict: A dictionary, the global environment that code should + be executed in. + """ + # Indeed, using exec generates a lint warning. But some user code + # actually uses exec, and we have to test for it ... + exec code in global_dict diff --git a/third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py b/third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py new file mode 100755 index 0000000000..2719c950ad --- /dev/null +++ b/third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, 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. + +"""Auxiliary module for testing gflags.py. + +The purpose of this module is to test the behavior of flags that are defined +before main() executes. +""" + + + + +import gflags + +FLAGS = gflags.FLAGS + +gflags.DEFINE_boolean('tmod_baz_x', True, 'Boolean flag.') diff --git a/third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py b/third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py new file mode 100755 index 0000000000..760a37cc7b --- /dev/null +++ b/third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# Copyright (c) 2009, 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. + +"""Auxiliary module for testing gflags.py. + +The purpose of this module is to define a few flags, and declare some +other flags as being important. We want to make sure the unit tests +for gflags.py involve more than one module. +""" + +__author__ = 'salcianu@google.com (Alex Salcianu)' + +__pychecker__ = 'no-local' # for unittest + +import gflags +from flags_modules_for_testing import module_bar + +FLAGS = gflags.FLAGS + + +DECLARED_KEY_FLAGS = ['tmod_bar_x', 'tmod_bar_z', 'tmod_bar_t', + # Special (not user-defined) flag: + 'flagfile'] + + +def DefineFlags(flag_values=FLAGS): + """Defines a few flags.""" + module_bar.DefineFlags(flag_values=flag_values) + # The 'tmod_foo_' prefix (short for 'test_module_foo') ensures that we + # have no name clash with existing flags. + gflags.DEFINE_boolean('tmod_foo_bool', True, 'Boolean flag from module foo.', + flag_values=flag_values) + gflags.DEFINE_string('tmod_foo_str', 'default', 'String flag.', + flag_values=flag_values) + gflags.DEFINE_integer('tmod_foo_int', 3, 'Sample int flag.', + flag_values=flag_values) + + +def DeclareKeyFlags(flag_values=FLAGS): + """Declares a few key flags.""" + for flag_name in DECLARED_KEY_FLAGS: + gflags.DECLARE_key_flag(flag_name, flag_values=flag_values) + + +def DeclareExtraKeyFlags(flag_values=FLAGS): + """Declares some extra key flags.""" + gflags.ADOPT_module_key_flags(module_bar, flag_values=flag_values) + + +def NamesOfDefinedFlags(): + """Returns: list of names of flags defined by this module.""" + return ['tmod_foo_bool', 'tmod_foo_str', 'tmod_foo_int'] + + +def NamesOfDeclaredKeyFlags(): + """Returns: list of names of key flags for this module.""" + return NamesOfDefinedFlags() + DECLARED_KEY_FLAGS + + +def NamesOfDeclaredExtraKeyFlags(): + """Returns the list of names of additional key flags for this module. + + These are the flags that became key for this module only as a result + of a call to DeclareExtraKeyFlags() above. I.e., the flags declared + by module_bar, that were not already declared as key for this + module. + + Returns: + The list of names of additional key flags for this module. + """ + names_of_extra_key_flags = list(module_bar.NamesOfDefinedFlags()) + for flag_name in NamesOfDeclaredKeyFlags(): + while flag_name in names_of_extra_key_flags: + names_of_extra_key_flags.remove(flag_name) + return names_of_extra_key_flags + + +def RemoveFlags(flag_values=FLAGS): + """Deletes the flag definitions done by the above DefineFlags().""" + for flag_name in NamesOfDefinedFlags(): + module_bar.RemoveOneFlag(flag_name, flag_values=flag_values) + module_bar.RemoveFlags(flag_values=flag_values) + + +def GetModuleName(): + """Uses gflags._GetCallingModule() to return the name of this module. + + For checking that _GetCallingModule works as expected. + + Returns: + A string, the name of this module. + """ + # Calling the protected _GetCallingModule generates a lint warning, + # but we do not have any other alternative to test that function. + return gflags._GetCallingModule() + + +def DuplicateFlags(flagnames=None): + """Returns a new FlagValues object with the requested flagnames. + + Used to test DuplicateFlagError detection. + + Args: + flagnames: str, A list of flag names to create. + + Returns: + A FlagValues object with one boolean flag for each name in flagnames. + """ + flag_values = gflags.FlagValues() + for name in flagnames: + gflags.DEFINE_boolean(name, False, 'Flag named %s' % (name,), + flag_values=flag_values) + return flag_values diff --git a/third_party/py/gflags/tests/gflags_googletest.py b/third_party/py/gflags/tests/gflags_googletest.py new file mode 100644 index 0000000000..9ae614ce80 --- /dev/null +++ b/third_party/py/gflags/tests/gflags_googletest.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# Copyright (c) 2011, 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. + +"""Some simple additions to the unittest framework useful for gflags testing.""" + + + +import re +import unittest + + +def Sorted(lst): + """Equivalent of sorted(), but not dependent on python version.""" + sorted_list = lst[:] + sorted_list.sort() + return sorted_list + + +def MultiLineEqual(expected, actual): + """Returns True if expected == actual, or returns False and logs.""" + if actual == expected: + return True + + print "Error: FLAGS.MainModuleHelp() didn't return the expected result." + print "Got:" + print actual + print "[End of got]" + + actual_lines = actual.split("\n") + expected_lines = expected.split("\n") + + num_actual_lines = len(actual_lines) + num_expected_lines = len(expected_lines) + + if num_actual_lines != num_expected_lines: + print "Number of actual lines = %d, expected %d" % ( + num_actual_lines, num_expected_lines) + + num_to_match = min(num_actual_lines, num_expected_lines) + + for i in range(num_to_match): + if actual_lines[i] != expected_lines[i]: + print "One discrepancy: Got:" + print actual_lines[i] + print "Expected:" + print expected_lines[i] + break + else: + # If we got here, found no discrepancy, print first new line. + if num_actual_lines > num_expected_lines: + print "New help line:" + print actual_lines[num_expected_lines] + elif num_expected_lines > num_actual_lines: + print "Missing expected help line:" + print expected_lines[num_actual_lines] + else: + print "Bug in this test -- discrepancy detected but not found." + + return False + + +class TestCase(unittest.TestCase): + def assertListEqual(self, list1, list2): + """Asserts that, when sorted, list1 and list2 are identical.""" + # This exists in python 2.7, but not previous versions. Use the + # built-in version if possible. + if hasattr(unittest.TestCase, "assertListEqual"): + unittest.TestCase.assertListEqual(self, Sorted(list1), Sorted(list2)) + else: + self.assertEqual(Sorted(list1), Sorted(list2)) + + def assertMultiLineEqual(self, expected, actual): + # This exists in python 2.7, but not previous versions. Use the + # built-in version if possible. + if hasattr(unittest.TestCase, "assertMultiLineEqual"): + unittest.TestCase.assertMultiLineEqual(self, expected, actual) + else: + self.assertTrue(MultiLineEqual(expected, actual)) + + def assertRaisesWithRegexpMatch(self, exception, regexp, fn, *args, **kwargs): + try: + fn(*args, **kwargs) + except exception, why: + self.assertTrue(re.search(regexp, str(why)), + "'%s' does not match '%s'" % (regexp, why)) + return + self.fail(exception.__name__ + " not raised") + + +def main(): + unittest.main() diff --git a/third_party/py/gflags/tests/gflags_helpxml_test.py b/third_party/py/gflags/tests/gflags_helpxml_test.py new file mode 100755 index 0000000000..fd78004b73 --- /dev/null +++ b/third_party/py/gflags/tests/gflags_helpxml_test.py @@ -0,0 +1,535 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, 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. + +"""Unit tests for the XML-format help generated by the gflags.py module.""" + +__author__ = 'salcianu@google.com (Alex Salcianu)' + + +import string +import StringIO +import sys +import xml.dom.minidom +import xml.sax.saxutils +import gflags_googletest as googletest +import gflags +from flags_modules_for_testing import module_bar + + +class _MakeXMLSafeTest(googletest.TestCase): + + def _Check(self, s, expected_output): + self.assertEqual(gflags._MakeXMLSafe(s), expected_output) + + def testMakeXMLSafe(self): + self._Check('plain text', 'plain text') + self._Check('(x < y) && (a >= b)', + '(x < y) && (a >= b)') + # Some characters with ASCII code < 32 are illegal in XML 1.0 and + # are removed by us. However, '\n', '\t', and '\r' are legal. + self._Check('\x09\x0btext \x02 with\x0dsome \x08 good & bad chars', + '\ttext with\rsome good & bad chars') + + +def _ListSeparatorsInXMLFormat(separators, indent=''): + """Generates XML encoding of a list of list separators. + + Args: + separators: A list of list separators. Usually, this should be a + string whose characters are the valid list separators, e.g., ',' + means that both comma (',') and space (' ') are valid list + separators. + indent: A string that is added at the beginning of each generated + XML element. + + Returns: + A string. + """ + result = '' + separators = list(separators) + separators.sort() + for sep_char in separators: + result += ('%s%s\n' % + (indent, repr(sep_char))) + return result + + +class WriteFlagHelpInXMLFormatTest(googletest.TestCase): + """Test the XML-format help for a single flag at a time. + + There is one test* method for each kind of DEFINE_* declaration. + """ + + def setUp(self): + # self.fv is a FlagValues object, just like gflags.FLAGS. Each + # test registers one flag with this FlagValues. + self.fv = gflags.FlagValues() + + def _CheckFlagHelpInXML(self, flag_name, module_name, + expected_output, is_key=False): + # StringIO.StringIO is a file object that writes into a memory string. + sio = StringIO.StringIO() + flag_obj = self.fv[flag_name] + flag_obj.WriteInfoInXMLFormat(sio, module_name, is_key=is_key, indent=' ') + self.assertMultiLineEqual(sio.getvalue(), expected_output) + sio.close() + + def testFlagHelpInXML_Int(self): + gflags.DEFINE_integer('index', 17, 'An integer flag', flag_values=self.fv) + expected_output_pattern = ( + ' \n' + ' module.name\n' + ' index\n' + ' An integer flag\n' + ' 17\n' + ' %d\n' + ' int\n' + ' \n') + self._CheckFlagHelpInXML('index', 'module.name', + expected_output_pattern % 17) + # Check that the output is correct even when the current value of + # a flag is different from the default one. + self.fv['index'].value = 20 + self._CheckFlagHelpInXML('index', 'module.name', + expected_output_pattern % 20) + + def testFlagHelpInXML_IntWithBounds(self): + gflags.DEFINE_integer('nb_iters', 17, 'An integer flag', + lower_bound=5, upper_bound=27, + flag_values=self.fv) + expected_output = ( + ' \n' + ' yes\n' + ' module.name\n' + ' nb_iters\n' + ' An integer flag\n' + ' 17\n' + ' 17\n' + ' int\n' + ' 5\n' + ' 27\n' + ' \n') + self._CheckFlagHelpInXML('nb_iters', 'module.name', + expected_output, is_key=True) + + def testFlagHelpInXML_String(self): + gflags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.', + flag_values=self.fv) + expected_output = ( + ' \n' + ' simple_module\n' + ' file_path\n' + ' A test string flag.\n' + ' /path/to/my/dir\n' + ' /path/to/my/dir\n' + ' string\n' + ' \n') + self._CheckFlagHelpInXML('file_path', 'simple_module', + expected_output) + + def testFlagHelpInXML_StringWithXMLIllegalChars(self): + gflags.DEFINE_string('file_path', '/path/to/\x08my/dir', + 'A test string flag.', flag_values=self.fv) + # '\x08' is not a legal character in XML 1.0 documents. Our + # current code purges such characters from the generated XML. + expected_output = ( + ' \n' + ' simple_module\n' + ' file_path\n' + ' A test string flag.\n' + ' /path/to/my/dir\n' + ' /path/to/my/dir\n' + ' string\n' + ' \n') + self._CheckFlagHelpInXML('file_path', 'simple_module', + expected_output) + + def testFlagHelpInXML_Boolean(self): + gflags.DEFINE_boolean('use_hack', False, 'Use performance hack', + flag_values=self.fv) + expected_output = ( + ' \n' + ' yes\n' + ' a_module\n' + ' use_hack\n' + ' Use performance hack\n' + ' false\n' + ' false\n' + ' bool\n' + ' \n') + self._CheckFlagHelpInXML('use_hack', 'a_module', + expected_output, is_key=True) + + def testFlagHelpInXML_Enum(self): + gflags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'], + 'Compiler version to use.', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' cc_version\n' + ' <stable|experimental>: ' + 'Compiler version to use.\n' + ' stable\n' + ' stable\n' + ' string enum\n' + ' stable\n' + ' experimental\n' + ' \n') + self._CheckFlagHelpInXML('cc_version', 'tool', expected_output) + + def testFlagHelpInXML_CommaSeparatedList(self): + gflags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip', + 'Files to process.', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' files\n' + ' Files to process.\n' + ' a.cc,a.h,archive/old.zip\n' + ' [\'a.cc\', \'a.h\', \'archive/old.zip\']\n' + ' comma separated list of strings\n' + ' \',\'\n' + ' \n') + self._CheckFlagHelpInXML('files', 'tool', expected_output) + + def testListAsDefaultArgument_CommaSeparatedList(self): + gflags.DEFINE_list('allow_users', ['alice', 'bob'], + 'Users with access.', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' allow_users\n' + ' Users with access.\n' + ' alice,bob\n' + ' [\'alice\', \'bob\']\n' + ' comma separated list of strings\n' + ' \',\'\n' + ' \n') + self._CheckFlagHelpInXML('allow_users', 'tool', expected_output) + + def testFlagHelpInXML_SpaceSeparatedList(self): + gflags.DEFINE_spaceseplist('dirs', 'src libs bin', + 'Directories to search.', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' dirs\n' + ' Directories to search.\n' + ' src libs bin\n' + ' [\'src\', \'libs\', \'bin\']\n' + ' whitespace separated list of strings\n' + 'LIST_SEPARATORS' + ' \n').replace('LIST_SEPARATORS', + _ListSeparatorsInXMLFormat(string.whitespace, + indent=' ')) + self._CheckFlagHelpInXML('dirs', 'tool', expected_output) + + def testFlagHelpInXML_MultiString(self): + gflags.DEFINE_multistring('to_delete', ['a.cc', 'b.h'], + 'Files to delete', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' to_delete\n' + ' Files to delete;\n ' + 'repeat this option to specify a list of values\n' + ' [\'a.cc\', \'b.h\']\n' + ' [\'a.cc\', \'b.h\']\n' + ' multi string\n' + ' \n') + self._CheckFlagHelpInXML('to_delete', 'tool', expected_output) + + def testFlagHelpInXML_MultiInt(self): + gflags.DEFINE_multi_int('cols', [5, 7, 23], + 'Columns to select', flag_values=self.fv) + expected_output = ( + ' \n' + ' tool\n' + ' cols\n' + ' Columns to select;\n ' + 'repeat this option to specify a list of values\n' + ' [5, 7, 23]\n' + ' [5, 7, 23]\n' + ' multi int\n' + ' \n') + self._CheckFlagHelpInXML('cols', 'tool', expected_output) + + +# The next EXPECTED_HELP_XML_* constants are parts of a template for +# the expected XML output from WriteHelpInXMLFormatTest below. When +# we assemble these parts into a single big string, we'll take into +# account the ordering between the name of the main module and the +# name of module_bar. Next, we'll fill in the docstring for this +# module (%(usage_doc)s), the name of the main module +# (%(main_module_name)s) and the name of the module module_bar +# (%(module_bar_name)s). See WriteHelpInXMLFormatTest below. +# +# NOTE: given the current implementation of _GetMainModule(), we +# already know the ordering between the main module and module_bar. +# However, there is no guarantee that _GetMainModule will never be +# changed in the future (especially since it's far from perfect). +EXPECTED_HELP_XML_START = """\ + + + gflags_helpxml_test.py + %(usage_doc)s +""" + +EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE = """\ + + yes + %(main_module_name)s + allow_users + Users with access. + alice,bob + ['alice', 'bob'] + comma separated list of strings + ',' + + + yes + %(main_module_name)s + cc_version + <stable|experimental>: Compiler version to use. + stable + stable + string enum + stable + experimental + + + yes + %(main_module_name)s + cols + Columns to select; + repeat this option to specify a list of values + [5, 7, 23] + [5, 7, 23] + multi int + + + yes + %(main_module_name)s + dirs + Directories to create. + src libs bins + ['src', 'libs', 'bins'] + whitespace separated list of strings +%(whitespace_separators)s + + yes + %(main_module_name)s + file_path + A test string flag. + /path/to/my/dir + /path/to/my/dir + string + + + yes + %(main_module_name)s + files + Files to process. + a.cc,a.h,archive/old.zip + ['a.cc', 'a.h', 'archive/old.zip'] + comma separated list of strings + \',\' + + + yes + %(main_module_name)s + index + An integer flag + 17 + 17 + int + + + yes + %(main_module_name)s + nb_iters + An integer flag + 17 + 17 + int + 5 + 27 + + + yes + %(main_module_name)s + to_delete + Files to delete; + repeat this option to specify a list of values + ['a.cc', 'b.h'] + ['a.cc', 'b.h'] + multi string + + + yes + %(main_module_name)s + use_hack + Use performance hack + false + false + bool + +""" + +EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR = """\ + + %(module_bar_name)s + tmod_bar_t + Sample int flag. + 4 + 4 + int + + + yes + %(module_bar_name)s + tmod_bar_u + Sample int flag. + 5 + 5 + int + + + %(module_bar_name)s + tmod_bar_v + Sample int flag. + 6 + 6 + int + + + %(module_bar_name)s + tmod_bar_x + Boolean flag. + true + true + bool + + + %(module_bar_name)s + tmod_bar_y + String flag. + default + default + string + + + yes + %(module_bar_name)s + tmod_bar_z + Another boolean flag from module bar. + false + false + bool + +""" + +EXPECTED_HELP_XML_END = """\ + +""" + + +class WriteHelpInXMLFormatTest(googletest.TestCase): + """Big test of FlagValues.WriteHelpInXMLFormat, with several flags.""" + + def testWriteHelpInXMLFormat(self): + fv = gflags.FlagValues() + # Since these flags are defined by the top module, they are all key. + gflags.DEFINE_integer('index', 17, 'An integer flag', flag_values=fv) + gflags.DEFINE_integer('nb_iters', 17, 'An integer flag', + lower_bound=5, upper_bound=27, flag_values=fv) + gflags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.', + flag_values=fv) + gflags.DEFINE_boolean('use_hack', False, 'Use performance hack', + flag_values=fv) + gflags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'], + 'Compiler version to use.', flag_values=fv) + gflags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip', + 'Files to process.', flag_values=fv) + gflags.DEFINE_list('allow_users', ['alice', 'bob'], + 'Users with access.', flag_values=fv) + gflags.DEFINE_spaceseplist('dirs', 'src libs bins', + 'Directories to create.', flag_values=fv) + gflags.DEFINE_multistring('to_delete', ['a.cc', 'b.h'], + 'Files to delete', flag_values=fv) + gflags.DEFINE_multi_int('cols', [5, 7, 23], + 'Columns to select', flag_values=fv) + # Define a few flags in a different module. + module_bar.DefineFlags(flag_values=fv) + # And declare only a few of them to be key. This way, we have + # different kinds of flags, defined in different modules, and not + # all of them are key flags. + gflags.DECLARE_key_flag('tmod_bar_z', flag_values=fv) + gflags.DECLARE_key_flag('tmod_bar_u', flag_values=fv) + + # Generate flag help in XML format in the StringIO sio. + sio = StringIO.StringIO() + fv.WriteHelpInXMLFormat(sio) + + # Check that we got the expected result. + expected_output_template = EXPECTED_HELP_XML_START + main_module_name = gflags._GetMainModule() + module_bar_name = module_bar.__name__ + + if main_module_name < module_bar_name: + expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE + expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR + else: + expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR + expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE + + expected_output_template += EXPECTED_HELP_XML_END + + # XML representation of the whitespace list separators. + whitespace_separators = _ListSeparatorsInXMLFormat(string.whitespace, + indent=' ') + expected_output = ( + expected_output_template % + {'usage_doc': sys.modules['__main__'].__doc__, + 'main_module_name': main_module_name, + 'module_bar_name': module_bar_name, + 'whitespace_separators': whitespace_separators}) + + actual_output = sio.getvalue() + self.assertMultiLineEqual(actual_output, expected_output) + + # Also check that our result is valid XML. minidom.parseString + # throws an xml.parsers.expat.ExpatError in case of an error. + xml.dom.minidom.parseString(actual_output) + + +if __name__ == '__main__': + googletest.main() diff --git a/third_party/py/gflags/tests/gflags_unittest.py b/third_party/py/gflags/tests/gflags_unittest.py new file mode 100755 index 0000000000..8e948bf36f --- /dev/null +++ b/third_party/py/gflags/tests/gflags_unittest.py @@ -0,0 +1,1949 @@ +#!/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 gflags.py module" + +__pychecker__ = "no-local" # for unittest + + +import cStringIO +import sys +import os +import shutil + +import gflags +from flags_modules_for_testing import module_foo +from flags_modules_for_testing import module_bar +from flags_modules_for_testing import module_baz + +FLAGS=gflags.FLAGS + +import gflags_googletest as googletest + +# TODO(csilvers): add a wrapper function around FLAGS(argv) that +# verifies the input is a list or tuple. This avoids bugs where we +# make argv a string instead of a list, by mistake. + +class FlagsUnitTest(googletest.TestCase): + "Flags Unit Test" + + def setUp(self): + # make sure we are using the old, stupid way of parsing flags. + FLAGS.UseGnuGetOpt(False) + + 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)" + gflags.DEFINE_integer("repeat", 4, repeatHelp, + lower_bound=0, short_name='r') + gflags.DEFINE_string("name", "Bob", "namehelp") + gflags.DEFINE_boolean("debug", 0, "debughelp") + gflags.DEFINE_boolean("q", 1, "quiet mode") + gflags.DEFINE_boolean("quack", 0, "superstring of 'q'") + gflags.DEFINE_boolean("noexec", 1, "boolean flag with no as prefix") + gflags.DEFINE_integer("x", 3, "how eXtreme to be") + gflags.DEFINE_integer("l", 0x7fffffff00000000, "how long to be") + gflags.DEFINE_list('letters', 'a,b,c', "a list of letters") + gflags.DEFINE_list('numbers', [1, 2, 3], "a list of numbers") + gflags.DEFINE_enum("kwery", None, ['who', 'what', 'why', 'where', 'when'], + "?") + + # Specify number of flags defined above. The short_name defined + # for 'repeat' counts as an extra flag. + number_defined_flags = 11 + 1 + self.assertEqual(len(FLAGS.RegisteredFlags()), + number_defined_flags + number_test_framework_flags) + + 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 == 0x7fffffff00000000, ("integer default values not set:" + + FLAGS.l) + assert FLAGS.letters == ['a', 'b', 'c'], ("list default values not set:" + + FLAGS.letters) + assert FLAGS.numbers == [1, 2, 3], ("list default values not set:" + + FLAGS.numbers) + assert FLAGS.kwery is None, ("enum default None value not set:" + + FLAGS.kwery) + + 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 repeat + assert flag_values['q'] == 1 + assert flag_values['quack'] == 0 + assert flag_values['x'] == 3 + assert flag_values['l'] == 0x7fffffff00000000 + assert flag_values['letters'] == ['a', 'b', 'c'] + assert flag_values['numbers'] == [1, 2, 3] + assert flag_values['kwery'] is None + + # 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'" + assert FLAGS['letters'].default_as_str == "'a,b,c'" + assert FLAGS['numbers'].default_as_str == "'1,2,3'" + + # 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 + self.assertEqual(len(FLAGS.RegisteredFlags()), + number_defined_flags + 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() + assert 'letters' in FLAGS.RegisteredFlags() + assert 'numbers' 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 + self.assertEqual(len(FLAGS.RegisteredFlags()), + number_defined_flags - 1 + 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 + FLAGS.Reset() + assert FLAGS['debug'].present == 0 + assert not FLAGS['debug'].value + + # Test that reset restores default value when default value is None. + argv = ('./program', '--kwery=who') + argv = FLAGS(argv) + assert len(argv) == 1, "wrong number of arguments pulled" + assert argv[0] == './program', "program name not preserved" + assert FLAGS['kwery'].present == 1 + assert FLAGS['kwery'].value == 'who' + FLAGS.Reset() + assert FLAGS['kwery'].present == 0 + assert FLAGS['kwery'].value == None + + # Test integer argument passing + argv = ('./program', '--x', '0x12345') + argv = FLAGS(argv) + self.assertEquals(FLAGS.x, 0x12345) + self.assertEquals(type(FLAGS.x), int) + + argv = ('./program', '--x', '0x1234567890ABCDEF1234567890ABCDEF') + argv = FLAGS(argv) + self.assertEquals(FLAGS.x, 0x1234567890ABCDEF1234567890ABCDEF) + 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 gflags.IllegalFlagValue: + pass + + # Test boolean argument parsing + gflags.DEFINE_boolean("test0", None, "test boolean parsing") + argv = ('./program', '--notest0') + argv = FLAGS(argv) + assert FLAGS.test0 == 0 + + gflags.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 + gflags.DEFINE_boolean("testnone", None, "test boolean parsing") + argv = ('./program',) + argv = FLAGS(argv) + assert FLAGS.testnone == None + + # Test get with default + gflags.DEFINE_boolean("testget1", None, "test parsing with defaults") + gflags.DEFINE_boolean("testget2", None, "test parsing with defaults") + gflags.DEFINE_boolean("testget3", None, "test parsing with defaults") + gflags.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'], + [],] + + gflags.DEFINE_list('testlist', '', 'test lists parsing') + gflags.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 + gflags.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', ] + gflags.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], gflags.BooleanFlag)] + + truebool_flags = ['--%s' % (name) + for name in flagnames + if isinstance(FLAGS[name], gflags.BooleanFlag) and + FLAGS.get(name, None)] + falsebool_flags = ['--no%s' % (name) + for name in flagnames + if isinstance(FLAGS[name], gflags.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(), + "--kwery None " + "--l 9223372032559808512 " + "--letters ['a', 'b', 'c'] " + "--m ['str1', 'str2'] --m_str ['str1', 'str2'] " + "--name giants " + "--numbers [1, 2, 3] " + "--repeat 3 " + "--s ['sing1'] --s_str ['sing1'] " + "" + "" + "--testget4 None --testlist [] " + "--testspacelist [] --x 10 " + "--noexec --quack " + "--test1 " + "--testget1 --tmod_baz_x " + "--no? --nodebug --nohelp --nohelpshort --nohelpxml --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(), + "--kwery None " + "--l 9223372032559808512 " + "--letters ['a', 'b', 'c'] " + "--m ['str1', 'str2', 'upd1'] " + "--m_str ['str1', 'str2', 'upd1'] " + "--name giants " + "--numbers [1, 2, 3] " + "--repeat 3 " + "--s ['upd2'] --s_str ['upd2'] " + "" + "" + "--testget4 None --testlist [] " + "--testspacelist [] --x 10 " + "--debug --noexec --quack " + "--test1 " + "--testget1 --tmod_baz_x " + "--no? --nohelp --nohelpshort --nohelpxml --noq " + "" + "--notest0 --notestget2 --notestget3 --notestnone") + + #################################### + # Test all kind of error conditions. + + # Duplicate flag detection + try: + gflags.DEFINE_boolean("run", 0, "runhelp", short_name='q') + raise AssertionError("duplicate flag detection failed") + except gflags.DuplicateFlag: + pass + + # Duplicate short flag detection + try: + gflags.DEFINE_boolean("zoom1", 0, "runhelp z1", short_name='z') + gflags.DEFINE_boolean("zoom2", 0, "runhelp z2", short_name='z') + raise AssertionError("duplicate short flag detection failed") + except gflags.DuplicateFlag, e: + self.assertTrue("The flag 'z' is defined twice. " in e.args[0]) + self.assertTrue("First from" in e.args[0]) + self.assertTrue(", Second from" in e.args[0]) + + # Duplicate mixed flag detection + try: + gflags.DEFINE_boolean("short1", 0, "runhelp s1", short_name='s') + gflags.DEFINE_boolean("s", 0, "runhelp s2") + raise AssertionError("duplicate mixed flag detection failed") + except gflags.DuplicateFlag, e: + self.assertTrue("The flag 's' is defined twice. " in e.args[0]) + self.assertTrue("First from" in e.args[0]) + self.assertTrue(", Second from" in e.args[0]) + + # Check that duplicate flag detection detects definition sites + # correctly. + flagnames = ["repeated"] + original_flags = gflags.FlagValues() + gflags.DEFINE_boolean(flagnames[0], False, "Flag about to be repeated.", + flag_values=original_flags) + duplicate_flags = module_foo.DuplicateFlags(flagnames) + try: + original_flags.AppendFlagValues(duplicate_flags) + except gflags.DuplicateFlagError, e: + self.assertTrue("flags_unittest" in str(e)) + self.assertTrue("module_foo" in str(e)) + + # Make sure allow_override works + try: + gflags.DEFINE_boolean("dup1", 0, "runhelp d11", short_name='u', + allow_override=0) + flag = FLAGS.FlagDict()['dup1'] + self.assertEqual(flag.default, 0) + + gflags.DEFINE_boolean("dup1", 1, "runhelp d12", short_name='u', + allow_override=1) + flag = FLAGS.FlagDict()['dup1'] + self.assertEqual(flag.default, 1) + except gflags.DuplicateFlag: + raise AssertionError("allow_override did not permit a flag duplication") + + # Make sure allow_override works + try: + gflags.DEFINE_boolean("dup2", 0, "runhelp d21", short_name='u', + allow_override=1) + flag = FLAGS.FlagDict()['dup2'] + self.assertEqual(flag.default, 0) + + gflags.DEFINE_boolean("dup2", 1, "runhelp d22", short_name='u', + allow_override=0) + flag = FLAGS.FlagDict()['dup2'] + self.assertEqual(flag.default, 1) + except gflags.DuplicateFlag: + raise AssertionError("allow_override did not permit a flag duplication") + + # Make sure allow_override doesn't work with None default + try: + gflags.DEFINE_boolean("dup3", 0, "runhelp d31", short_name='u3', + allow_override=0) + flag = FLAGS.FlagDict()['dup3'] + self.assertEqual(flag.default, 0) + + gflags.DEFINE_boolean("dup3", None, "runhelp d32", short_name='u3', + allow_override=1) + raise AssertionError('Cannot override a flag with a default of None') + except gflags.DuplicateFlagCannotPropagateNoneToSwig: + pass + + # Make sure that re-importing a module does not cause a DuplicateFlagError + # to be raised. + try: + sys.modules.pop( + "flags_modules_for_testing.module_baz") + import flags_modules_for_testing.module_baz + except gflags.DuplicateFlagError: + raise AssertionError("Module reimport caused flag duplication error") + + # Make sure that when we override, the help string gets updated correctly + gflags.DEFINE_boolean("dup3", 0, "runhelp d31", short_name='u', + allow_override=1) + gflags.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) + + # Make sure AppendFlagValues works + new_flags = gflags.FlagValues() + gflags.DEFINE_boolean("new1", 0, "runhelp n1", flag_values=new_flags) + gflags.DEFINE_boolean("new2", 0, "runhelp n2", flag_values=new_flags) + self.assertEqual(len(new_flags.FlagDict()), 2) + old_len = len(FLAGS.FlagDict()) + FLAGS.AppendFlagValues(new_flags) + self.assertEqual(len(FLAGS.FlagDict())-old_len, 2) + self.assertEqual("new1" in FLAGS.FlagDict(), True) + self.assertEqual("new2" in FLAGS.FlagDict(), True) + + # Then test that removing those flags works + FLAGS.RemoveFlagValues(new_flags) + self.assertEqual(len(FLAGS.FlagDict()), old_len) + self.assertFalse("new1" in FLAGS.FlagDict()) + self.assertFalse("new2" in FLAGS.FlagDict()) + + # Make sure AppendFlagValues works with flags with shortnames. + new_flags = gflags.FlagValues() + gflags.DEFINE_boolean("new3", 0, "runhelp n3", flag_values=new_flags) + gflags.DEFINE_boolean("new4", 0, "runhelp n4", flag_values=new_flags, + short_name="n4") + self.assertEqual(len(new_flags.FlagDict()), 3) + old_len = len(FLAGS.FlagDict()) + FLAGS.AppendFlagValues(new_flags) + self.assertEqual(len(FLAGS.FlagDict())-old_len, 3) + self.assertTrue("new3" in FLAGS.FlagDict()) + self.assertTrue("new4" in FLAGS.FlagDict()) + self.assertTrue("n4" in FLAGS.FlagDict()) + self.assertEqual(FLAGS.FlagDict()['n4'], FLAGS.FlagDict()['new4']) + + # Then test removing them + FLAGS.RemoveFlagValues(new_flags) + self.assertEqual(len(FLAGS.FlagDict()), old_len) + self.assertFalse("new3" in FLAGS.FlagDict()) + self.assertFalse("new4" in FLAGS.FlagDict()) + self.assertFalse("n4" in FLAGS.FlagDict()) + + # Make sure AppendFlagValues fails on duplicates + gflags.DEFINE_boolean("dup4", 0, "runhelp d41") + new_flags = gflags.FlagValues() + gflags.DEFINE_boolean("dup4", 0, "runhelp d42", flag_values=new_flags) + try: + FLAGS.AppendFlagValues(new_flags) + raise AssertionError("ignore_copy was not set but caused no exception") + except gflags.DuplicateFlag: + pass + + # Integer out of bounds + try: + argv = ('./program', '--repeat=-4') + FLAGS(argv) + raise AssertionError('integer bounds exception not raised:' + + str(FLAGS.repeat)) + except gflags.IllegalFlagValue: + pass + + # Non-integer + try: + argv = ('./program', '--repeat=2.5') + FLAGS(argv) + raise AssertionError("malformed integer value exception not raised") + except gflags.IllegalFlagValue: + pass + + # Missing required arugment + try: + argv = ('./program', '--name') + FLAGS(argv) + raise AssertionError("Flag argument required exception not raised") + except gflags.FlagsError: + pass + + # Non-boolean arguments for boolean + try: + argv = ('./program', '--debug=goofup') + FLAGS(argv) + raise AssertionError("Illegal flag value exception not raised") + except gflags.IllegalFlagValue: + pass + + try: + argv = ('./program', '--debug=42') + FLAGS(argv) + raise AssertionError("Illegal flag value exception not raised") + except gflags.IllegalFlagValue: + pass + + + # Non-numeric argument for integer flag --repeat + try: + argv = ('./program', '--repeat', 'Bob', 'extra') + FLAGS(argv) + raise AssertionError("Illegal flag value exception not raised") + except gflags.IllegalFlagValue: + pass + + # Test ModuleHelp(). + helpstr = FLAGS.ModuleHelp(module_baz) + + expected_help = "\n" + module_baz.__name__ + ":" + """ + --[no]tmod_baz_x: Boolean flag. + (default: 'true')""" + + self.assertMultiLineEqual(expected_help, helpstr) + + # Test MainModuleHelp(). This must be part of test_flags because + # it dpeends on dup1/2/3/etc being introduced first. + helpstr = FLAGS.MainModuleHelp() + + expected_help = "\n" + sys.argv[0] + ':' + """ + --[no]debug: debughelp + (default: 'false') + -u,--[no]dup1: runhelp d12 + (default: 'true') + -u,--[no]dup2: runhelp d22 + (default: 'true') + -u,--[no]dup3: runhelp d32 + (default: 'true') + --[no]dup4: runhelp d41 + (default: 'false') + --kwery: : ? + --l: how long to be + (default: '9223372032559808512') + (an integer) + --letters: a list of letters + (default: 'a,b,c') + (a comma separated list) + -m,--m_str: string option that can occur multiple times; + repeat this option to specify a list of values + (default: "['def1', 'def2']") + --name: namehelp + (default: 'Bob') + --[no]noexec: boolean flag with no as prefix + (default: 'true') + --numbers: a list of numbers + (default: '1,2,3') + (a comma separated list) + --[no]q: quiet mode + (default: 'true') + --[no]quack: superstring of 'q' + (default: 'false') + -r,--repeat: how many times to repeat (0-5) + (default: '4') + (a non-negative integer) + -s,--s_str: string option that can occur multiple times; + repeat this option to specify a list of values + (default: "['sing1']") + --[no]test0: test boolean parsing + --[no]test1: test boolean parsing + --[no]testget1: test parsing with defaults + --[no]testget2: test parsing with defaults + --[no]testget3: test parsing with defaults + --testget4: test parsing with defaults + (an integer) + --testlist: test lists parsing + (default: '') + (a comma separated list) + --[no]testnone: test boolean parsing + --testspacelist: tests space lists parsing + (default: '') + (a whitespace separated list) + --x: how eXtreme to be + (default: '3') + (an integer) + -z,--[no]zoom1: runhelp z1 + (default: 'false')""" + + # Insert the --help flags in their proper place. + help_help = """\ + -?,--[no]help: show this help + --[no]helpshort: show usage only for this module + --[no]helpxml: like --help, but generates XML output +""" + expected_help = expected_help.replace(' --kwery', + help_help + ' --kwery') + + self.assertMultiLineEqual(expected_help, helpstr) + + +class MultiNumericalFlagsTest(googletest.TestCase): + + def testMultiNumericalFlags(self): + """Test multi_int and multi_float flags.""" + + int_defaults = [77, 88,] + gflags.DEFINE_multi_int('m_int', int_defaults, + 'integer option that can occur multiple times', + short_name='mi') + self.assertListEqual(FLAGS.get('m_int', None), int_defaults) + argv = ('./program', '--m_int=-99', '--mi=101') + FLAGS(argv) + self.assertListEqual(FLAGS.get('m_int', None), [-99, 101,]) + + float_defaults = [2.2, 3] + gflags.DEFINE_multi_float('m_float', float_defaults, + 'float option that can occur multiple times', + short_name='mf') + for (expected, actual) in zip(float_defaults, FLAGS.get('m_float', None)): + self.assertAlmostEquals(expected, actual) + argv = ('./program', '--m_float=-17', '--mf=2.78e9') + FLAGS(argv) + expected_floats = [-17.0, 2.78e9] + for (expected, actual) in zip(expected_floats, FLAGS.get('m_float', None)): + self.assertAlmostEquals(expected, actual) + + def testSingleValueDefault(self): + """Test multi_int and multi_float flags with a single default value.""" + int_default = 77 + gflags.DEFINE_multi_int('m_int1', int_default, + 'integer option that can occur multiple times') + self.assertListEqual(FLAGS.get('m_int1', None), [int_default]) + + float_default = 2.2 + gflags.DEFINE_multi_float('m_float1', float_default, + 'float option that can occur multiple times') + actual = FLAGS.get('m_float1', None) + self.assertEquals(1, len(actual)) + self.assertAlmostEquals(actual[0], float_default) + + def testBadMultiNumericalFlags(self): + """Test multi_int and multi_float flags with non-parseable values.""" + + # Test non-parseable defaults. + self.assertRaisesWithRegexpMatch( + gflags.IllegalFlagValue, + 'flag --m_int2=abc: invalid literal for int\(\) with base 10: \'abc\'', + gflags.DEFINE_multi_int, 'm_int2', ['abc'], 'desc') + + self.assertRaisesWithRegexpMatch( + gflags.IllegalFlagValue, + 'flag --m_float2=abc: invalid literal for float\(\): abc', + gflags.DEFINE_multi_float, 'm_float2', ['abc'], 'desc') + + # Test non-parseable command line values. + gflags.DEFINE_multi_int('m_int2', '77', + 'integer option that can occur multiple times') + argv = ('./program', '--m_int2=def') + self.assertRaisesWithRegexpMatch( + gflags.IllegalFlagValue, + 'flag --m_int2=def: invalid literal for int\(\) with base 10: \'def\'', + FLAGS, argv) + + gflags.DEFINE_multi_float('m_float2', 2.2, + 'float option that can occur multiple times') + argv = ('./program', '--m_float2=def') + self.assertRaisesWithRegexpMatch( + gflags.IllegalFlagValue, + 'flag --m_float2=def: invalid literal for float\(\): def', + FLAGS, argv) + + +class UnicodeFlagsTest(googletest.TestCase): + """Testing proper unicode support for flags.""" + + def testUnicodeDefaultAndHelpstring(self): + gflags.DEFINE_string("unicode_str", "\xC3\x80\xC3\xBD".decode("utf-8"), + "help:\xC3\xAA".decode("utf-8")) + argv = ("./program",) + FLAGS(argv) # should not raise any exceptions + + argv = ("./program", "--unicode_str=foo") + FLAGS(argv) # should not raise any exceptions + + def testUnicodeInList(self): + gflags.DEFINE_list("unicode_list", ["abc", "\xC3\x80".decode("utf-8"), + "\xC3\xBD".decode("utf-8")], + "help:\xC3\xAB".decode("utf-8")) + argv = ("./program",) + FLAGS(argv) # should not raise any exceptions + + argv = ("./program", "--unicode_list=hello,there") + FLAGS(argv) # should not raise any exceptions + + def testXMLOutput(self): + gflags.DEFINE_string("unicode1", "\xC3\x80\xC3\xBD".decode("utf-8"), + "help:\xC3\xAC".decode("utf-8")) + gflags.DEFINE_list("unicode2", ["abc", "\xC3\x80".decode("utf-8"), + "\xC3\xBD".decode("utf-8")], + "help:\xC3\xAD".decode("utf-8")) + gflags.DEFINE_list("non_unicode", ["abc", "def", "ghi"], + "help:\xC3\xAD".decode("utf-8")) + + outfile = cStringIO.StringIO() + FLAGS.WriteHelpInXMLFormat(outfile) + actual_output = outfile.getvalue() + + # The xml output is large, so we just check parts of it. + self.assertTrue("unicode1\n" + " help:ì\n" + " Àý\n" + " Àý" + in actual_output) + self.assertTrue("unicode2\n" + " help:í\n" + " abc,À,ý\n" + " [\'abc\', u\'\\xc0\', u\'\\xfd\']" + in actual_output) + self.assertTrue("non_unicode\n" + " help:í\n" + " abc,def,ghi\n" + " [\'abc\', \'def\', \'ghi\']" + in actual_output) + + +class LoadFromFlagFileTest(googletest.TestCase): + """Testing loading flags from a file and parsing them.""" + + def setUp(self): + self.flag_values = gflags.FlagValues() + # make sure we are using the old, stupid way of parsing flags. + self.flag_values.UseGnuGetOpt(False) + gflags.DEFINE_string('UnitTestMessage1', 'Foo!', 'You Add Here.', + flag_values=self.flag_values) + gflags.DEFINE_string('UnitTestMessage2', 'Bar!', 'Hello, Sailor!', + flag_values=self.flag_values) + gflags.DEFINE_boolean('UnitTestBoolFlag', 0, 'Some Boolean thing', + flag_values=self.flag_values) + gflags.DEFINE_integer('UnitTestNumber', 12345, 'Some integer', + lower_bound=0, flag_values=self.flag_values) + gflags.DEFINE_list('UnitTestList', "1,2,3", 'Some list', + flag_values=self.flag_values) + self.files_to_delete = [] + + def tearDown(self): + self._RemoveTestFiles() + + 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') + tmp_flag_file_4 = open(tmp_path + '/UnitTestFile4.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) + # this file is unreadable + tmp_flag_file_4.write('--flagfile=%s\n' % tmp_flag_file_3.name) + tmp_flag_file_4.write('--UnitTestMessage1=setFromTempFile3\n') + tmp_flag_file_4.write('--UnitTestMessage1=setFromTempFile3\n') + os.chmod(tmp_path + '/UnitTestFile4.tst', 0) + file_list.append(tmp_flag_file_4.name) + + tmp_flag_file_1.close() + tmp_flag_file_2.close() + tmp_flag_file_3.close() + tmp_flag_file_4.close() + + self.files_to_delete = file_list + + return file_list # these are just the file names + # end SetupFiles def + + def _RemoveTestFiles(self): + """Closes the files we just created. tempfile deletes them for us """ + for file_name in self.files_to_delete: + try: + os.remove(file_name) + except OSError, e_msg: + print '%s\n, Problem deleting test file' % e_msg + #end RemoveTestFiles def + + def _ReadFlagsFromFiles(self, argv, force_gnu): + return argv[:1] + self.flag_values.ReadFlagsFromFiles(argv[1:], + force_gnu=force_gnu) + + #### Flagfile Unit Tests #### + def testMethod_flagfiles_1(self): + """ Test trivial case with no flagfile based options. """ + fake_cmd_line = 'fooScript --UnitTestBoolFlag' + fake_argv = fake_cmd_line.split(' ') + self.flag_values(fake_argv) + self.assertEqual( self.flag_values.UnitTestBoolFlag, 1) + self.assertEqual( fake_argv, self._ReadFlagsFromFiles(fake_argv, False)) + + # end testMethodOne + + def testMethod_flagfiles_2(self): + """Tests parsing one file + arguments off simulated argv""" + 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. + # Flags from the file will appear in the order order they are sepcified + # in the file, in the same position as the flagfile argument. + expected_results = ['fooScript', + '--q', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag'] + test_results = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + # end testTwo def + + def testMethod_flagfiles_3(self): + """Tests parsing nested files + arguments of simulated argv""" + 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', + '--UnitTestNumber=77', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag', + '--UnitTestMessage2=setFromTempFile2', + '--UnitTestNumber=6789a'] + test_results = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + # end testThree def + + def testMethod_flagfiles_4(self): + """Tests parsing self-referential files + arguments of simulated argv. + This test should print a warning to stderr of some sort. + """ + 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 = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + + def testMethod_flagfiles_5(self): + """Test that --flagfile parsing respects the '--' end-of-options marker.""" + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = 'fooScript --SomeFlag -- --flagfile=%s' % tmp_files[0] + fake_argv = fake_cmd_line.split(' ') + expected_results = ['fooScript', + '--SomeFlag', + '--', + '--flagfile=%s' % tmp_files[0]] + + test_results = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + + def testMethod_flagfiles_6(self): + """Test that --flagfile parsing stops at non-options (non-GNU behavior).""" + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --SomeFlag some_arg --flagfile=%s' + % tmp_files[0]) + fake_argv = fake_cmd_line.split(' ') + expected_results = ['fooScript', + '--SomeFlag', + 'some_arg', + '--flagfile=%s' % tmp_files[0]] + + test_results = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + + def testMethod_flagfiles_7(self): + """Test that --flagfile parsing skips over a non-option (GNU behavior).""" + self.flag_values.UseGnuGetOpt() + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --SomeFlag some_arg --flagfile=%s' + % tmp_files[0]) + fake_argv = fake_cmd_line.split(' ') + expected_results = ['fooScript', + '--SomeFlag', + 'some_arg', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag'] + + test_results = self._ReadFlagsFromFiles(fake_argv, False) + self.assertEqual(expected_results, test_results) + + def testMethod_flagfiles_8(self): + """Test that --flagfile parsing respects force_gnu=True.""" + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --SomeFlag some_arg --flagfile=%s' + % tmp_files[0]) + fake_argv = fake_cmd_line.split(' ') + expected_results = ['fooScript', + '--SomeFlag', + 'some_arg', + '--UnitTestMessage1=tempFile1!', + '--UnitTestNumber=54321', + '--noUnitTestBoolFlag'] + + test_results = self._ReadFlagsFromFiles(fake_argv, True) + self.assertEqual(expected_results, test_results) + + def testMethod_flagfiles_NoPermissions(self): + """Test that --flagfile raises except on file that is unreadable.""" + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --SomeFlag some_arg --flagfile=%s' + % tmp_files[3]) + fake_argv = fake_cmd_line.split(' ') + self.assertRaises(gflags.CantOpenFlagFileError, + self._ReadFlagsFromFiles, fake_argv, True) + + def testMethod_flagfiles_NotFound(self): + """Test that --flagfile raises except on file that does not exist.""" + tmp_files = self._SetupTestFiles() + # specify our temp file on the fake cmd line + fake_cmd_line = ('fooScript --SomeFlag some_arg --flagfile=%sNOTEXIST' + % tmp_files[3]) + fake_argv = fake_cmd_line.split(' ') + self.assertRaises(gflags.CantOpenFlagFileError, + self._ReadFlagsFromFiles, fake_argv, True) + + 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. + """ + 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 = self.flag_values.ExtractFilename(fake_flagfile_item_style_1) + self.assertEqual(expected_results, test_results) + + test_results = self.flag_values.ExtractFilename(fake_flagfile_item_style_2) + self.assertEqual(expected_results, test_results) + + # 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 + """ + fake_argv = ['fooScript', '--UnitTestBoolFlag', + 'command', '--command_arg1', '--UnitTestBoom', '--UnitTestB'] + argv = self.flag_values(fake_argv) + self.assertEqual(argv, fake_argv[:1] + fake_argv[2:]) + + def test_parse_flags_after_args_if_using_gnu_getopt(self): + """ + Test that flags given after arguments are parsed if using gnu_getopt. + """ + self.flag_values.UseGnuGetOpt() + fake_argv = ['fooScript', '--UnitTestBoolFlag', + 'command', '--UnitTestB'] + argv = self.flag_values(fake_argv) + self.assertEqual(argv, ['fooScript', 'command']) + + def test_SetDefault(self): + """ + Test changing flag defaults. + """ + # Test that SetDefault changes both the default and the value, + # and that the value is changed when one is given as an option. + self.flag_values['UnitTestMessage1'].SetDefault('New value') + self.assertEqual(self.flag_values.UnitTestMessage1, 'New value') + self.assertEqual(self.flag_values['UnitTestMessage1'].default_as_str, + "'New value'") + self.flag_values([ 'dummyscript', '--UnitTestMessage1=Newer value' ]) + self.assertEqual(self.flag_values.UnitTestMessage1, 'Newer value') + + # Test that setting the default to None works correctly. + self.flag_values['UnitTestNumber'].SetDefault(None) + self.assertEqual(self.flag_values.UnitTestNumber, None) + self.assertEqual(self.flag_values['UnitTestNumber'].default_as_str, None) + self.flag_values([ 'dummyscript', '--UnitTestNumber=56' ]) + self.assertEqual(self.flag_values.UnitTestNumber, 56) + + # Test that setting the default to zero works correctly. + self.flag_values['UnitTestNumber'].SetDefault(0) + self.assertEqual(self.flag_values.UnitTestNumber, 0) + self.assertEqual(self.flag_values['UnitTestNumber'].default_as_str, "'0'") + self.flag_values([ 'dummyscript', '--UnitTestNumber=56' ]) + self.assertEqual(self.flag_values.UnitTestNumber, 56) + + # Test that setting the default to "" works correctly. + self.flag_values['UnitTestMessage1'].SetDefault("") + self.assertEqual(self.flag_values.UnitTestMessage1, "") + self.assertEqual(self.flag_values['UnitTestMessage1'].default_as_str, "''") + self.flag_values([ 'dummyscript', '--UnitTestMessage1=fifty-six' ]) + self.assertEqual(self.flag_values.UnitTestMessage1, "fifty-six") + + # Test that setting the default to false works correctly. + self.flag_values['UnitTestBoolFlag'].SetDefault(False) + self.assertEqual(self.flag_values.UnitTestBoolFlag, False) + self.assertEqual(self.flag_values['UnitTestBoolFlag'].default_as_str, + "'false'") + self.flag_values([ 'dummyscript', '--UnitTestBoolFlag=true' ]) + self.assertEqual(self.flag_values.UnitTestBoolFlag, True) + + # Test that setting a list default works correctly. + self.flag_values['UnitTestList'].SetDefault('4,5,6') + self.assertEqual(self.flag_values.UnitTestList, ['4', '5', '6']) + self.assertEqual(self.flag_values['UnitTestList'].default_as_str, "'4,5,6'") + self.flag_values([ 'dummyscript', '--UnitTestList=7,8,9' ]) + self.assertEqual(self.flag_values.UnitTestList, ['7', '8', '9']) + + # Test that setting invalid defaults raises exceptions + self.assertRaises(gflags.IllegalFlagValue, + self.flag_values['UnitTestNumber'].SetDefault, 'oops') + self.assertRaises(gflags.IllegalFlagValue, + self.flag_values.SetDefault, 'UnitTestNumber', -1) + + +class FlagsParsingTest(googletest.TestCase): + """Testing different aspects of parsing: '-f' vs '--flag', etc.""" + + def setUp(self): + self.flag_values = gflags.FlagValues() + + def testMethod_ShortestUniquePrefixes(self): + """Test FlagValues.ShortestUniquePrefixes""" + + gflags.DEFINE_string('a', '', '', flag_values=self.flag_values) + gflags.DEFINE_string('abc', '', '', flag_values=self.flag_values) + gflags.DEFINE_string('common_a_string', '', '', flag_values=self.flag_values) + gflags.DEFINE_boolean('common_b_boolean', 0, '', + flag_values=self.flag_values) + gflags.DEFINE_boolean('common_c_boolean', 0, '', + flag_values=self.flag_values) + gflags.DEFINE_boolean('common', 0, '', flag_values=self.flag_values) + gflags.DEFINE_integer('commonly', 0, '', flag_values=self.flag_values) + gflags.DEFINE_boolean('zz', 0, '', flag_values=self.flag_values) + gflags.DEFINE_integer('nozz', 0, '', flag_values=self.flag_values) + + shorter_flags = self.flag_values.ShortestUniquePrefixes( + self.flag_values.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) + + self.flag_values.__delattr__('a') + self.flag_values.__delattr__('abc') + self.flag_values.__delattr__('common_a_string') + self.flag_values.__delattr__('common_b_boolean') + self.flag_values.__delattr__('common_c_boolean') + self.flag_values.__delattr__('common') + self.flag_values.__delattr__('commonly') + self.flag_values.__delattr__('zz') + self.flag_values.__delattr__('nozz') + + def test_twodasharg_first(self): + gflags.DEFINE_string("twodash_name", "Bob", "namehelp", + flag_values=self.flag_values) + gflags.DEFINE_string("twodash_blame", "Rob", "blamehelp", + flag_values=self.flag_values) + argv = ('./program', + '--', + '--twodash_name=Harry') + argv = self.flag_values(argv) + self.assertEqual('Bob', self.flag_values.twodash_name) + self.assertEqual(argv[1], '--twodash_name=Harry') + + def test_twodasharg_middle(self): + gflags.DEFINE_string("twodash2_name", "Bob", "namehelp", + flag_values=self.flag_values) + gflags.DEFINE_string("twodash2_blame", "Rob", "blamehelp", + flag_values=self.flag_values) + argv = ('./program', + '--twodash2_blame=Larry', + '--', + '--twodash2_name=Harry') + argv = self.flag_values(argv) + self.assertEqual('Bob', self.flag_values.twodash2_name) + self.assertEqual('Larry', self.flag_values.twodash2_blame) + self.assertEqual(argv[1], '--twodash2_name=Harry') + + def test_onedasharg_first(self): + gflags.DEFINE_string("onedash_name", "Bob", "namehelp", + flag_values=self.flag_values) + gflags.DEFINE_string("onedash_blame", "Rob", "blamehelp", + flag_values=self.flag_values) + argv = ('./program', + '-', + '--onedash_name=Harry') + argv = self.flag_values(argv) + self.assertEqual(argv[1], '-') + # TODO(csilvers): we should still parse --onedash_name=Harry as a + # flag, but currently we don't (we stop flag processing as soon as + # we see the first non-flag). + # - This requires gnu_getopt from Python 2.3+ see FLAGS.UseGnuGetOpt() + + def test_unrecognized_flags(self): + gflags.DEFINE_string("name", "Bob", "namehelp", flag_values=self.flag_values) + # Unknown flag --nosuchflag + try: + argv = ('./program', '--nosuchflag', '--name=Bob', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflag' + assert e.flagvalue == '--nosuchflag' + + # Unknown flag -w (short option) + try: + argv = ('./program', '-w', '--name=Bob', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'w' + assert e.flagvalue == '-w' + + # Unknown flag --nosuchflagwithparam=foo + try: + argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflagwithparam' + assert e.flagvalue == '--nosuchflagwithparam=foo' + + # Allow unknown flag --nosuchflag if specified with undefok + argv = ('./program', '--nosuchflag', '--name=Bob', + '--undefok=nosuchflag', 'extra') + argv = self.flag_values(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" + + # Allow unknown flag --noboolflag if undefok=boolflag is specified + argv = ('./program', '--noboolflag', '--name=Bob', + '--undefok=boolflag', 'extra') + argv = self.flag_values(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" + + # But not if the flagname is misspelled: + try: + argv = ('./program', '--nosuchflag', '--name=Bob', + '--undefok=nosuchfla', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflag' + + try: + argv = ('./program', '--nosuchflag', '--name=Bob', + '--undefok=nosuchflagg', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflag' + + # Allow unknown short flag -w if specified with undefok + argv = ('./program', '-w', '--name=Bob', '--undefok=w', 'extra') + argv = self.flag_values(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" + + # Allow unknown flag --nosuchflagwithparam=foo if specified + # with undefok + argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', + '--undefok=nosuchflagwithparam', 'extra') + argv = self.flag_values(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" + + # Even if undefok specifies multiple flags + argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo', + '--name=Bob', + '--undefok=nosuchflag,w,nosuchflagwithparam', + 'extra') + argv = self.flag_values(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" + + # However, not if undefok doesn't specify the flag + try: + argv = ('./program', '--nosuchflag', '--name=Bob', + '--undefok=another_such', 'extra') + self.flag_values(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflag' + + # Make sure --undefok doesn't mask other option errors. + try: + # Provide an option requiring a parameter but not giving it one. + argv = ('./program', '--undefok=name', '--name') + self.flag_values(argv) + raise AssertionError("Missing option parameter exception not raised") + except gflags.UnrecognizedFlag: + raise AssertionError("Wrong kind of error exception raised") + except gflags.FlagsError: + pass + + # Test --undefok + argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo', + '--name=Bob', + '--undefok', + 'nosuchflag,w,nosuchflagwithparam', + 'extra') + argv = self.flag_values(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" + + +class NonGlobalFlagsTest(googletest.TestCase): + + def test_nonglobal_flags(self): + """Test use of non-global FlagValues""" + nonglobal_flags = gflags.FlagValues() + gflags.DEFINE_string("nonglobal_flag", "Bob", "flaghelp", nonglobal_flags) + argv = ('./program', + '--nonglobal_flag=Mary', + 'extra') + argv = nonglobal_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 nonglobal_flags['nonglobal_flag'].value == 'Mary' + + def test_unrecognized_nonglobal_flags(self): + """Test unrecognized non-global flags""" + nonglobal_flags = gflags.FlagValues() + argv = ('./program', + '--nosuchflag') + try: + argv = nonglobal_flags(argv) + raise AssertionError("Unknown flag exception not raised") + except gflags.UnrecognizedFlag, e: + assert e.flagname == 'nosuchflag' + pass + + argv = ('./program', + '--nosuchflag', + '--undefok=nosuchflag') + + argv = nonglobal_flags(argv) + assert len(argv) == 1, "wrong number of arguments pulled" + assert argv[0]=='./program', "program name not preserved" + + def test_create_flag_errors(self): + # Since the exception classes are exposed, nothing stops users + # from creating their own instances. This test makes sure that + # people modifying the flags module understand that the external + # mechanisms for creating the exceptions should continue to work. + e = gflags.FlagsError() + e = gflags.FlagsError("message") + e = gflags.DuplicateFlag() + e = gflags.DuplicateFlag("message") + e = gflags.IllegalFlagValue() + e = gflags.IllegalFlagValue("message") + e = gflags.UnrecognizedFlag() + e = gflags.UnrecognizedFlag("message") + + def testFlagValuesDelAttr(self): + """Checks that del self.flag_values.flag_id works.""" + default_value = 'default value for testFlagValuesDelAttr' + # 1. Declare and delete a flag with no short name. + flag_values = gflags.FlagValues() + gflags.DEFINE_string('delattr_foo', default_value, 'A simple flag.', + flag_values=flag_values) + self.assertEquals(flag_values.delattr_foo, default_value) + flag_obj = flag_values['delattr_foo'] + # We also check that _FlagIsRegistered works as expected :) + self.assertTrue(flag_values._FlagIsRegistered(flag_obj)) + del flag_values.delattr_foo + self.assertFalse('delattr_foo' in flag_values.FlagDict()) + self.assertFalse(flag_values._FlagIsRegistered(flag_obj)) + # If the previous del FLAGS.delattr_foo did not work properly, the + # next definition will trigger a redefinition error. + gflags.DEFINE_integer('delattr_foo', 3, 'A simple flag.', + flag_values=flag_values) + del flag_values.delattr_foo + + self.assertFalse('delattr_foo' in flag_values.RegisteredFlags()) + + # 2. Declare and delete a flag with a short name. + gflags.DEFINE_string('delattr_bar', default_value, 'flag with short name', + short_name='x5', flag_values=flag_values) + flag_obj = flag_values['delattr_bar'] + self.assertTrue(flag_values._FlagIsRegistered(flag_obj)) + del flag_values.x5 + self.assertTrue(flag_values._FlagIsRegistered(flag_obj)) + del flag_values.delattr_bar + self.assertFalse(flag_values._FlagIsRegistered(flag_obj)) + + # 3. Just like 2, but del flag_values.name last + gflags.DEFINE_string('delattr_bar', default_value, 'flag with short name', + short_name='x5', flag_values=flag_values) + flag_obj = flag_values['delattr_bar'] + self.assertTrue(flag_values._FlagIsRegistered(flag_obj)) + del flag_values.delattr_bar + self.assertTrue(flag_values._FlagIsRegistered(flag_obj)) + del flag_values.x5 + self.assertFalse(flag_values._FlagIsRegistered(flag_obj)) + + self.assertFalse('delattr_bar' in flag_values.RegisteredFlags()) + self.assertFalse('x5' in flag_values.RegisteredFlags()) + + +class KeyFlagsTest(googletest.TestCase): + + def setUp(self): + self.flag_values = gflags.FlagValues() + + def _GetNamesOfDefinedFlags(self, module, flag_values): + """Returns the list of names of flags defined by a module. + + Auxiliary for the testKeyFlags* methods. + + Args: + module: A module object or a string module name. + flag_values: A FlagValues object. + + Returns: + A list of strings. + """ + return [f.name for f in flag_values._GetFlagsDefinedByModule(module)] + + def _GetNamesOfKeyFlags(self, module, flag_values): + """Returns the list of names of key flags for a module. + + Auxiliary for the testKeyFlags* methods. + + Args: + module: A module object or a string module name. + flag_values: A FlagValues object. + + Returns: + A list of strings. + """ + return [f.name for f in flag_values._GetKeyFlagsForModule(module)] + + def _AssertListsHaveSameElements(self, list_1, list_2): + # Checks that two lists have the same elements with the same + # multiplicity, in possibly different order. + list_1 = list(list_1) + list_1.sort() + list_2 = list(list_2) + list_2.sort() + self.assertListEqual(list_1, list_2) + + def testKeyFlags(self): + # Before starting any testing, make sure no flags are already + # defined for module_foo and module_bar. + self.assertListEqual(self._GetNamesOfKeyFlags(module_foo, self.flag_values), + []) + self.assertListEqual(self._GetNamesOfKeyFlags(module_bar, self.flag_values), + []) + self.assertListEqual(self._GetNamesOfDefinedFlags(module_foo, + self.flag_values), + []) + self.assertListEqual(self._GetNamesOfDefinedFlags(module_bar, + self.flag_values), + []) + + # Defines a few flags in module_foo and module_bar. + module_foo.DefineFlags(flag_values=self.flag_values) + + try: + # Part 1. Check that all flags defined by module_foo are key for + # that module, and similarly for module_bar. + for module in [module_foo, module_bar]: + self._AssertListsHaveSameElements( + self.flag_values._GetFlagsDefinedByModule(module), + self.flag_values._GetKeyFlagsForModule(module)) + # Also check that each module defined the expected flags. + self._AssertListsHaveSameElements( + self._GetNamesOfDefinedFlags(module, self.flag_values), + module.NamesOfDefinedFlags()) + + # Part 2. Check that gflags.DECLARE_key_flag works fine. + # Declare that some flags from module_bar are key for + # module_foo. + module_foo.DeclareKeyFlags(flag_values=self.flag_values) + + # Check that module_foo has the expected list of defined flags. + self._AssertListsHaveSameElements( + self._GetNamesOfDefinedFlags(module_foo, self.flag_values), + module_foo.NamesOfDefinedFlags()) + + # Check that module_foo has the expected list of key flags. + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(module_foo, self.flag_values), + module_foo.NamesOfDeclaredKeyFlags()) + + # Part 3. Check that gflags.ADOPT_module_key_flags works fine. + # Trigger a call to gflags.ADOPT_module_key_flags(module_bar) + # inside module_foo. This should declare a few more key + # flags in module_foo. + module_foo.DeclareExtraKeyFlags(flag_values=self.flag_values) + + # Check that module_foo has the expected list of key flags. + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(module_foo, self.flag_values), + module_foo.NamesOfDeclaredKeyFlags() + + module_foo.NamesOfDeclaredExtraKeyFlags()) + finally: + module_foo.RemoveFlags(flag_values=self.flag_values) + + def testKeyFlagsWithNonDefaultFlagValuesObject(self): + # Check that key flags work even when we use a FlagValues object + # that is not the default gflags.self.flag_values object. Otherwise, this + # test is similar to testKeyFlags, but it uses only module_bar. + # The other test module (module_foo) uses only the default values + # for the flag_values keyword arguments. This way, testKeyFlags + # and this method test both the default FlagValues, the explicitly + # specified one, and a mixed usage of the two. + + # A brand-new FlagValues object, to use instead of gflags.self.flag_values. + fv = gflags.FlagValues() + + # Before starting any testing, make sure no flags are already + # defined for module_foo and module_bar. + self.assertListEqual( + self._GetNamesOfKeyFlags(module_bar, fv), + []) + self.assertListEqual( + self._GetNamesOfDefinedFlags(module_bar, fv), + []) + + module_bar.DefineFlags(flag_values=fv) + + # Check that all flags defined by module_bar are key for that + # module, and that module_bar defined the expected flags. + self._AssertListsHaveSameElements( + fv._GetFlagsDefinedByModule(module_bar), + fv._GetKeyFlagsForModule(module_bar)) + self._AssertListsHaveSameElements( + self._GetNamesOfDefinedFlags(module_bar, fv), + module_bar.NamesOfDefinedFlags()) + + # Pick two flags from module_bar, declare them as key for the + # current (i.e., main) module (via gflags.DECLARE_key_flag), and + # check that we get the expected effect. The important thing is + # that we always use flags_values=fv (instead of the default + # self.flag_values). + main_module = gflags._GetMainModule() + names_of_flags_defined_by_bar = module_bar.NamesOfDefinedFlags() + flag_name_0 = names_of_flags_defined_by_bar[0] + flag_name_2 = names_of_flags_defined_by_bar[2] + + gflags.DECLARE_key_flag(flag_name_0, flag_values=fv) + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(main_module, fv), + [flag_name_0]) + + gflags.DECLARE_key_flag(flag_name_2, flag_values=fv) + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(main_module, fv), + [flag_name_0, flag_name_2]) + + # Try with a special (not user-defined) flag too: + gflags.DECLARE_key_flag('undefok', flag_values=fv) + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(main_module, fv), + [flag_name_0, flag_name_2, 'undefok']) + + gflags.ADOPT_module_key_flags(module_bar, fv) + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(main_module, fv), + names_of_flags_defined_by_bar + ['undefok']) + + # Adopt key flags from the flags module itself. + gflags.ADOPT_module_key_flags(gflags, flag_values=fv) + self._AssertListsHaveSameElements( + self._GetNamesOfKeyFlags(main_module, fv), + names_of_flags_defined_by_bar + ['flagfile', 'undefok']) + + def testMainModuleHelpWithKeyFlags(self): + # Similar to test_main_module_help, but this time we make sure to + # declare some key flags. + + # Safety check that the main module does not declare any flags + # at the beginning of this test. + expected_help = '' + self.assertMultiLineEqual(expected_help, self.flag_values.MainModuleHelp()) + + # Define one flag in this main module and some flags in modules + # a and b. Also declare one flag from module a and one flag + # from module b as key flags for the main module. + gflags.DEFINE_integer('main_module_int_fg', 1, + 'Integer flag in the main module.', + flag_values=self.flag_values) + + try: + main_module_int_fg_help = ( + " --main_module_int_fg: Integer flag in the main module.\n" + " (default: '1')\n" + " (an integer)") + + expected_help += "\n%s:\n%s" % (sys.argv[0], main_module_int_fg_help) + self.assertMultiLineEqual(expected_help, + self.flag_values.MainModuleHelp()) + + # The following call should be a no-op: any flag declared by a + # module is automatically key for that module. + gflags.DECLARE_key_flag('main_module_int_fg', flag_values=self.flag_values) + self.assertMultiLineEqual(expected_help, + self.flag_values.MainModuleHelp()) + + # The definition of a few flags in an imported module should not + # change the main module help. + module_foo.DefineFlags(flag_values=self.flag_values) + self.assertMultiLineEqual(expected_help, + self.flag_values.MainModuleHelp()) + + gflags.DECLARE_key_flag('tmod_foo_bool', flag_values=self.flag_values) + tmod_foo_bool_help = ( + " --[no]tmod_foo_bool: Boolean flag from module foo.\n" + " (default: 'true')") + expected_help += "\n" + tmod_foo_bool_help + self.assertMultiLineEqual(expected_help, + self.flag_values.MainModuleHelp()) + + gflags.DECLARE_key_flag('tmod_bar_z', flag_values=self.flag_values) + tmod_bar_z_help = ( + " --[no]tmod_bar_z: Another boolean flag from module bar.\n" + " (default: 'false')") + # Unfortunately, there is some flag sorting inside + # MainModuleHelp, so we can't keep incrementally extending + # the expected_help string ... + expected_help = ("\n%s:\n%s\n%s\n%s" % + (sys.argv[0], + main_module_int_fg_help, + tmod_bar_z_help, + tmod_foo_bool_help)) + self.assertMultiLineEqual(self.flag_values.MainModuleHelp(), + expected_help) + + finally: + # At the end, delete all the flag information we created. + self.flag_values.__delattr__('main_module_int_fg') + module_foo.RemoveFlags(flag_values=self.flag_values) + + def test_ADOPT_module_key_flags(self): + # Check that ADOPT_module_key_flags raises an exception when + # called with a module name (as opposed to a module object). + self.assertRaises(gflags.FlagsError, + gflags.ADOPT_module_key_flags, + 'pyglib.app') + + +class GetCallingModuleTest(googletest.TestCase): + """Test whether we correctly determine the module which defines the flag.""" + + def test_GetCallingModule(self): + self.assertEqual(gflags._GetCallingModule(), sys.argv[0]) + self.assertEqual( + module_foo.GetModuleName(), + 'flags_modules_for_testing.module_foo') + self.assertEqual( + module_bar.GetModuleName(), + 'flags_modules_for_testing.module_bar') + + # We execute the following exec statements for their side-effect + # (i.e., not raising an error). They emphasize the case that not + # all code resides in one of the imported modules: Python is a + # really dynamic language, where we can dynamically construct some + # code and execute it. + code = ("import gflags\n" + "module_name = gflags._GetCallingModule()") + exec(code) + + # Next two exec statements executes code with a global environment + # that is different from the global environment of any imported + # module. + exec(code, {}) + # vars(self) returns a dictionary corresponding to the symbol + # table of the self object. dict(...) makes a distinct copy of + # this dictionary, such that any new symbol definition by the + # exec-ed code (e.g., import flags, module_name = ...) does not + # affect the symbol table of self. + exec(code, dict(vars(self))) + + # Next test is actually more involved: it checks not only that + # _GetCallingModule does not crash inside exec code, it also checks + # that it returns the expected value: the code executed via exec + # code is treated as being executed by the current module. We + # check it twice: first time by executing exec from the main + # module, second time by executing it from module_bar. + global_dict = {} + exec(code, global_dict) + self.assertEqual(global_dict['module_name'], + sys.argv[0]) + + global_dict = {} + module_bar.ExecuteCode(code, global_dict) + self.assertEqual( + global_dict['module_name'], + 'flags_modules_for_testing.module_bar') + + def test_GetCallingModuleWithIteritemsError(self): + # This test checks that _GetCallingModule is using + # sys.modules.items(), instead of .iteritems(). + orig_sys_modules = sys.modules + + # Mock sys.modules: simulates error produced by importing a module + # in paralel with our iteration over sys.modules.iteritems(). + class SysModulesMock(dict): + def __init__(self, original_content): + dict.__init__(self, original_content) + + def iteritems(self): + # Any dictionary method is fine, but not .iteritems(). + raise RuntimeError('dictionary changed size during iteration') + + sys.modules = SysModulesMock(orig_sys_modules) + try: + # _GetCallingModule should still work as expected: + self.assertEqual(gflags._GetCallingModule(), sys.argv[0]) + self.assertEqual( + module_foo.GetModuleName(), + 'flags_modules_for_testing.module_foo') + finally: + sys.modules = orig_sys_modules + + +class FindModuleTest(googletest.TestCase): + """Testing methods that find a module that defines a given flag.""" + + def testFindModuleDefiningFlag(self): + self.assertEqual('default', FLAGS.FindModuleDefiningFlag( + '__NON_EXISTENT_FLAG__', 'default')) + self.assertEqual( + module_baz.__name__, FLAGS.FindModuleDefiningFlag('tmod_baz_x')) + + def testFindModuleIdDefiningFlag(self): + self.assertEqual('default', FLAGS.FindModuleIdDefiningFlag( + '__NON_EXISTENT_FLAG__', 'default')) + self.assertEqual( + id(module_baz), FLAGS.FindModuleIdDefiningFlag('tmod_baz_x')) + + +class FlagsErrorMessagesTest(googletest.TestCase): + """Testing special cases for integer and float flags error messages.""" + + def setUp(self): + # make sure we are using the old, stupid way of parsing flags. + self.flag_values = gflags.FlagValues() + self.flag_values.UseGnuGetOpt(False) + + def testIntegerErrorText(self): + # Make sure we get proper error text + gflags.DEFINE_integer('positive', 4, 'non-negative flag', lower_bound=1, + flag_values=self.flag_values) + gflags.DEFINE_integer('non_negative', 4, 'positive flag', lower_bound=0, + flag_values=self.flag_values) + gflags.DEFINE_integer('negative', -4, 'negative flag', upper_bound=-1, + flag_values=self.flag_values) + gflags.DEFINE_integer('non_positive', -4, 'non-positive flag', upper_bound=0, + flag_values=self.flag_values) + gflags.DEFINE_integer('greater', 19, 'greater-than flag', lower_bound=4, + flag_values=self.flag_values) + gflags.DEFINE_integer('smaller', -19, 'smaller-than flag', upper_bound=4, + flag_values=self.flag_values) + gflags.DEFINE_integer('usual', 4, 'usual flag', lower_bound=0, + upper_bound=10000, flag_values=self.flag_values) + gflags.DEFINE_integer('another_usual', 0, 'usual flag', lower_bound=-1, + upper_bound=1, flag_values=self.flag_values) + + self._CheckErrorMessage('positive', -4, 'a positive integer') + self._CheckErrorMessage('non_negative', -4, 'a non-negative integer') + self._CheckErrorMessage('negative', 0, 'a negative integer') + self._CheckErrorMessage('non_positive', 4, 'a non-positive integer') + self._CheckErrorMessage('usual', -4, 'an integer in the range [0, 10000]') + self._CheckErrorMessage('another_usual', 4, + 'an integer in the range [-1, 1]') + self._CheckErrorMessage('greater', -5, 'integer >= 4') + self._CheckErrorMessage('smaller', 5, 'integer <= 4') + + def testFloatErrorText(self): + gflags.DEFINE_float('positive', 4, 'non-negative flag', lower_bound=1, + flag_values=self.flag_values) + gflags.DEFINE_float('non_negative', 4, 'positive flag', lower_bound=0, + flag_values=self.flag_values) + gflags.DEFINE_float('negative', -4, 'negative flag', upper_bound=-1, + flag_values=self.flag_values) + gflags.DEFINE_float('non_positive', -4, 'non-positive flag', upper_bound=0, + flag_values=self.flag_values) + gflags.DEFINE_float('greater', 19, 'greater-than flag', lower_bound=4, + flag_values=self.flag_values) + gflags.DEFINE_float('smaller', -19, 'smaller-than flag', upper_bound=4, + flag_values=self.flag_values) + gflags.DEFINE_float('usual', 4, 'usual flag', lower_bound=0, + upper_bound=10000, flag_values=self.flag_values) + gflags.DEFINE_float('another_usual', 0, 'usual flag', lower_bound=-1, + upper_bound=1, flag_values=self.flag_values) + + self._CheckErrorMessage('positive', 0.5, 'number >= 1') + self._CheckErrorMessage('non_negative', -4.0, 'a non-negative number') + self._CheckErrorMessage('negative', 0.5, 'number <= -1') + self._CheckErrorMessage('non_positive', 4.0, 'a non-positive number') + self._CheckErrorMessage('usual', -4.0, 'a number in the range [0, 10000]') + self._CheckErrorMessage('another_usual', 4.0, + 'a number in the range [-1, 1]') + self._CheckErrorMessage('smaller', 5.0, 'number <= 4') + + def _CheckErrorMessage(self, flag_name, flag_value, expected_message_suffix): + """Set a flag to a given value and make sure we get expected message.""" + + try: + self.flag_values.__setattr__(flag_name, flag_value) + raise AssertionError('Bounds exception not raised!') + except gflags.IllegalFlagValue, e: + expected = ('flag --%(name)s=%(value)s: %(value)s is not %(suffix)s' % + {'name': flag_name, 'value': flag_value, + 'suffix': expected_message_suffix}) + self.assertEquals(str(e), expected) + + +def main(): + googletest.main() + + +if __name__ == '__main__': + main() diff --git a/third_party/py/gflags/tests/gflags_validators_test.py b/third_party/py/gflags/tests/gflags_validators_test.py new file mode 100755 index 0000000000..460e6d01d9 --- /dev/null +++ b/third_party/py/gflags/tests/gflags_validators_test.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +# Copyright (c) 2010, 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. + +"""Testing that flags validators framework does work. + +This file tests that each flag validator called when it should be, and that +failed validator will throw an exception, etc. +""" + +__author__ = 'olexiy@google.com (Olexiy Oryeshko)' + +import gflags_googletest as googletest +import gflags +import gflags_validators + + +class SimpleValidatorTest(googletest.TestCase): + """Testing gflags.RegisterValidator() method.""" + + def setUp(self): + super(SimpleValidatorTest, self).setUp() + self.flag_values = gflags.FlagValues() + self.call_args = [] + + def testSuccess(self): + def Checker(x): + self.call_args.append(x) + return True + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program') + self.flag_values(argv) + self.assertEquals(None, self.flag_values.test_flag) + self.flag_values.test_flag = 2 + self.assertEquals(2, self.flag_values.test_flag) + self.assertEquals([None, 2], self.call_args) + + def testDefaultValueNotUsedSuccess(self): + def Checker(x): + self.call_args.append(x) + return True + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program', '--test_flag=1') + self.flag_values(argv) + self.assertEquals(1, self.flag_values.test_flag) + self.assertEquals([1], self.call_args) + + def testValidatorNotCalledWhenOtherFlagIsChanged(self): + def Checker(x): + self.call_args.append(x) + return True + gflags.DEFINE_integer('test_flag', 1, 'Usual integer flag', + flag_values=self.flag_values) + gflags.DEFINE_integer('other_flag', 2, 'Other integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program') + self.flag_values(argv) + self.assertEquals(1, self.flag_values.test_flag) + self.flag_values.other_flag = 3 + self.assertEquals([1], self.call_args) + + def testExceptionRaisedIfCheckerFails(self): + def Checker(x): + self.call_args.append(x) + return x == 1 + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program', '--test_flag=1') + self.flag_values(argv) + try: + self.flag_values.test_flag = 2 + raise AssertionError('gflags.IllegalFlagValue expected') + except gflags.IllegalFlagValue, e: + self.assertEquals('flag --test_flag=2: Errors happen', str(e)) + self.assertEquals([1, 2], self.call_args) + + def testExceptionRaisedIfCheckerRaisesException(self): + def Checker(x): + self.call_args.append(x) + if x == 1: + return True + raise gflags_validators.Error('Specific message') + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program', '--test_flag=1') + self.flag_values(argv) + try: + self.flag_values.test_flag = 2 + raise AssertionError('gflags.IllegalFlagValue expected') + except gflags.IllegalFlagValue, e: + self.assertEquals('flag --test_flag=2: Specific message', str(e)) + self.assertEquals([1, 2], self.call_args) + + def testErrorMessageWhenCheckerReturnsFalseOnStart(self): + def Checker(x): + self.call_args.append(x) + return False + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program', '--test_flag=1') + try: + self.flag_values(argv) + raise AssertionError('gflags.IllegalFlagValue expected') + except gflags.IllegalFlagValue, e: + self.assertEquals('flag --test_flag=1: Errors happen', str(e)) + self.assertEquals([1], self.call_args) + + def testErrorMessageWhenCheckerRaisesExceptionOnStart(self): + def Checker(x): + self.call_args.append(x) + raise gflags_validators.Error('Specific message') + gflags.DEFINE_integer('test_flag', None, 'Usual integer flag', + flag_values=self.flag_values) + gflags.RegisterValidator('test_flag', + Checker, + message='Errors happen', + flag_values=self.flag_values) + + argv = ('./program', '--test_flag=1') + try: + self.flag_values(argv) + raise AssertionError('IllegalFlagValue expected') + except gflags.IllegalFlagValue, e: + self.assertEquals('flag --test_flag=1: Specific message', str(e)) + self.assertEquals([1], self.call_args) + + def testValidatorsCheckedInOrder(self): + + def Required(x): + self.calls.append('Required') + return x is not None + + def Even(x): + self.calls.append('Even') + return x % 2 == 0 + + self.calls = [] + self._DefineFlagAndValidators(Required, Even) + self.assertEquals(['Required', 'Even'], self.calls) + + self.calls = [] + self._DefineFlagAndValidators(Even, Required) + self.assertEquals(['Even', 'Required'], self.calls) + + def _DefineFlagAndValidators(self, first_validator, second_validator): + local_flags = gflags.FlagValues() + gflags.DEFINE_integer('test_flag', 2, 'test flag', flag_values=local_flags) + gflags.RegisterValidator('test_flag', + first_validator, + message='', + flag_values=local_flags) + gflags.RegisterValidator('test_flag', + second_validator, + message='', + flag_values=local_flags) + argv = ('./program') + local_flags(argv) + + +if __name__ == '__main__': + googletest.main() -- cgit v1.2.3