From d82e69cef3f52f27c418c515f8af6af57d55aaf4 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 10 Jan 2017 17:36:27 -0800 Subject: Bump python-gflags to version 3.1.0. The new version adds support for Python 3. Change-Id: I88f322c6e9f35699fe5f708f52c5b84fb00a5b4b --- 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, 4682 insertions(+), 6346 deletions(-) create mode 100644 third_party/py/gflags/CONTRIBUTING.md delete mode 100644 third_party/py/gflags/NEWS mode change 100755 => 100644 third_party/py/gflags/debian/rules create mode 100644 third_party/py/gflags/gflags/__init__.py create mode 100644 third_party/py/gflags/gflags/_helpers.py create mode 100644 third_party/py/gflags/gflags/_helpers_test.py create mode 100644 third_party/py/gflags/gflags/argument_parser.py create mode 100644 third_party/py/gflags/gflags/exceptions.py create mode 100644 third_party/py/gflags/gflags/flag.py create mode 100644 third_party/py/gflags/gflags/flags_formatting_test.py create mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/__init__.py create mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py create mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py create mode 100644 third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py create mode 100644 third_party/py/gflags/gflags/flags_unicode_literals_test.py create mode 100644 third_party/py/gflags/gflags/flagvalues.py create mode 100644 third_party/py/gflags/gflags/third_party/__init__.py create mode 100644 third_party/py/gflags/gflags/third_party/pep257/LICENSE create mode 100644 third_party/py/gflags/gflags/third_party/pep257/__init__.py create mode 100644 third_party/py/gflags/gflags/validators.py mode change 100755 => 100644 third_party/py/gflags/gflags2man.py delete mode 100755 third_party/py/gflags/gflags_validators.py delete mode 100644 third_party/py/gflags/python_gflags.egg-info/PKG-INFO delete mode 100644 third_party/py/gflags/python_gflags.egg-info/SOURCES.txt delete mode 100644 third_party/py/gflags/python_gflags.egg-info/dependency_links.txt delete mode 100644 third_party/py/gflags/python_gflags.egg-info/top_level.txt delete mode 100644 third_party/py/gflags/setup.cfg mode change 100755 => 100644 third_party/py/gflags/setup.py delete mode 100644 third_party/py/gflags/tests/flags_modules_for_testing/__init__.py delete mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py delete mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py delete mode 100755 third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py delete mode 100644 third_party/py/gflags/tests/gflags_googletest.py delete mode 100755 third_party/py/gflags/tests/gflags_helpxml_test.py delete mode 100755 third_party/py/gflags/tests/gflags_unittest.py delete 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 77f2722818..f0f526daf0 100644 --- a/third_party/py/gflags/BUILD +++ b/third_party/py/gflags/BUILD @@ -8,10 +8,7 @@ filegroup( py_library( name = "gflags", - srcs = [ - "__init__.py", - "gflags_validators.py", - ], + srcs = glob(["**/*.py"]), srcs_version = "PY2AND3", visibility = ["//visibility:public"], ) diff --git a/third_party/py/gflags/CONTRIBUTING.md b/third_party/py/gflags/CONTRIBUTING.md new file mode 100644 index 0000000000..7e8cbf5c00 --- /dev/null +++ b/third_party/py/gflags/CONTRIBUTING.md @@ -0,0 +1,11 @@ +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 87732a2b97..60dc799f5e 100644 --- a/third_party/py/gflags/ChangeLog +++ b/third_party/py/gflags/ChangeLog @@ -1,3 +1,69 @@ +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 17851bfa77..59c4c5e077 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,8 +12,5 @@ include debian/control include debian/copyright include debian/docs include debian/rules -include gflags.py -include gflags2man.py -include gflags_validators.py -include setup.py -recursive-include tests *.py +include *.py +recursive-include gflags * diff --git a/third_party/py/gflags/NEWS b/third_party/py/gflags/NEWS deleted file mode 100644 index 8aaa72bf30..0000000000 --- a/third_party/py/gflags/NEWS +++ /dev/null @@ -1,78 +0,0 @@ -== 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 faab7198f2..f290599483 100644 --- a/third_party/py/gflags/PKG-INFO +++ b/third_party/py/gflags/PKG-INFO @@ -1,10 +1,17 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: python-gflags -Version: 2.0 +Version: 3.1.0 Summary: Google Commandline Flags Module -Home-page: http://code.google.com/p/python-gflags +Home-page: https://github.com/google/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 da2a03fc1c..786d817c67 100644 --- a/third_party/py/gflags/README.md +++ b/third_party/py/gflags/README.md @@ -1,6 +1,6 @@ -[gflags](https://python-gflags.googlecode.com) +[gflags](https://github.com/google/python-gflags) -------- -* Version: 2.0 +* Version: 3.1.0 * License: New BSD License -* 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) +* 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) diff --git a/third_party/py/gflags/__init__.py b/third_party/py/gflags/__init__.py index 23a3135207..77e4285448 100644 --- a/third_party/py/gflags/__init__.py +++ b/third_party/py/gflags/__init__.py @@ -1,2863 +1 @@ -#!/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) +from gflags import * diff --git a/third_party/py/gflags/debian/rules b/third_party/py/gflags/debian/rules old mode 100755 new mode 100644 diff --git a/third_party/py/gflags/gflags/__init__.py b/third_party/py/gflags/gflags/__init__.py new file mode 100644 index 0000000000..b0025eaa39 --- /dev/null +++ b/third_party/py/gflags/gflags/__init__.py @@ -0,0 +1,891 @@ +#!/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 new file mode 100644 index 0000000000..f740506fdd --- /dev/null +++ b/third_party/py/gflags/gflags/_helpers.py @@ -0,0 +1,430 @@ +#!/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 new file mode 100644 index 0000000000..2a7c124837 --- /dev/null +++ b/third_party/py/gflags/gflags/_helpers_test.py @@ -0,0 +1,177 @@ +#!/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 new file mode 100644 index 0000000000..9f7262b231 --- /dev/null +++ b/third_party/py/gflags/gflags/argument_parser.py @@ -0,0 +1,480 @@ +#!/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 new file mode 100644 index 0000000000..efaabb89fd --- /dev/null +++ b/third_party/py/gflags/gflags/exceptions.py @@ -0,0 +1,133 @@ +#!/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 new file mode 100644 index 0000000000..e176b5e35f --- /dev/null +++ b/third_party/py/gflags/gflags/flag.py @@ -0,0 +1,416 @@ +#!/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 new file mode 100644 index 0000000000..d2d585cb72 --- /dev/null +++ b/third_party/py/gflags/gflags/flags_formatting_test.py @@ -0,0 +1,201 @@ +#!/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 new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..4dddd005bb --- /dev/null +++ b/third_party/py/gflags/gflags/flags_modules_for_testing/module_bar.py @@ -0,0 +1,135 @@ +#!/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 new file mode 100644 index 0000000000..cb47832ba3 --- /dev/null +++ b/third_party/py/gflags/gflags/flags_modules_for_testing/module_baz.py @@ -0,0 +1,43 @@ +#!/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 new file mode 100644 index 0000000000..34e464d297 --- /dev/null +++ b/third_party/py/gflags/gflags/flags_modules_for_testing/module_foo.py @@ -0,0 +1,141 @@ +#!/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 new file mode 100644 index 0000000000..cb20887c13 --- /dev/null +++ b/third_party/py/gflags/gflags/flags_unicode_literals_test.py @@ -0,0 +1,26 @@ +#!/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 new file mode 100644 index 0000000000..a47b40308f --- /dev/null +++ b/third_party/py/gflags/gflags/flagvalues.py @@ -0,0 +1,1261 @@ +#!/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 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/third_party/py/gflags/gflags/third_party/pep257/LICENSE b/third_party/py/gflags/gflags/third_party/pep257/LICENSE new file mode 100644 index 0000000000..9bf5acf7fb --- /dev/null +++ b/third_party/py/gflags/gflags/third_party/pep257/LICENSE @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000000..e9443f8afc --- /dev/null +++ b/third_party/py/gflags/gflags/third_party/pep257/__init__.py @@ -0,0 +1,37 @@ +#!/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 new file mode 100644 index 0000000000..79d10f92a5 --- /dev/null +++ b/third_party/py/gflags/gflags/validators.py @@ -0,0 +1,194 @@ +#!/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 100755 new mode 100644 diff --git a/third_party/py/gflags/gflags_validators.py b/third_party/py/gflags/gflags_validators.py deleted file mode 100755 index d83058d50f..0000000000 --- a/third_party/py/gflags/gflags_validators.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/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 deleted file mode 100644 index faab7198f2..0000000000 --- a/third_party/py/gflags/python_gflags.egg-info/PKG-INFO +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index e6068dfde1..0000000000 --- a/third_party/py/gflags/python_gflags.egg-info/SOURCES.txt +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 8b13789179..0000000000 --- a/third_party/py/gflags/python_gflags.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - 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 deleted file mode 100644 index 93c1fcdc74..0000000000 --- a/third_party/py/gflags/python_gflags.egg-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -gflags -gflags_validators diff --git a/third_party/py/gflags/setup.cfg b/third_party/py/gflags/setup.cfg deleted file mode 100644 index 861a9f5542..0000000000 --- a/third_party/py/gflags/setup.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[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 100755 new mode 100644 index 573db2d410..663121a7d4 --- a/third_party/py/gflags/setup.py +++ b/third_party/py/gflags/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python - -# Copyright (c) 2007, Google Inc. -# All rights reserved. +# Copyright 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 @@ -29,16 +27,26 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from setuptools import setup +"""Setup module for python-gflags.""" + +from distutils.core import setup setup(name='python-gflags', - version='2.0', + version='3.1.0', description='Google Commandline Flags Module', license='BSD', author='Google Inc. and others', author_email='google-gflags@googlegroups.com', - url='http://code.google.com/p/python-gflags', - py_modules=["gflags", "gflags_validators"], - data_files=[("bin", ["gflags2man.py"])], - include_package_data=True, - ) + 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', + ] + ) 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 deleted file mode 100644 index e69de29bb2..0000000000 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 deleted file mode 100755 index 230627f23a..0000000000 --- a/third_party/py/gflags/tests/flags_modules_for_testing/module_bar.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/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 deleted file mode 100755 index 2719c950ad..0000000000 --- a/third_party/py/gflags/tests/flags_modules_for_testing/module_baz.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/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 deleted file mode 100755 index 760a37cc7b..0000000000 --- a/third_party/py/gflags/tests/flags_modules_for_testing/module_foo.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/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 deleted file mode 100644 index 9ae614ce80..0000000000 --- a/third_party/py/gflags/tests/gflags_googletest.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/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 deleted file mode 100755 index fd78004b73..0000000000 --- a/third_party/py/gflags/tests/gflags_helpxml_test.py +++ /dev/null @@ -1,535 +0,0 @@ -#!/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 deleted file mode 100755 index 8e948bf36f..0000000000 --- a/third_party/py/gflags/tests/gflags_unittest.py +++ /dev/null @@ -1,1949 +0,0 @@ -#!/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 deleted file mode 100755 index 460e6d01d9..0000000000 --- a/third_party/py/gflags/tests/gflags_validators_test.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/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