| #!/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 |
| |
| from gflags import _helpers |
| from gflags import argument_parser |
| from gflags 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 |