|  | # Copyright 2017 The Abseil Authors. | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  |  | 
|  | """Contains Flag class - information about single command-line flag. | 
|  |  | 
|  | Do NOT import this module directly. Import the flags package and use the | 
|  | aliases defined at the package level instead. | 
|  | """ | 
|  |  | 
|  | from __future__ import absolute_import | 
|  | from __future__ import division | 
|  | from __future__ import print_function | 
|  |  | 
|  | import functools | 
|  |  | 
|  | from absl.flags import _argument_parser | 
|  | from absl.flags import _exceptions | 
|  | from absl.flags import _helpers | 
|  |  | 
|  |  | 
|  | @functools.total_ordering | 
|  | class Flag(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_unparsed - the unparsed 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_override_cpp - the flag may be redefined in C++ without raising an | 
|  | error, value "transferred" 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; | 
|  | .allow_using_method_names - whether this flag can be defined even if it has | 
|  | a name that conflicts with a FlagValues method. | 
|  |  | 
|  | 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_override_cpp=False, allow_hide_cpp=False, | 
|  | allow_overwrite=True, allow_using_method_names=False): | 
|  | 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_override_cpp = allow_override_cpp | 
|  | self.allow_hide_cpp = allow_hide_cpp | 
|  | self.allow_overwrite = allow_overwrite | 
|  | self.allow_using_method_names = allow_using_method_names | 
|  |  | 
|  | self.using_default_value = True | 
|  | self._value = None | 
|  | self.validators = [] | 
|  | if allow_hide_cpp and allow_override_cpp: | 
|  | raise _exceptions.Error( | 
|  | "Can't have both allow_hide_cpp (means use Python flag) and " | 
|  | 'allow_override_cpp (means use C++ flag after InitGoogle)') | 
|  |  | 
|  | self._set_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): | 
|  | """Returns 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.str_or_unicode(value)) | 
|  |  | 
|  | def parse(self, argument): | 
|  | """Parses string and sets flag value. | 
|  |  | 
|  | Args: | 
|  | argument: str or the correct flag value type, argument to be parsed. | 
|  | """ | 
|  | if self.present and not self.allow_overwrite: | 
|  | raise _exceptions.IllegalFlagValueError( | 
|  | 'flag --%s=%s: already defined as %s' % ( | 
|  | self.name, argument, self.value)) | 
|  | self.value = self._parse(argument) | 
|  | self.present += 1 | 
|  |  | 
|  | def _parse(self, argument): | 
|  | """Internal parse function. | 
|  |  | 
|  | It returns the parsed value, and does not modify class states. | 
|  |  | 
|  | Args: | 
|  | argument: str or the correct flag value type, argument to be parsed. | 
|  |  | 
|  | Returns: | 
|  | The parsed value. | 
|  | """ | 
|  | try: | 
|  | return self.parser.parse(argument) | 
|  | except (TypeError, ValueError) as e:  # Recast as IllegalFlagValueError. | 
|  | raise _exceptions.IllegalFlagValueError( | 
|  | 'flag --%s=%s: %s' % (self.name, argument, e)) | 
|  |  | 
|  | def unparse(self): | 
|  | self.value = 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.""" | 
|  | self.default_unparsed = value | 
|  | if value is None: | 
|  | self.default = None | 
|  | else: | 
|  | self.default = self._parse(value) | 
|  | self.default_as_str = self._get_parsed_value_as_string(self.default) | 
|  | if self.using_default_value: | 
|  | self.value = self.default | 
|  |  | 
|  | def flag_type(self): | 
|  | """Returns a str that describes the type of the 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.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: minidom.Document, the DOM document it should create nodes from. | 
|  | module_name: str,, the name of the module that defines this flag. | 
|  | is_key: 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.create_xml_dom_element(doc, 'key', 'yes')) | 
|  | element.appendChild(_helpers.create_xml_dom_element( | 
|  | doc, 'file', module_name)) | 
|  | # Adds flag features that are relevant for all flags. | 
|  | element.appendChild(_helpers.create_xml_dom_element(doc, 'name', self.name)) | 
|  | if self.short_name: | 
|  | element.appendChild(_helpers.create_xml_dom_element( | 
|  | doc, 'short_name', self.short_name)) | 
|  | if self.help: | 
|  | element.appendChild(_helpers.create_xml_dom_element( | 
|  | 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.create_xml_dom_element( | 
|  | doc, 'default', default_serialized)) | 
|  | element.appendChild(_helpers.create_xml_dom_element( | 
|  | doc, 'current', self.value)) | 
|  | element.appendChild(_helpers.create_xml_dom_element( | 
|  | 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: 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() | 
|  | super(BooleanFlag, self).__init__( | 
|  | 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,  # pylint: disable=redefined-builtin | 
|  | short_name=None, case_sensitive=True, **args): | 
|  | p = _argument_parser.EnumParser(enum_values, case_sensitive) | 
|  | g = _argument_parser.ArgumentSerializer() | 
|  | super(EnumFlag, self).__init__( | 
|  | 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.create_xml_dom_element( | 
|  | 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): | 
|  | super(MultiFlag, self).__init__(*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. | 
|  | """ | 
|  | new_values = self._parse(arguments) | 
|  | if self.present: | 
|  | self.value.extend(new_values) | 
|  | else: | 
|  | self.value = new_values | 
|  | self.present += len(new_values) | 
|  |  | 
|  | def _parse(self, arguments): | 
|  | 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] | 
|  |  | 
|  | return [super(MultiFlag, self)._parse(item) for item in arguments] | 
|  |  | 
|  | def serialize(self): | 
|  | """See base class.""" | 
|  | 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): | 
|  | """See base class.""" | 
|  | 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.create_xml_dom_element( | 
|  | doc, 'enum_value', enum_value)) | 
|  | return elements |