Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- lib/python3.10/site-packages/absl/__init__.py +15 -0
- lib/python3.10/site-packages/absl/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/__pycache__/app.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/__pycache__/command_name.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/app.py +488 -0
- lib/python3.10/site-packages/absl/app.pyi +88 -0
- lib/python3.10/site-packages/absl/command_name.py +63 -0
- lib/python3.10/site-packages/absl/flags/__init__.py +220 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_argument_parser.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_defines.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_exceptions.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_flag.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_flagvalues.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_helpers.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_validators.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/_validators_classes.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/__pycache__/argparse_flags.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/flags/_argument_parser.py +633 -0
- lib/python3.10/site-packages/absl/flags/_defines.py +1702 -0
- lib/python3.10/site-packages/absl/flags/_exceptions.py +107 -0
- lib/python3.10/site-packages/absl/flags/_flag.py +566 -0
- lib/python3.10/site-packages/absl/flags/_flagvalues.py +1486 -0
- lib/python3.10/site-packages/absl/flags/_helpers.py +427 -0
- lib/python3.10/site-packages/absl/flags/_validators.py +353 -0
- lib/python3.10/site-packages/absl/flags/_validators_classes.py +172 -0
- lib/python3.10/site-packages/absl/flags/argparse_flags.py +390 -0
- lib/python3.10/site-packages/absl/logging/__init__.py +1331 -0
- lib/python3.10/site-packages/absl/logging/__init__.pyi +261 -0
- lib/python3.10/site-packages/absl/logging/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/logging/__pycache__/converter.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/logging/converter.py +214 -0
- lib/python3.10/site-packages/absl/py.typed +0 -0
- lib/python3.10/site-packages/absl/testing/__init__.py +13 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/__init__.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/absltest.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/flagsaver.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/parameterized.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/__pycache__/xml_reporter.cpython-310.pyc +0 -0
- lib/python3.10/site-packages/absl/testing/_bazelize_command.py +68 -0
- lib/python3.10/site-packages/absl/testing/_pretty_print_reporter.py +92 -0
- lib/python3.10/site-packages/absl/testing/absltest.py +0 -0
- lib/python3.10/site-packages/absl/testing/flagsaver.py +393 -0
- lib/python3.10/site-packages/absl/testing/parameterized.py +726 -0
- lib/python3.10/site-packages/absl/testing/xml_reporter.py +570 -0
- lib/python3.10/site-packages/absl_py-2.3.1.dist-info/INSTALLER +1 -0
- lib/python3.10/site-packages/absl_py-2.3.1.dist-info/METADATA +101 -0
- lib/python3.10/site-packages/absl_py-2.3.1.dist-info/RECORD +53 -0
lib/python3.10/site-packages/absl/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
__version__ = '2.3.1'
|
lib/python3.10/site-packages/absl/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (216 Bytes). View file
|
|
|
lib/python3.10/site-packages/absl/__pycache__/app.cpython-310.pyc
ADDED
|
Binary file (13.9 kB). View file
|
|
|
lib/python3.10/site-packages/absl/__pycache__/command_name.cpython-310.pyc
ADDED
|
Binary file (1.69 kB). View file
|
|
|
lib/python3.10/site-packages/absl/app.py
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Generic entry point for Abseil Python applications.
|
| 16 |
+
|
| 17 |
+
To use this module, define a ``main`` function with a single ``argv`` argument
|
| 18 |
+
and call ``app.run(main)``. For example::
|
| 19 |
+
|
| 20 |
+
def main(argv):
|
| 21 |
+
if len(argv) > 1:
|
| 22 |
+
raise app.UsageError('Too many command-line arguments.')
|
| 23 |
+
|
| 24 |
+
if __name__ == '__main__':
|
| 25 |
+
app.run(main)
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
import collections
|
| 29 |
+
import errno
|
| 30 |
+
import os
|
| 31 |
+
import pdb
|
| 32 |
+
import sys
|
| 33 |
+
import textwrap
|
| 34 |
+
import traceback
|
| 35 |
+
|
| 36 |
+
from absl import command_name
|
| 37 |
+
from absl import flags
|
| 38 |
+
from absl import logging
|
| 39 |
+
|
| 40 |
+
try:
|
| 41 |
+
import faulthandler
|
| 42 |
+
except ImportError:
|
| 43 |
+
faulthandler = None
|
| 44 |
+
|
| 45 |
+
FLAGS = flags.FLAGS
|
| 46 |
+
|
| 47 |
+
flags.DEFINE_boolean('run_with_pdb', False, 'Set to true for PDB debug mode')
|
| 48 |
+
flags.DEFINE_boolean('pdb_post_mortem', False,
|
| 49 |
+
'Set to true to handle uncaught exceptions with PDB '
|
| 50 |
+
'post mortem.')
|
| 51 |
+
flags.DEFINE_alias('pdb', 'pdb_post_mortem')
|
| 52 |
+
flags.DEFINE_boolean('run_with_profiling', False,
|
| 53 |
+
'Set to true for profiling the script. '
|
| 54 |
+
'Execution will be slower, and the output format might '
|
| 55 |
+
'change over time.')
|
| 56 |
+
flags.DEFINE_string('profile_file', None,
|
| 57 |
+
'Dump profile information to a file (for python -m '
|
| 58 |
+
'pstats). Implies --run_with_profiling.')
|
| 59 |
+
flags.DEFINE_boolean('use_cprofile_for_profiling', True,
|
| 60 |
+
'Use cProfile instead of the profile module for '
|
| 61 |
+
'profiling. This has no effect unless '
|
| 62 |
+
'--run_with_profiling is set.')
|
| 63 |
+
flags.DEFINE_boolean('only_check_args', False,
|
| 64 |
+
'Set to true to validate args and exit.',
|
| 65 |
+
allow_hide_cpp=True)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
# If main() exits via an abnormal exception, call into these
|
| 69 |
+
# handlers before exiting.
|
| 70 |
+
EXCEPTION_HANDLERS = []
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class Error(Exception):
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class UsageError(Error):
|
| 78 |
+
"""Exception raised when the arguments supplied by the user are invalid.
|
| 79 |
+
|
| 80 |
+
Raise this when the arguments supplied are invalid from the point of
|
| 81 |
+
view of the application. For example when two mutually exclusive
|
| 82 |
+
flags have been supplied or when there are not enough non-flag
|
| 83 |
+
arguments. It is distinct from flags.Error which covers the lower
|
| 84 |
+
level of parsing and validating individual flags.
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
def __init__(self, message, exitcode=1):
|
| 88 |
+
super().__init__(message)
|
| 89 |
+
self.exitcode = exitcode
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
class HelpFlag(flags.BooleanFlag):
|
| 93 |
+
"""Special boolean flag that displays usage and raises SystemExit."""
|
| 94 |
+
NAME = 'help'
|
| 95 |
+
SHORT_NAME = '?'
|
| 96 |
+
|
| 97 |
+
def __init__(self):
|
| 98 |
+
super().__init__(
|
| 99 |
+
self.NAME,
|
| 100 |
+
False,
|
| 101 |
+
'show this help',
|
| 102 |
+
short_name=self.SHORT_NAME,
|
| 103 |
+
allow_hide_cpp=True,
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
def parse(self, arg):
|
| 107 |
+
if self._parse(arg):
|
| 108 |
+
usage(shorthelp=True, writeto_stdout=True)
|
| 109 |
+
# Advertise --helpfull on stdout, since usage() was on stdout.
|
| 110 |
+
print()
|
| 111 |
+
print('Try --helpfull to get a list of all flags.')
|
| 112 |
+
sys.exit(1)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class HelpshortFlag(HelpFlag):
|
| 116 |
+
"""--helpshort is an alias for --help."""
|
| 117 |
+
NAME = 'helpshort'
|
| 118 |
+
SHORT_NAME = None
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class HelpfullFlag(flags.BooleanFlag):
|
| 122 |
+
"""Display help for flags in the main module and all dependent modules."""
|
| 123 |
+
|
| 124 |
+
def __init__(self):
|
| 125 |
+
super().__init__('helpfull', False, 'show full help', allow_hide_cpp=True)
|
| 126 |
+
|
| 127 |
+
def parse(self, arg):
|
| 128 |
+
if self._parse(arg):
|
| 129 |
+
usage(writeto_stdout=True)
|
| 130 |
+
sys.exit(1)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
class HelpXMLFlag(flags.BooleanFlag):
|
| 134 |
+
"""Similar to HelpfullFlag, but generates output in XML format."""
|
| 135 |
+
|
| 136 |
+
def __init__(self):
|
| 137 |
+
super().__init__(
|
| 138 |
+
'helpxml',
|
| 139 |
+
False,
|
| 140 |
+
'like --helpfull, but generates XML output',
|
| 141 |
+
allow_hide_cpp=True,
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
def parse(self, arg):
|
| 145 |
+
if self._parse(arg):
|
| 146 |
+
flags.FLAGS.write_help_in_xml_format(sys.stdout)
|
| 147 |
+
sys.exit(1)
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def parse_flags_with_usage(args):
|
| 151 |
+
"""Tries to parse the flags, print usage, and exit if unparsable.
|
| 152 |
+
|
| 153 |
+
Args:
|
| 154 |
+
args: [str], a non-empty list of the command line arguments including
|
| 155 |
+
program name.
|
| 156 |
+
|
| 157 |
+
Returns:
|
| 158 |
+
[str], a non-empty list of remaining command line arguments after parsing
|
| 159 |
+
flags, including program name.
|
| 160 |
+
"""
|
| 161 |
+
try:
|
| 162 |
+
return FLAGS(args)
|
| 163 |
+
except flags.Error as error:
|
| 164 |
+
message = str(error)
|
| 165 |
+
if '\n' in message:
|
| 166 |
+
final_message = 'FATAL Flags parsing error:\n%s\n' % textwrap.indent(
|
| 167 |
+
message, ' ')
|
| 168 |
+
else:
|
| 169 |
+
final_message = 'FATAL Flags parsing error: %s\n' % message
|
| 170 |
+
sys.stderr.write(final_message)
|
| 171 |
+
sys.stderr.write('Pass --helpshort or --helpfull to see help on flags.\n')
|
| 172 |
+
sys.exit(1)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
_define_help_flags_called = False
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def define_help_flags():
|
| 179 |
+
"""Registers help flags. Idempotent."""
|
| 180 |
+
# Use a global to ensure idempotence.
|
| 181 |
+
global _define_help_flags_called
|
| 182 |
+
|
| 183 |
+
if not _define_help_flags_called:
|
| 184 |
+
flags.DEFINE_flag(HelpFlag())
|
| 185 |
+
flags.DEFINE_flag(HelpshortFlag()) # alias for --help
|
| 186 |
+
flags.DEFINE_flag(HelpfullFlag())
|
| 187 |
+
flags.DEFINE_flag(HelpXMLFlag())
|
| 188 |
+
_define_help_flags_called = True
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _register_and_parse_flags_with_usage(
|
| 192 |
+
argv=None,
|
| 193 |
+
flags_parser=parse_flags_with_usage,
|
| 194 |
+
):
|
| 195 |
+
"""Registers help flags, parses arguments and shows usage if appropriate.
|
| 196 |
+
|
| 197 |
+
This also calls sys.exit(0) if flag --only_check_args is True.
|
| 198 |
+
|
| 199 |
+
Args:
|
| 200 |
+
argv: [str], a non-empty list of the command line arguments including
|
| 201 |
+
program name, sys.argv is used if None.
|
| 202 |
+
flags_parser: Callable[[List[str]], Any], the function used to parse flags.
|
| 203 |
+
The return value of this function is passed to `main` untouched. It must
|
| 204 |
+
guarantee FLAGS is parsed after this function is called.
|
| 205 |
+
|
| 206 |
+
Returns:
|
| 207 |
+
The return value of `flags_parser`. When using the default `flags_parser`,
|
| 208 |
+
it returns the following:
|
| 209 |
+
[str], a non-empty list of remaining command line arguments after parsing
|
| 210 |
+
flags, including program name.
|
| 211 |
+
|
| 212 |
+
Raises:
|
| 213 |
+
Error: Raised when flags_parser is called, but FLAGS is not parsed.
|
| 214 |
+
SystemError: Raised when it's called more than once.
|
| 215 |
+
"""
|
| 216 |
+
# fmt: on
|
| 217 |
+
if _register_and_parse_flags_with_usage.done:
|
| 218 |
+
raise SystemError('Flag registration can be done only once.')
|
| 219 |
+
|
| 220 |
+
define_help_flags()
|
| 221 |
+
|
| 222 |
+
original_argv = sys.argv if argv is None else argv
|
| 223 |
+
args_to_main = flags_parser(original_argv)
|
| 224 |
+
if not FLAGS.is_parsed():
|
| 225 |
+
raise Error('FLAGS must be parsed after flags_parser is called.')
|
| 226 |
+
|
| 227 |
+
# Exit when told so.
|
| 228 |
+
if FLAGS.only_check_args:
|
| 229 |
+
sys.exit(0)
|
| 230 |
+
# Immediately after flags are parsed, bump verbosity to INFO if the flag has
|
| 231 |
+
# not been set.
|
| 232 |
+
if FLAGS['verbosity'].using_default_value:
|
| 233 |
+
FLAGS.verbosity = 0
|
| 234 |
+
_register_and_parse_flags_with_usage.done = True
|
| 235 |
+
|
| 236 |
+
return args_to_main
|
| 237 |
+
|
| 238 |
+
_register_and_parse_flags_with_usage.done = False
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def _run_main(main, argv):
|
| 242 |
+
"""Calls main, optionally with pdb or profiler."""
|
| 243 |
+
if FLAGS.run_with_pdb:
|
| 244 |
+
sys.exit(pdb.runcall(main, argv))
|
| 245 |
+
elif FLAGS.run_with_profiling or FLAGS.profile_file:
|
| 246 |
+
# Avoid import overhead since most apps (including performance-sensitive
|
| 247 |
+
# ones) won't be run with profiling.
|
| 248 |
+
# pylint: disable=g-import-not-at-top
|
| 249 |
+
import atexit
|
| 250 |
+
if FLAGS.use_cprofile_for_profiling:
|
| 251 |
+
import cProfile as profile
|
| 252 |
+
else:
|
| 253 |
+
import profile
|
| 254 |
+
profiler = profile.Profile()
|
| 255 |
+
if FLAGS.profile_file:
|
| 256 |
+
atexit.register(profiler.dump_stats, FLAGS.profile_file)
|
| 257 |
+
else:
|
| 258 |
+
atexit.register(profiler.print_stats)
|
| 259 |
+
sys.exit(profiler.runcall(main, argv))
|
| 260 |
+
else:
|
| 261 |
+
sys.exit(main(argv))
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def _call_exception_handlers(exception):
|
| 265 |
+
"""Calls any installed exception handlers."""
|
| 266 |
+
for handler in EXCEPTION_HANDLERS:
|
| 267 |
+
try:
|
| 268 |
+
if handler.wants(exception):
|
| 269 |
+
handler.handle(exception)
|
| 270 |
+
except: # pylint: disable=bare-except
|
| 271 |
+
try:
|
| 272 |
+
# We don't want to stop for exceptions in the exception handlers but
|
| 273 |
+
# we shouldn't hide them either.
|
| 274 |
+
logging.error(traceback.format_exc())
|
| 275 |
+
except: # pylint: disable=bare-except
|
| 276 |
+
# In case even the logging statement fails, ignore.
|
| 277 |
+
pass
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
def run(
|
| 281 |
+
main,
|
| 282 |
+
argv=None,
|
| 283 |
+
flags_parser=parse_flags_with_usage,
|
| 284 |
+
):
|
| 285 |
+
"""Begins executing the program.
|
| 286 |
+
|
| 287 |
+
Args:
|
| 288 |
+
main: The main function to execute. It takes an single argument "argv",
|
| 289 |
+
which is a list of command line arguments with parsed flags removed.
|
| 290 |
+
The return value is passed to `sys.exit`, and so for example
|
| 291 |
+
a return value of 0 or None results in a successful termination, whereas
|
| 292 |
+
a return value of 1 results in abnormal termination.
|
| 293 |
+
For more details, see https://docs.python.org/3/library/sys#sys.exit
|
| 294 |
+
argv: A non-empty list of the command line arguments including program name,
|
| 295 |
+
sys.argv is used if None.
|
| 296 |
+
flags_parser: Callable[[List[str]], Any], the function used to parse flags.
|
| 297 |
+
The return value of this function is passed to `main` untouched.
|
| 298 |
+
It must guarantee FLAGS is parsed after this function is called.
|
| 299 |
+
Should be passed as a keyword-only arg which will become mandatory in a
|
| 300 |
+
future release.
|
| 301 |
+
- Parses command line flags with the flag module.
|
| 302 |
+
- If there are any errors, prints usage().
|
| 303 |
+
- Calls main() with the remaining arguments.
|
| 304 |
+
- If main() raises a UsageError, prints usage and the error message.
|
| 305 |
+
"""
|
| 306 |
+
# fmt: on
|
| 307 |
+
try:
|
| 308 |
+
args = _run_init(
|
| 309 |
+
sys.argv if argv is None else argv,
|
| 310 |
+
flags_parser,
|
| 311 |
+
)
|
| 312 |
+
while _init_callbacks:
|
| 313 |
+
callback = _init_callbacks.popleft()
|
| 314 |
+
callback()
|
| 315 |
+
try:
|
| 316 |
+
_run_main(main, args)
|
| 317 |
+
except UsageError as error:
|
| 318 |
+
usage(shorthelp=True, detailed_error=error, exitcode=error.exitcode)
|
| 319 |
+
except:
|
| 320 |
+
exc = sys.exc_info()[1]
|
| 321 |
+
# Don't try to post-mortem debug successful SystemExits, since those
|
| 322 |
+
# mean there wasn't actually an error. In particular, the test framework
|
| 323 |
+
# raises SystemExit(False) even if all tests passed.
|
| 324 |
+
if isinstance(exc, SystemExit) and not exc.code:
|
| 325 |
+
raise
|
| 326 |
+
|
| 327 |
+
# Check the tty so that we don't hang waiting for input in an
|
| 328 |
+
# non-interactive scenario.
|
| 329 |
+
if FLAGS.pdb_post_mortem and sys.stdout.isatty():
|
| 330 |
+
traceback.print_exc()
|
| 331 |
+
print()
|
| 332 |
+
print(' *** Entering post-mortem debugging ***')
|
| 333 |
+
print()
|
| 334 |
+
pdb.post_mortem()
|
| 335 |
+
raise
|
| 336 |
+
except Exception as e:
|
| 337 |
+
_call_exception_handlers(e)
|
| 338 |
+
raise
|
| 339 |
+
|
| 340 |
+
# Callbacks which have been deferred until after _run_init has been called.
|
| 341 |
+
_init_callbacks = collections.deque()
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def call_after_init(callback):
|
| 345 |
+
"""Calls the given callback only once ABSL has finished initialization.
|
| 346 |
+
|
| 347 |
+
If ABSL has already finished initialization when ``call_after_init`` is
|
| 348 |
+
called then the callback is executed immediately, otherwise `callback` is
|
| 349 |
+
stored to be executed after ``app.run`` has finished initializing (aka. just
|
| 350 |
+
before the main function is called).
|
| 351 |
+
|
| 352 |
+
If called after ``app.run``, this is equivalent to calling ``callback()`` in
|
| 353 |
+
the caller thread. If called before ``app.run``, callbacks are run
|
| 354 |
+
sequentially (in an undefined order) in the same thread as ``app.run``.
|
| 355 |
+
|
| 356 |
+
Args:
|
| 357 |
+
callback: a callable to be called once ABSL has finished initialization.
|
| 358 |
+
This may be immediate if initialization has already finished. It
|
| 359 |
+
takes no arguments and returns nothing.
|
| 360 |
+
"""
|
| 361 |
+
if _run_init.done:
|
| 362 |
+
callback()
|
| 363 |
+
else:
|
| 364 |
+
_init_callbacks.append(callback)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def _run_init(
|
| 368 |
+
argv,
|
| 369 |
+
flags_parser,
|
| 370 |
+
):
|
| 371 |
+
"""Does one-time initialization and re-parses flags on rerun."""
|
| 372 |
+
if _run_init.done:
|
| 373 |
+
return flags_parser(argv)
|
| 374 |
+
command_name.make_process_name_useful()
|
| 375 |
+
# Set up absl logging handler.
|
| 376 |
+
logging.use_absl_handler()
|
| 377 |
+
args = _register_and_parse_flags_with_usage(
|
| 378 |
+
argv=argv,
|
| 379 |
+
flags_parser=flags_parser,
|
| 380 |
+
)
|
| 381 |
+
if faulthandler:
|
| 382 |
+
try:
|
| 383 |
+
faulthandler.enable()
|
| 384 |
+
except Exception: # pylint: disable=broad-except
|
| 385 |
+
# Some tests verify stderr output very closely, so don't print anything.
|
| 386 |
+
# Disabled faulthandler is a low-impact error.
|
| 387 |
+
pass
|
| 388 |
+
_run_init.done = True
|
| 389 |
+
return args
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
_run_init.done = False
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
def usage(shorthelp=False, writeto_stdout=False, detailed_error=None,
|
| 396 |
+
exitcode=None):
|
| 397 |
+
"""Writes __main__'s docstring to stderr with some help text.
|
| 398 |
+
|
| 399 |
+
Args:
|
| 400 |
+
shorthelp: bool, if True, prints only flags from the main module,
|
| 401 |
+
rather than all flags.
|
| 402 |
+
writeto_stdout: bool, if True, writes help message to stdout,
|
| 403 |
+
rather than to stderr.
|
| 404 |
+
detailed_error: str, additional detail about why usage info was presented.
|
| 405 |
+
exitcode: optional integer, if set, exits with this status code after
|
| 406 |
+
writing help.
|
| 407 |
+
"""
|
| 408 |
+
if writeto_stdout:
|
| 409 |
+
stdfile = sys.stdout
|
| 410 |
+
else:
|
| 411 |
+
stdfile = sys.stderr
|
| 412 |
+
|
| 413 |
+
doc = sys.modules['__main__'].__doc__
|
| 414 |
+
if not doc:
|
| 415 |
+
doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
|
| 416 |
+
doc = flags.text_wrap(doc, indent=' ', firstline_indent='')
|
| 417 |
+
else:
|
| 418 |
+
# Replace all '%s' with sys.argv[0], and all '%%' with '%'.
|
| 419 |
+
num_specifiers = doc.count('%') - 2 * doc.count('%%')
|
| 420 |
+
try:
|
| 421 |
+
doc %= (sys.argv[0],) * num_specifiers
|
| 422 |
+
except (OverflowError, TypeError, ValueError):
|
| 423 |
+
# Just display the docstring as-is.
|
| 424 |
+
pass
|
| 425 |
+
if shorthelp:
|
| 426 |
+
flag_str = FLAGS.main_module_help()
|
| 427 |
+
else:
|
| 428 |
+
flag_str = FLAGS.get_help()
|
| 429 |
+
try:
|
| 430 |
+
stdfile.write(doc)
|
| 431 |
+
if flag_str:
|
| 432 |
+
stdfile.write('\nflags:\n')
|
| 433 |
+
stdfile.write(flag_str)
|
| 434 |
+
stdfile.write('\n')
|
| 435 |
+
if detailed_error is not None:
|
| 436 |
+
stdfile.write('\n%s\n' % detailed_error)
|
| 437 |
+
except OSError as e:
|
| 438 |
+
# We avoid printing a huge backtrace if we get EPIPE, because
|
| 439 |
+
# "foo.par --help | less" is a frequent use case.
|
| 440 |
+
if e.errno != errno.EPIPE:
|
| 441 |
+
raise
|
| 442 |
+
if exitcode is not None:
|
| 443 |
+
sys.exit(exitcode)
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
class ExceptionHandler:
|
| 447 |
+
"""Base exception handler from which other may inherit."""
|
| 448 |
+
|
| 449 |
+
def wants(self, exc):
|
| 450 |
+
"""Returns whether this handler wants to handle the exception or not.
|
| 451 |
+
|
| 452 |
+
This base class returns True for all exceptions by default. Override in
|
| 453 |
+
subclass if it wants to be more selective.
|
| 454 |
+
|
| 455 |
+
Args:
|
| 456 |
+
exc: Exception, the current exception.
|
| 457 |
+
"""
|
| 458 |
+
del exc # Unused.
|
| 459 |
+
return True
|
| 460 |
+
|
| 461 |
+
def handle(self, exc):
|
| 462 |
+
"""Do something with the current exception.
|
| 463 |
+
|
| 464 |
+
Args:
|
| 465 |
+
exc: Exception, the current exception
|
| 466 |
+
|
| 467 |
+
This method must be overridden.
|
| 468 |
+
"""
|
| 469 |
+
raise NotImplementedError()
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def install_exception_handler(handler):
|
| 473 |
+
"""Installs an exception handler.
|
| 474 |
+
|
| 475 |
+
Args:
|
| 476 |
+
handler: ExceptionHandler, the exception handler to install.
|
| 477 |
+
|
| 478 |
+
Raises:
|
| 479 |
+
TypeError: Raised when the handler was not of the correct type.
|
| 480 |
+
|
| 481 |
+
All installed exception handlers will be called if main() exits via
|
| 482 |
+
an abnormal exception, i.e. not one of SystemExit, KeyboardInterrupt,
|
| 483 |
+
FlagsError or UsageError.
|
| 484 |
+
"""
|
| 485 |
+
if not isinstance(handler, ExceptionHandler):
|
| 486 |
+
raise TypeError('handler of type %s does not inherit from ExceptionHandler'
|
| 487 |
+
% type(handler))
|
| 488 |
+
EXCEPTION_HANDLERS.append(handler)
|
lib/python3.10/site-packages/absl/app.pyi
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Callable, Collection, Iterable, List, NoReturn, Optional, TypeVar, Union, overload
|
| 2 |
+
|
| 3 |
+
from absl.flags import _flag
|
| 4 |
+
|
| 5 |
+
_MainArgs = TypeVar('_MainArgs')
|
| 6 |
+
_Exc = TypeVar('_Exc', bound=Exception)
|
| 7 |
+
|
| 8 |
+
class ExceptionHandler():
|
| 9 |
+
|
| 10 |
+
def wants(self, exc: _Exc) -> bool:
|
| 11 |
+
...
|
| 12 |
+
|
| 13 |
+
def handle(self, exc: _Exc):
|
| 14 |
+
...
|
| 15 |
+
|
| 16 |
+
EXCEPTION_HANDLERS: List[ExceptionHandler] = ...
|
| 17 |
+
|
| 18 |
+
class HelpFlag(_flag.BooleanFlag):
|
| 19 |
+
def __init__(self):
|
| 20 |
+
...
|
| 21 |
+
|
| 22 |
+
class HelpshortFlag(HelpFlag):
|
| 23 |
+
...
|
| 24 |
+
|
| 25 |
+
class HelpfullFlag(_flag.BooleanFlag):
|
| 26 |
+
def __init__(self):
|
| 27 |
+
...
|
| 28 |
+
|
| 29 |
+
class HelpXMLFlag(_flag.BooleanFlag):
|
| 30 |
+
def __init__(self):
|
| 31 |
+
...
|
| 32 |
+
|
| 33 |
+
def define_help_flags() -> None:
|
| 34 |
+
...
|
| 35 |
+
|
| 36 |
+
@overload
|
| 37 |
+
def usage(shorthelp: Union[bool, int] = ...,
|
| 38 |
+
writeto_stdout: Union[bool, int] = ...,
|
| 39 |
+
detailed_error: Optional[Any] = ...,
|
| 40 |
+
exitcode: None = ...) -> None:
|
| 41 |
+
...
|
| 42 |
+
|
| 43 |
+
@overload
|
| 44 |
+
def usage(shorthelp: Union[bool, int],
|
| 45 |
+
writeto_stdout: Union[bool, int],
|
| 46 |
+
detailed_error: Optional[Any],
|
| 47 |
+
exitcode: int) -> NoReturn:
|
| 48 |
+
...
|
| 49 |
+
|
| 50 |
+
@overload
|
| 51 |
+
def usage(shorthelp: Union[bool, int] = ...,
|
| 52 |
+
writeto_stdout: Union[bool, int] = ...,
|
| 53 |
+
detailed_error: Optional[Any] = ...,
|
| 54 |
+
*,
|
| 55 |
+
exitcode: int) -> NoReturn:
|
| 56 |
+
...
|
| 57 |
+
|
| 58 |
+
def install_exception_handler(handler: ExceptionHandler) -> None:
|
| 59 |
+
...
|
| 60 |
+
|
| 61 |
+
class Error(Exception):
|
| 62 |
+
...
|
| 63 |
+
|
| 64 |
+
class UsageError(Error):
|
| 65 |
+
exitcode: int
|
| 66 |
+
|
| 67 |
+
def parse_flags_with_usage(args: List[str]) -> List[str]:
|
| 68 |
+
...
|
| 69 |
+
|
| 70 |
+
def call_after_init(callback: Callable[[], Any]) -> None:
|
| 71 |
+
...
|
| 72 |
+
|
| 73 |
+
# Without the flag_parser argument, `main` should require a List[str].
|
| 74 |
+
@overload
|
| 75 |
+
def run(
|
| 76 |
+
main: Callable[[List[str]], Any],
|
| 77 |
+
argv: Optional[List[str]] = ...,
|
| 78 |
+
) -> NoReturn:
|
| 79 |
+
...
|
| 80 |
+
|
| 81 |
+
@overload
|
| 82 |
+
def run(
|
| 83 |
+
main: Callable[[_MainArgs], Any],
|
| 84 |
+
argv: Optional[List[str]] = ...,
|
| 85 |
+
*,
|
| 86 |
+
flags_parser: Callable[[List[str]], _MainArgs],
|
| 87 |
+
) -> NoReturn:
|
| 88 |
+
...
|
lib/python3.10/site-packages/absl/command_name.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""A tiny stand alone library to change the kernel process name on Linux."""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
import sys
|
| 19 |
+
|
| 20 |
+
# This library must be kept small and stand alone. It is used by small things
|
| 21 |
+
# that require no extension modules.
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def make_process_name_useful():
|
| 25 |
+
"""Sets the process name to something better than 'python' if possible."""
|
| 26 |
+
set_kernel_process_name(os.path.basename(sys.argv[0]))
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def set_kernel_process_name(name):
|
| 30 |
+
"""Changes the Kernel's /proc/self/status process name on Linux.
|
| 31 |
+
|
| 32 |
+
The kernel name is NOT what will be shown by the ps or top command.
|
| 33 |
+
It is a 15 character string stored in the kernel's process table that
|
| 34 |
+
is included in the kernel log when a process is OOM killed.
|
| 35 |
+
The first 15 bytes of name are used. Non-ASCII unicode is replaced with '?'.
|
| 36 |
+
|
| 37 |
+
Does nothing if /proc/self/comm cannot be written or prctl() fails.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
name: bytes|unicode, the Linux kernel's command name to set.
|
| 41 |
+
"""
|
| 42 |
+
if not isinstance(name, bytes):
|
| 43 |
+
name = name.encode('ascii', 'replace')
|
| 44 |
+
try:
|
| 45 |
+
# This is preferred to using ctypes to try and call prctl() when possible.
|
| 46 |
+
with open('/proc/self/comm', 'wb') as proc_comm:
|
| 47 |
+
proc_comm.write(name[:15])
|
| 48 |
+
except OSError:
|
| 49 |
+
try:
|
| 50 |
+
import ctypes # pylint: disable=g-import-not-at-top
|
| 51 |
+
except ImportError:
|
| 52 |
+
return # No ctypes.
|
| 53 |
+
try:
|
| 54 |
+
libc = ctypes.CDLL('libc.so.6')
|
| 55 |
+
except OSError:
|
| 56 |
+
return # No libc.so.6.
|
| 57 |
+
pr_set_name = ctypes.c_ulong(15) # linux/prctl.h PR_SET_NAME value.
|
| 58 |
+
zero = ctypes.c_ulong(0)
|
| 59 |
+
try:
|
| 60 |
+
libc.prctl(pr_set_name, name, zero, zero, zero)
|
| 61 |
+
# Ignore the prctl return value. Nothing we can do if it errored.
|
| 62 |
+
except AttributeError:
|
| 63 |
+
return # No prctl.
|
lib/python3.10/site-packages/absl/flags/__init__.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""This package is used to define and parse command line flags.
|
| 15 |
+
|
| 16 |
+
This package defines a *distributed* flag-definition policy: rather than
|
| 17 |
+
an application having to define all flags in or near main(), each Python
|
| 18 |
+
module defines flags that are useful to it. When one Python module
|
| 19 |
+
imports another, it gains access to the other's flags. (This is
|
| 20 |
+
implemented by having all modules share a common, global registry object
|
| 21 |
+
containing all the flag information.)
|
| 22 |
+
|
| 23 |
+
Flags are defined through the use of one of the DEFINE_xxx functions.
|
| 24 |
+
The specific function used determines how the flag is parsed, checked,
|
| 25 |
+
and optionally type-converted, when it's seen on the command line.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
import sys
|
| 29 |
+
|
| 30 |
+
from absl.flags import _argument_parser
|
| 31 |
+
from absl.flags import _defines
|
| 32 |
+
from absl.flags import _exceptions
|
| 33 |
+
from absl.flags import _flag
|
| 34 |
+
from absl.flags import _flagvalues
|
| 35 |
+
from absl.flags import _helpers
|
| 36 |
+
from absl.flags import _validators
|
| 37 |
+
|
| 38 |
+
__all__ = (
|
| 39 |
+
'DEFINE',
|
| 40 |
+
'DEFINE_flag',
|
| 41 |
+
'DEFINE_string',
|
| 42 |
+
'DEFINE_boolean',
|
| 43 |
+
'DEFINE_bool',
|
| 44 |
+
'DEFINE_float',
|
| 45 |
+
'DEFINE_integer',
|
| 46 |
+
'DEFINE_enum',
|
| 47 |
+
'DEFINE_enum_class',
|
| 48 |
+
'DEFINE_list',
|
| 49 |
+
'DEFINE_spaceseplist',
|
| 50 |
+
'DEFINE_multi',
|
| 51 |
+
'DEFINE_multi_string',
|
| 52 |
+
'DEFINE_multi_integer',
|
| 53 |
+
'DEFINE_multi_float',
|
| 54 |
+
'DEFINE_multi_enum',
|
| 55 |
+
'DEFINE_multi_enum_class',
|
| 56 |
+
'DEFINE_alias',
|
| 57 |
+
# Flag validators.
|
| 58 |
+
'register_validator',
|
| 59 |
+
'validator',
|
| 60 |
+
'register_multi_flags_validator',
|
| 61 |
+
'multi_flags_validator',
|
| 62 |
+
'mark_flag_as_required',
|
| 63 |
+
'mark_flags_as_required',
|
| 64 |
+
'mark_flags_as_mutual_exclusive',
|
| 65 |
+
'mark_bool_flags_as_mutual_exclusive',
|
| 66 |
+
# Flag modifiers.
|
| 67 |
+
'set_default',
|
| 68 |
+
'override_value',
|
| 69 |
+
# Key flag related functions.
|
| 70 |
+
'declare_key_flag',
|
| 71 |
+
'adopt_module_key_flags',
|
| 72 |
+
'disclaim_key_flags',
|
| 73 |
+
# Module exceptions.
|
| 74 |
+
'Error',
|
| 75 |
+
'CantOpenFlagFileError',
|
| 76 |
+
'DuplicateFlagError',
|
| 77 |
+
'IllegalFlagValueError',
|
| 78 |
+
'UnrecognizedFlagError',
|
| 79 |
+
'UnparsedFlagAccessError',
|
| 80 |
+
'ValidationError',
|
| 81 |
+
'FlagNameConflictsWithMethodError',
|
| 82 |
+
# Public classes.
|
| 83 |
+
'Flag',
|
| 84 |
+
'BooleanFlag',
|
| 85 |
+
'EnumFlag',
|
| 86 |
+
'EnumClassFlag',
|
| 87 |
+
'MultiFlag',
|
| 88 |
+
'MultiEnumClassFlag',
|
| 89 |
+
'FlagHolder',
|
| 90 |
+
'FlagValues',
|
| 91 |
+
'ArgumentParser',
|
| 92 |
+
'BooleanParser',
|
| 93 |
+
'EnumParser',
|
| 94 |
+
'EnumClassParser',
|
| 95 |
+
'ArgumentSerializer',
|
| 96 |
+
'FloatParser',
|
| 97 |
+
'IntegerParser',
|
| 98 |
+
'BaseListParser',
|
| 99 |
+
'ListParser',
|
| 100 |
+
'ListSerializer',
|
| 101 |
+
'EnumClassListSerializer',
|
| 102 |
+
'CsvListSerializer',
|
| 103 |
+
'WhitespaceSeparatedListParser',
|
| 104 |
+
'EnumClassSerializer',
|
| 105 |
+
# Helper functions.
|
| 106 |
+
'get_help_width',
|
| 107 |
+
'text_wrap',
|
| 108 |
+
'flag_dict_to_args',
|
| 109 |
+
'doc_to_help',
|
| 110 |
+
# The global FlagValues instance.
|
| 111 |
+
'FLAGS',
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# Initialize the FLAGS_MODULE as early as possible.
|
| 115 |
+
# It's only used by adopt_module_key_flags to take SPECIAL_FLAGS into account.
|
| 116 |
+
_helpers.FLAGS_MODULE = sys.modules[__name__]
|
| 117 |
+
|
| 118 |
+
# Add current module to disclaimed module ids.
|
| 119 |
+
_helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
|
| 120 |
+
|
| 121 |
+
# DEFINE functions. They are explained in more details in the module doc string.
|
| 122 |
+
# pylint: disable=invalid-name
|
| 123 |
+
DEFINE = _defines.DEFINE
|
| 124 |
+
DEFINE_flag = _defines.DEFINE_flag
|
| 125 |
+
DEFINE_string = _defines.DEFINE_string
|
| 126 |
+
DEFINE_boolean = _defines.DEFINE_boolean
|
| 127 |
+
DEFINE_bool = DEFINE_boolean # Match C++ API.
|
| 128 |
+
DEFINE_float = _defines.DEFINE_float
|
| 129 |
+
DEFINE_integer = _defines.DEFINE_integer
|
| 130 |
+
DEFINE_enum = _defines.DEFINE_enum
|
| 131 |
+
DEFINE_enum_class = _defines.DEFINE_enum_class
|
| 132 |
+
DEFINE_list = _defines.DEFINE_list
|
| 133 |
+
DEFINE_spaceseplist = _defines.DEFINE_spaceseplist
|
| 134 |
+
DEFINE_multi = _defines.DEFINE_multi
|
| 135 |
+
DEFINE_multi_string = _defines.DEFINE_multi_string
|
| 136 |
+
DEFINE_multi_integer = _defines.DEFINE_multi_integer
|
| 137 |
+
DEFINE_multi_float = _defines.DEFINE_multi_float
|
| 138 |
+
DEFINE_multi_enum = _defines.DEFINE_multi_enum
|
| 139 |
+
DEFINE_multi_enum_class = _defines.DEFINE_multi_enum_class
|
| 140 |
+
DEFINE_alias = _defines.DEFINE_alias
|
| 141 |
+
# pylint: enable=invalid-name
|
| 142 |
+
|
| 143 |
+
# Flag validators.
|
| 144 |
+
register_validator = _validators.register_validator
|
| 145 |
+
validator = _validators.validator
|
| 146 |
+
register_multi_flags_validator = _validators.register_multi_flags_validator
|
| 147 |
+
multi_flags_validator = _validators.multi_flags_validator
|
| 148 |
+
mark_flag_as_required = _validators.mark_flag_as_required
|
| 149 |
+
mark_flags_as_required = _validators.mark_flags_as_required
|
| 150 |
+
mark_flags_as_mutual_exclusive = _validators.mark_flags_as_mutual_exclusive
|
| 151 |
+
mark_bool_flags_as_mutual_exclusive = _validators.mark_bool_flags_as_mutual_exclusive
|
| 152 |
+
|
| 153 |
+
# Flag modifiers.
|
| 154 |
+
set_default = _defines.set_default
|
| 155 |
+
override_value = _defines.override_value
|
| 156 |
+
|
| 157 |
+
# Key flag related functions.
|
| 158 |
+
declare_key_flag = _defines.declare_key_flag
|
| 159 |
+
adopt_module_key_flags = _defines.adopt_module_key_flags
|
| 160 |
+
disclaim_key_flags = _defines.disclaim_key_flags
|
| 161 |
+
|
| 162 |
+
# Module exceptions.
|
| 163 |
+
# pylint: disable=invalid-name
|
| 164 |
+
Error = _exceptions.Error
|
| 165 |
+
CantOpenFlagFileError = _exceptions.CantOpenFlagFileError
|
| 166 |
+
DuplicateFlagError = _exceptions.DuplicateFlagError
|
| 167 |
+
IllegalFlagValueError = _exceptions.IllegalFlagValueError
|
| 168 |
+
UnrecognizedFlagError = _exceptions.UnrecognizedFlagError
|
| 169 |
+
UnparsedFlagAccessError = _exceptions.UnparsedFlagAccessError
|
| 170 |
+
ValidationError = _exceptions.ValidationError
|
| 171 |
+
FlagNameConflictsWithMethodError = _exceptions.FlagNameConflictsWithMethodError
|
| 172 |
+
|
| 173 |
+
# Public classes.
|
| 174 |
+
Flag = _flag.Flag
|
| 175 |
+
BooleanFlag = _flag.BooleanFlag
|
| 176 |
+
EnumFlag = _flag.EnumFlag
|
| 177 |
+
EnumClassFlag = _flag.EnumClassFlag
|
| 178 |
+
MultiFlag = _flag.MultiFlag
|
| 179 |
+
MultiEnumClassFlag = _flag.MultiEnumClassFlag
|
| 180 |
+
FlagHolder = _flagvalues.FlagHolder
|
| 181 |
+
FlagValues = _flagvalues.FlagValues
|
| 182 |
+
ArgumentParser = _argument_parser.ArgumentParser
|
| 183 |
+
BooleanParser = _argument_parser.BooleanParser
|
| 184 |
+
EnumParser = _argument_parser.EnumParser
|
| 185 |
+
EnumClassParser = _argument_parser.EnumClassParser
|
| 186 |
+
ArgumentSerializer = _argument_parser.ArgumentSerializer
|
| 187 |
+
FloatParser = _argument_parser.FloatParser
|
| 188 |
+
IntegerParser = _argument_parser.IntegerParser
|
| 189 |
+
BaseListParser = _argument_parser.BaseListParser
|
| 190 |
+
ListParser = _argument_parser.ListParser
|
| 191 |
+
ListSerializer = _argument_parser.ListSerializer
|
| 192 |
+
EnumClassListSerializer = _argument_parser.EnumClassListSerializer
|
| 193 |
+
CsvListSerializer = _argument_parser.CsvListSerializer
|
| 194 |
+
WhitespaceSeparatedListParser = _argument_parser.WhitespaceSeparatedListParser
|
| 195 |
+
EnumClassSerializer = _argument_parser.EnumClassSerializer
|
| 196 |
+
# pylint: enable=invalid-name
|
| 197 |
+
|
| 198 |
+
# Helper functions.
|
| 199 |
+
get_help_width = _helpers.get_help_width
|
| 200 |
+
text_wrap = _helpers.text_wrap
|
| 201 |
+
flag_dict_to_args = _helpers.flag_dict_to_args
|
| 202 |
+
doc_to_help = _helpers.doc_to_help
|
| 203 |
+
|
| 204 |
+
# Special flags.
|
| 205 |
+
_helpers.SPECIAL_FLAGS = FlagValues()
|
| 206 |
+
|
| 207 |
+
DEFINE_string(
|
| 208 |
+
'flagfile', '',
|
| 209 |
+
'Insert flag definitions from the given file into the command line.',
|
| 210 |
+
_helpers.SPECIAL_FLAGS) # pytype: disable=wrong-arg-types
|
| 211 |
+
|
| 212 |
+
DEFINE_string('undefok', '',
|
| 213 |
+
'comma-separated list of flag names that it is okay to specify '
|
| 214 |
+
'on the command line even if the program does not define a flag '
|
| 215 |
+
'with that name. IMPORTANT: flags in this list that have '
|
| 216 |
+
'arguments MUST use the --flag=value format.',
|
| 217 |
+
_helpers.SPECIAL_FLAGS) # pytype: disable=wrong-arg-types
|
| 218 |
+
|
| 219 |
+
#: The global FlagValues instance.
|
| 220 |
+
FLAGS = _flagvalues.FLAGS
|
lib/python3.10/site-packages/absl/flags/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (3.68 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_argument_parser.cpython-310.pyc
ADDED
|
Binary file (22.4 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_defines.cpython-310.pyc
ADDED
|
Binary file (39 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_exceptions.cpython-310.pyc
ADDED
|
Binary file (3.81 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_flag.cpython-310.pyc
ADDED
|
Binary file (18.6 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_flagvalues.cpython-310.pyc
ADDED
|
Binary file (43.9 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_helpers.cpython-310.pyc
ADDED
|
Binary file (10.1 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_validators.cpython-310.pyc
ADDED
|
Binary file (13.3 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/_validators_classes.cpython-310.pyc
ADDED
|
Binary file (6.74 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/__pycache__/argparse_flags.cpython-310.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
lib/python3.10/site-packages/absl/flags/_argument_parser.py
ADDED
|
@@ -0,0 +1,633 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Contains base classes used to parse and convert arguments.
|
| 16 |
+
|
| 17 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 18 |
+
aliases defined at the package level instead.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import collections
|
| 22 |
+
import csv
|
| 23 |
+
import enum
|
| 24 |
+
import io
|
| 25 |
+
import string
|
| 26 |
+
from typing import Any, Dict, Generic, Iterable, List, Optional, Sequence, Type, TypeVar, Union
|
| 27 |
+
from xml.dom import minidom
|
| 28 |
+
|
| 29 |
+
from absl.flags import _helpers
|
| 30 |
+
|
| 31 |
+
_T = TypeVar('_T')
|
| 32 |
+
_ET = TypeVar('_ET', bound=enum.Enum)
|
| 33 |
+
_N = TypeVar('_N', int, float)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class _ArgumentParserCache(type):
|
| 37 |
+
"""Metaclass used to cache and share argument parsers among flags."""
|
| 38 |
+
|
| 39 |
+
_instances: Dict[Any, Any] = {}
|
| 40 |
+
|
| 41 |
+
def __call__(cls, *args, **kwargs):
|
| 42 |
+
"""Returns an instance of the argument parser cls.
|
| 43 |
+
|
| 44 |
+
This method overrides behavior of the __new__ methods in
|
| 45 |
+
all subclasses of ArgumentParser (inclusive). If an instance
|
| 46 |
+
for cls with the same set of arguments exists, this instance is
|
| 47 |
+
returned, otherwise a new instance is created.
|
| 48 |
+
|
| 49 |
+
If any keyword arguments are defined, or the values in args
|
| 50 |
+
are not hashable, this method always returns a new instance of
|
| 51 |
+
cls.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
*args: Positional initializer arguments.
|
| 55 |
+
**kwargs: Initializer keyword arguments.
|
| 56 |
+
|
| 57 |
+
Returns:
|
| 58 |
+
An instance of cls, shared or new.
|
| 59 |
+
"""
|
| 60 |
+
if kwargs:
|
| 61 |
+
return type.__call__(cls, *args, **kwargs)
|
| 62 |
+
else:
|
| 63 |
+
instances = cls._instances
|
| 64 |
+
key = (cls,) + tuple(args)
|
| 65 |
+
try:
|
| 66 |
+
return instances[key]
|
| 67 |
+
except KeyError:
|
| 68 |
+
# No cache entry for key exists, create a new one.
|
| 69 |
+
return instances.setdefault(key, type.__call__(cls, *args))
|
| 70 |
+
except TypeError:
|
| 71 |
+
# An object in args cannot be hashed, always return
|
| 72 |
+
# a new instance.
|
| 73 |
+
return type.__call__(cls, *args)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class ArgumentParser(Generic[_T], metaclass=_ArgumentParserCache):
|
| 77 |
+
"""Base class used to parse and convert arguments.
|
| 78 |
+
|
| 79 |
+
The :meth:`parse` method checks to make sure that the string argument is a
|
| 80 |
+
legal value and convert it to a native type. If the value cannot be
|
| 81 |
+
converted, it should throw a ``ValueError`` exception with a human
|
| 82 |
+
readable explanation of why the value is illegal.
|
| 83 |
+
|
| 84 |
+
Subclasses should also define a syntactic_help string which may be
|
| 85 |
+
presented to the user to describe the form of the legal values.
|
| 86 |
+
|
| 87 |
+
Argument parser classes must be stateless, since instances are cached
|
| 88 |
+
and shared between flags. Initializer arguments are allowed, but all
|
| 89 |
+
member variables must be derived from initializer arguments only.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
syntactic_help: str = ''
|
| 93 |
+
|
| 94 |
+
def parse(self, argument: str) -> Optional[_T]:
|
| 95 |
+
"""Parses the string argument and returns the native value.
|
| 96 |
+
|
| 97 |
+
By default it returns its argument unmodified.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
argument: string argument passed in the commandline.
|
| 101 |
+
|
| 102 |
+
Raises:
|
| 103 |
+
ValueError: Raised when it fails to parse the argument.
|
| 104 |
+
TypeError: Raised when the argument has the wrong type.
|
| 105 |
+
|
| 106 |
+
Returns:
|
| 107 |
+
The parsed value in native type.
|
| 108 |
+
"""
|
| 109 |
+
if not isinstance(argument, str):
|
| 110 |
+
raise TypeError('flag value must be a string, found "{}"'.format(
|
| 111 |
+
type(argument)))
|
| 112 |
+
return argument # type: ignore[return-value]
|
| 113 |
+
|
| 114 |
+
def flag_type(self) -> str:
|
| 115 |
+
"""Returns a string representing the type of the flag."""
|
| 116 |
+
return 'string'
|
| 117 |
+
|
| 118 |
+
def _custom_xml_dom_elements(
|
| 119 |
+
self, doc: minidom.Document
|
| 120 |
+
) -> List[minidom.Element]:
|
| 121 |
+
"""Returns a list of minidom.Element to add additional flag information.
|
| 122 |
+
|
| 123 |
+
Args:
|
| 124 |
+
doc: minidom.Document, the DOM document it should create nodes from.
|
| 125 |
+
"""
|
| 126 |
+
del doc # Unused.
|
| 127 |
+
return []
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
class ArgumentSerializer(Generic[_T]):
|
| 131 |
+
"""Base class for generating string representations of a flag value."""
|
| 132 |
+
|
| 133 |
+
def serialize(self, value: _T) -> str:
|
| 134 |
+
"""Returns a serialized string of the value."""
|
| 135 |
+
return str(value)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class NumericParser(ArgumentParser[_N]):
|
| 139 |
+
"""Parser of numeric values.
|
| 140 |
+
|
| 141 |
+
Parsed value may be bounded to a given upper and lower bound.
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
lower_bound: Optional[_N]
|
| 145 |
+
upper_bound: Optional[_N]
|
| 146 |
+
|
| 147 |
+
def is_outside_bounds(self, val: _N) -> bool:
|
| 148 |
+
"""Returns whether the value is outside the bounds or not."""
|
| 149 |
+
return ((self.lower_bound is not None and val < self.lower_bound) or
|
| 150 |
+
(self.upper_bound is not None and val > self.upper_bound))
|
| 151 |
+
|
| 152 |
+
def parse(self, argument: Union[str, _N]) -> _N:
|
| 153 |
+
"""See base class."""
|
| 154 |
+
val = self.convert(argument)
|
| 155 |
+
if self.is_outside_bounds(val):
|
| 156 |
+
raise ValueError('%s is not %s' % (val, self.syntactic_help))
|
| 157 |
+
return val
|
| 158 |
+
|
| 159 |
+
def _custom_xml_dom_elements(
|
| 160 |
+
self, doc: minidom.Document
|
| 161 |
+
) -> List[minidom.Element]:
|
| 162 |
+
elements = []
|
| 163 |
+
if self.lower_bound is not None:
|
| 164 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 165 |
+
doc, 'lower_bound', self.lower_bound))
|
| 166 |
+
if self.upper_bound is not None:
|
| 167 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 168 |
+
doc, 'upper_bound', self.upper_bound))
|
| 169 |
+
return elements
|
| 170 |
+
|
| 171 |
+
def convert(self, argument: Union[str, _N]) -> _N:
|
| 172 |
+
"""Returns the correct numeric value of argument.
|
| 173 |
+
|
| 174 |
+
Subclass must implement this method, and raise TypeError if argument is not
|
| 175 |
+
string or has the right numeric type.
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
argument: string argument passed in the commandline, or the numeric type.
|
| 179 |
+
|
| 180 |
+
Raises:
|
| 181 |
+
TypeError: Raised when argument is not a string or the right numeric type.
|
| 182 |
+
ValueError: Raised when failed to convert argument to the numeric value.
|
| 183 |
+
"""
|
| 184 |
+
raise NotImplementedError
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class FloatParser(NumericParser[float]):
|
| 188 |
+
"""Parser of floating point values.
|
| 189 |
+
|
| 190 |
+
Parsed value may be bounded to a given upper and lower bound.
|
| 191 |
+
"""
|
| 192 |
+
number_article = 'a'
|
| 193 |
+
number_name = 'number'
|
| 194 |
+
syntactic_help = ' '.join((number_article, number_name))
|
| 195 |
+
|
| 196 |
+
def __init__(
|
| 197 |
+
self,
|
| 198 |
+
lower_bound: Optional[float] = None,
|
| 199 |
+
upper_bound: Optional[float] = None,
|
| 200 |
+
) -> None:
|
| 201 |
+
super().__init__()
|
| 202 |
+
self.lower_bound = lower_bound
|
| 203 |
+
self.upper_bound = upper_bound
|
| 204 |
+
sh = self.syntactic_help
|
| 205 |
+
if lower_bound is not None and upper_bound is not None:
|
| 206 |
+
sh = ('%s in the range [%s, %s]' % (sh, lower_bound, upper_bound))
|
| 207 |
+
elif lower_bound == 0:
|
| 208 |
+
sh = 'a non-negative %s' % self.number_name
|
| 209 |
+
elif upper_bound == 0:
|
| 210 |
+
sh = 'a non-positive %s' % self.number_name
|
| 211 |
+
elif upper_bound is not None:
|
| 212 |
+
sh = '%s <= %s' % (self.number_name, upper_bound)
|
| 213 |
+
elif lower_bound is not None:
|
| 214 |
+
sh = '%s >= %s' % (self.number_name, lower_bound)
|
| 215 |
+
self.syntactic_help = sh
|
| 216 |
+
|
| 217 |
+
def convert(self, argument: Union[int, float, str]) -> float:
|
| 218 |
+
"""Returns the float value of argument."""
|
| 219 |
+
if (
|
| 220 |
+
(isinstance(argument, int) and not isinstance(argument, bool))
|
| 221 |
+
or isinstance(argument, float)
|
| 222 |
+
or isinstance(argument, str)
|
| 223 |
+
):
|
| 224 |
+
return float(argument)
|
| 225 |
+
else:
|
| 226 |
+
raise TypeError(
|
| 227 |
+
'Expect argument to be a string, int, or float, found {}'.format(
|
| 228 |
+
type(argument)))
|
| 229 |
+
|
| 230 |
+
def flag_type(self) -> str:
|
| 231 |
+
"""See base class."""
|
| 232 |
+
return 'float'
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class IntegerParser(NumericParser[int]):
|
| 236 |
+
"""Parser of an integer value.
|
| 237 |
+
|
| 238 |
+
Parsed value may be bounded to a given upper and lower bound.
|
| 239 |
+
"""
|
| 240 |
+
number_article = 'an'
|
| 241 |
+
number_name = 'integer'
|
| 242 |
+
syntactic_help = ' '.join((number_article, number_name))
|
| 243 |
+
|
| 244 |
+
def __init__(
|
| 245 |
+
self, lower_bound: Optional[int] = None, upper_bound: Optional[int] = None
|
| 246 |
+
) -> None:
|
| 247 |
+
super().__init__()
|
| 248 |
+
self.lower_bound = lower_bound
|
| 249 |
+
self.upper_bound = upper_bound
|
| 250 |
+
sh = self.syntactic_help
|
| 251 |
+
if lower_bound is not None and upper_bound is not None:
|
| 252 |
+
sh = ('%s in the range [%s, %s]' % (sh, lower_bound, upper_bound))
|
| 253 |
+
elif lower_bound == 1:
|
| 254 |
+
sh = 'a positive %s' % self.number_name
|
| 255 |
+
elif upper_bound == -1:
|
| 256 |
+
sh = 'a negative %s' % self.number_name
|
| 257 |
+
elif lower_bound == 0:
|
| 258 |
+
sh = 'a non-negative %s' % self.number_name
|
| 259 |
+
elif upper_bound == 0:
|
| 260 |
+
sh = 'a non-positive %s' % self.number_name
|
| 261 |
+
elif upper_bound is not None:
|
| 262 |
+
sh = '%s <= %s' % (self.number_name, upper_bound)
|
| 263 |
+
elif lower_bound is not None:
|
| 264 |
+
sh = '%s >= %s' % (self.number_name, lower_bound)
|
| 265 |
+
self.syntactic_help = sh
|
| 266 |
+
|
| 267 |
+
def convert(self, argument: Union[int, str]) -> int:
|
| 268 |
+
"""Returns the int value of argument."""
|
| 269 |
+
if isinstance(argument, int) and not isinstance(argument, bool):
|
| 270 |
+
return argument
|
| 271 |
+
elif isinstance(argument, str):
|
| 272 |
+
base = 10
|
| 273 |
+
if len(argument) > 2 and argument[0] == '0':
|
| 274 |
+
if argument[1] == 'o':
|
| 275 |
+
base = 8
|
| 276 |
+
elif argument[1] == 'x':
|
| 277 |
+
base = 16
|
| 278 |
+
return int(argument, base)
|
| 279 |
+
else:
|
| 280 |
+
raise TypeError('Expect argument to be a string or int, found {}'.format(
|
| 281 |
+
type(argument)))
|
| 282 |
+
|
| 283 |
+
def flag_type(self) -> str:
|
| 284 |
+
"""See base class."""
|
| 285 |
+
return 'int'
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
class BooleanParser(ArgumentParser[bool]):
|
| 289 |
+
"""Parser of boolean values."""
|
| 290 |
+
|
| 291 |
+
def parse(self, argument: Union[str, int]) -> bool:
|
| 292 |
+
"""See base class."""
|
| 293 |
+
if isinstance(argument, str):
|
| 294 |
+
if argument.lower() in ('true', 't', '1'):
|
| 295 |
+
return True
|
| 296 |
+
elif argument.lower() in ('false', 'f', '0'):
|
| 297 |
+
return False
|
| 298 |
+
else:
|
| 299 |
+
raise ValueError('Non-boolean argument to boolean flag', argument)
|
| 300 |
+
elif isinstance(argument, int):
|
| 301 |
+
# Only allow bool or integer 0, 1.
|
| 302 |
+
# Note that float 1.0 == True, 0.0 == False.
|
| 303 |
+
bool_value = bool(argument)
|
| 304 |
+
if argument == bool_value:
|
| 305 |
+
return bool_value
|
| 306 |
+
else:
|
| 307 |
+
raise ValueError('Non-boolean argument to boolean flag', argument)
|
| 308 |
+
|
| 309 |
+
raise TypeError('Non-boolean argument to boolean flag', argument)
|
| 310 |
+
|
| 311 |
+
def flag_type(self) -> str:
|
| 312 |
+
"""See base class."""
|
| 313 |
+
return 'bool'
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
class EnumParser(ArgumentParser[str]):
|
| 317 |
+
"""Parser of a string enum value (a string value from a given set)."""
|
| 318 |
+
|
| 319 |
+
def __init__(
|
| 320 |
+
self, enum_values: Iterable[str], case_sensitive: bool = True
|
| 321 |
+
) -> None:
|
| 322 |
+
"""Initializes EnumParser.
|
| 323 |
+
|
| 324 |
+
Args:
|
| 325 |
+
enum_values: [str], a non-empty list of string values in the enum.
|
| 326 |
+
case_sensitive: bool, whether or not the enum is to be case-sensitive.
|
| 327 |
+
|
| 328 |
+
Raises:
|
| 329 |
+
ValueError: When enum_values is empty.
|
| 330 |
+
"""
|
| 331 |
+
if not enum_values:
|
| 332 |
+
raise ValueError(f'enum_values cannot be empty, found "{enum_values}"')
|
| 333 |
+
if isinstance(enum_values, str):
|
| 334 |
+
raise ValueError(f'enum_values cannot be a str, found "{enum_values}"')
|
| 335 |
+
super().__init__()
|
| 336 |
+
self.enum_values = list(enum_values)
|
| 337 |
+
self.case_sensitive = case_sensitive
|
| 338 |
+
|
| 339 |
+
def parse(self, argument: str) -> str:
|
| 340 |
+
"""Determines validity of argument and returns the correct element of enum.
|
| 341 |
+
|
| 342 |
+
Args:
|
| 343 |
+
argument: str, the supplied flag value.
|
| 344 |
+
|
| 345 |
+
Returns:
|
| 346 |
+
The first matching element from enum_values.
|
| 347 |
+
|
| 348 |
+
Raises:
|
| 349 |
+
ValueError: Raised when argument didn't match anything in enum.
|
| 350 |
+
"""
|
| 351 |
+
if self.case_sensitive:
|
| 352 |
+
if argument not in self.enum_values:
|
| 353 |
+
raise ValueError('value should be one of <%s>' %
|
| 354 |
+
'|'.join(self.enum_values))
|
| 355 |
+
else:
|
| 356 |
+
return argument
|
| 357 |
+
else:
|
| 358 |
+
if argument.upper() not in [value.upper() for value in self.enum_values]:
|
| 359 |
+
raise ValueError('value should be one of <%s>' %
|
| 360 |
+
'|'.join(self.enum_values))
|
| 361 |
+
else:
|
| 362 |
+
return [value for value in self.enum_values
|
| 363 |
+
if value.upper() == argument.upper()][0]
|
| 364 |
+
|
| 365 |
+
def flag_type(self) -> str:
|
| 366 |
+
"""See base class."""
|
| 367 |
+
return 'string enum'
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
class EnumClassParser(ArgumentParser[_ET]):
|
| 371 |
+
"""Parser of an Enum class member."""
|
| 372 |
+
|
| 373 |
+
def __init__(
|
| 374 |
+
self, enum_class: Type[_ET], case_sensitive: bool = True
|
| 375 |
+
) -> None:
|
| 376 |
+
"""Initializes EnumParser.
|
| 377 |
+
|
| 378 |
+
Args:
|
| 379 |
+
enum_class: class, the Enum class with all possible flag values.
|
| 380 |
+
case_sensitive: bool, whether or not the enum is to be case-sensitive. If
|
| 381 |
+
False, all member names must be unique when case is ignored.
|
| 382 |
+
|
| 383 |
+
Raises:
|
| 384 |
+
TypeError: When enum_class is not a subclass of Enum.
|
| 385 |
+
ValueError: When enum_class is empty.
|
| 386 |
+
"""
|
| 387 |
+
if not issubclass(enum_class, enum.Enum):
|
| 388 |
+
raise TypeError(f'{enum_class} is not a subclass of Enum.')
|
| 389 |
+
if not enum_class.__members__:
|
| 390 |
+
raise ValueError('enum_class cannot be empty, but "{}" is empty.'
|
| 391 |
+
.format(enum_class))
|
| 392 |
+
if not case_sensitive:
|
| 393 |
+
members = collections.Counter(
|
| 394 |
+
name.lower() for name in enum_class.__members__)
|
| 395 |
+
duplicate_keys = {
|
| 396 |
+
member for member, count in members.items() if count > 1
|
| 397 |
+
}
|
| 398 |
+
if duplicate_keys:
|
| 399 |
+
raise ValueError(
|
| 400 |
+
'Duplicate enum values for {} using case_sensitive=False'.format(
|
| 401 |
+
duplicate_keys))
|
| 402 |
+
|
| 403 |
+
super().__init__()
|
| 404 |
+
self.enum_class = enum_class
|
| 405 |
+
self._case_sensitive = case_sensitive
|
| 406 |
+
if case_sensitive:
|
| 407 |
+
self._member_names = tuple(enum_class.__members__)
|
| 408 |
+
else:
|
| 409 |
+
self._member_names = tuple(
|
| 410 |
+
name.lower() for name in enum_class.__members__)
|
| 411 |
+
|
| 412 |
+
@property
|
| 413 |
+
def member_names(self) -> Sequence[str]:
|
| 414 |
+
"""The accepted enum names, in lowercase if not case sensitive."""
|
| 415 |
+
return self._member_names
|
| 416 |
+
|
| 417 |
+
def parse(self, argument: Union[_ET, str]) -> _ET:
|
| 418 |
+
"""Determines validity of argument and returns the correct element of enum.
|
| 419 |
+
|
| 420 |
+
Args:
|
| 421 |
+
argument: str or Enum class member, the supplied flag value.
|
| 422 |
+
|
| 423 |
+
Returns:
|
| 424 |
+
The first matching Enum class member in Enum class.
|
| 425 |
+
|
| 426 |
+
Raises:
|
| 427 |
+
ValueError: Raised when argument didn't match anything in enum.
|
| 428 |
+
"""
|
| 429 |
+
if isinstance(argument, self.enum_class):
|
| 430 |
+
return argument # pytype: disable=bad-return-type
|
| 431 |
+
elif not isinstance(argument, str):
|
| 432 |
+
raise ValueError(
|
| 433 |
+
'{} is not an enum member or a name of a member in {}'.format(
|
| 434 |
+
argument, self.enum_class))
|
| 435 |
+
key = EnumParser(
|
| 436 |
+
self._member_names, case_sensitive=self._case_sensitive).parse(argument)
|
| 437 |
+
if self._case_sensitive:
|
| 438 |
+
return self.enum_class[key]
|
| 439 |
+
else:
|
| 440 |
+
# If EnumParser.parse() return a value, we're guaranteed to find it
|
| 441 |
+
# as a member of the class
|
| 442 |
+
return next(value for name, value in self.enum_class.__members__.items()
|
| 443 |
+
if name.lower() == key.lower())
|
| 444 |
+
|
| 445 |
+
def flag_type(self) -> str:
|
| 446 |
+
"""See base class."""
|
| 447 |
+
return 'enum class'
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
class ListSerializer(Generic[_T], ArgumentSerializer[List[_T]]):
|
| 451 |
+
|
| 452 |
+
def __init__(self, list_sep: str) -> None:
|
| 453 |
+
self.list_sep = list_sep
|
| 454 |
+
|
| 455 |
+
def serialize(self, value: List[_T]) -> str:
|
| 456 |
+
"""See base class."""
|
| 457 |
+
return self.list_sep.join([str(x) for x in value])
|
| 458 |
+
|
| 459 |
+
|
| 460 |
+
class EnumClassListSerializer(ListSerializer[_ET]):
|
| 461 |
+
"""A serializer for :class:`MultiEnumClass` flags.
|
| 462 |
+
|
| 463 |
+
This serializer simply joins the output of `EnumClassSerializer` using a
|
| 464 |
+
provided separator.
|
| 465 |
+
"""
|
| 466 |
+
|
| 467 |
+
_element_serializer: 'EnumClassSerializer'
|
| 468 |
+
|
| 469 |
+
def __init__(self, list_sep: str, **kwargs) -> None:
|
| 470 |
+
"""Initializes EnumClassListSerializer.
|
| 471 |
+
|
| 472 |
+
Args:
|
| 473 |
+
list_sep: String to be used as a separator when serializing
|
| 474 |
+
**kwargs: Keyword arguments to the `EnumClassSerializer` used to serialize
|
| 475 |
+
individual values.
|
| 476 |
+
"""
|
| 477 |
+
super().__init__(list_sep)
|
| 478 |
+
self._element_serializer = EnumClassSerializer(**kwargs)
|
| 479 |
+
|
| 480 |
+
def serialize(self, value: Union[_ET, List[_ET]]) -> str:
|
| 481 |
+
"""See base class."""
|
| 482 |
+
if isinstance(value, list):
|
| 483 |
+
return self.list_sep.join(
|
| 484 |
+
self._element_serializer.serialize(x) for x in value)
|
| 485 |
+
else:
|
| 486 |
+
return self._element_serializer.serialize(value)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
class CsvListSerializer(ListSerializer[str]):
|
| 490 |
+
|
| 491 |
+
def serialize(self, value: List[str]) -> str:
|
| 492 |
+
"""Serializes a list as a CSV string or unicode."""
|
| 493 |
+
output = io.StringIO()
|
| 494 |
+
writer = csv.writer(output, delimiter=self.list_sep)
|
| 495 |
+
writer.writerow([str(x) for x in value])
|
| 496 |
+
serialized_value = output.getvalue().strip()
|
| 497 |
+
|
| 498 |
+
# We need the returned value to be pure ascii or Unicodes so that
|
| 499 |
+
# when the xml help is generated they are usefully encodable.
|
| 500 |
+
return str(serialized_value)
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
class EnumClassSerializer(ArgumentSerializer[_ET]):
|
| 504 |
+
"""Class for generating string representations of an enum class flag value."""
|
| 505 |
+
|
| 506 |
+
def __init__(self, lowercase: bool) -> None:
|
| 507 |
+
"""Initializes EnumClassSerializer.
|
| 508 |
+
|
| 509 |
+
Args:
|
| 510 |
+
lowercase: If True, enum member names are lowercased during serialization.
|
| 511 |
+
"""
|
| 512 |
+
self._lowercase = lowercase
|
| 513 |
+
|
| 514 |
+
def serialize(self, value: _ET) -> str:
|
| 515 |
+
"""Returns a serialized string of the Enum class value."""
|
| 516 |
+
as_string = str(value.name)
|
| 517 |
+
return as_string.lower() if self._lowercase else as_string
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
class BaseListParser(ArgumentParser):
|
| 521 |
+
"""Base class for a parser of lists of strings.
|
| 522 |
+
|
| 523 |
+
To extend, inherit from this class; from the subclass ``__init__``, call::
|
| 524 |
+
|
| 525 |
+
super().__init__(token, name)
|
| 526 |
+
|
| 527 |
+
where token is a character used to tokenize, and name is a description
|
| 528 |
+
of the separator.
|
| 529 |
+
"""
|
| 530 |
+
|
| 531 |
+
def __init__(
|
| 532 |
+
self, token: Optional[str] = None, name: Optional[str] = None
|
| 533 |
+
) -> None:
|
| 534 |
+
assert name
|
| 535 |
+
super().__init__()
|
| 536 |
+
self._token = token
|
| 537 |
+
self._name = name
|
| 538 |
+
self.syntactic_help = 'a %s separated list' % self._name
|
| 539 |
+
|
| 540 |
+
def parse(self, argument: str) -> List[str]:
|
| 541 |
+
"""See base class."""
|
| 542 |
+
if isinstance(argument, list):
|
| 543 |
+
return argument
|
| 544 |
+
elif not argument:
|
| 545 |
+
return []
|
| 546 |
+
else:
|
| 547 |
+
return [s.strip() for s in argument.split(self._token)]
|
| 548 |
+
|
| 549 |
+
def flag_type(self) -> str:
|
| 550 |
+
"""See base class."""
|
| 551 |
+
return '%s separated list of strings' % self._name
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
class ListParser(BaseListParser):
|
| 555 |
+
"""Parser for a comma-separated list of strings."""
|
| 556 |
+
|
| 557 |
+
def __init__(self) -> None:
|
| 558 |
+
super().__init__(',', 'comma')
|
| 559 |
+
|
| 560 |
+
def parse(self, argument: Union[str, List[str]]) -> List[str]:
|
| 561 |
+
"""Parses argument as comma-separated list of strings."""
|
| 562 |
+
if isinstance(argument, list):
|
| 563 |
+
return argument
|
| 564 |
+
elif not argument:
|
| 565 |
+
return []
|
| 566 |
+
else:
|
| 567 |
+
try:
|
| 568 |
+
return [s.strip() for s in list(csv.reader([argument], strict=True))[0]]
|
| 569 |
+
except csv.Error as e:
|
| 570 |
+
# Provide a helpful report for case like
|
| 571 |
+
# --listflag="$(printf 'hello,\nworld')"
|
| 572 |
+
# IOW, list flag values containing naked newlines. This error
|
| 573 |
+
# was previously "reported" by allowing csv.Error to
|
| 574 |
+
# propagate.
|
| 575 |
+
raise ValueError('Unable to parse the value %r as a %s: %s'
|
| 576 |
+
% (argument, self.flag_type(), e))
|
| 577 |
+
|
| 578 |
+
def _custom_xml_dom_elements(
|
| 579 |
+
self, doc: minidom.Document
|
| 580 |
+
) -> List[minidom.Element]:
|
| 581 |
+
elements = super()._custom_xml_dom_elements(doc)
|
| 582 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 583 |
+
doc, 'list_separator', repr(',')))
|
| 584 |
+
return elements
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
class WhitespaceSeparatedListParser(BaseListParser):
|
| 588 |
+
"""Parser for a whitespace-separated list of strings."""
|
| 589 |
+
|
| 590 |
+
def __init__(self, comma_compat: bool = False) -> None:
|
| 591 |
+
"""Initializer.
|
| 592 |
+
|
| 593 |
+
Args:
|
| 594 |
+
comma_compat: bool, whether to support comma as an additional separator.
|
| 595 |
+
If False then only whitespace is supported. This is intended only for
|
| 596 |
+
backwards compatibility with flags that used to be comma-separated.
|
| 597 |
+
"""
|
| 598 |
+
self._comma_compat = comma_compat
|
| 599 |
+
name = 'whitespace or comma' if self._comma_compat else 'whitespace'
|
| 600 |
+
super().__init__(None, name)
|
| 601 |
+
|
| 602 |
+
def parse(self, argument: Union[str, List[str]]) -> List[str]:
|
| 603 |
+
"""Parses argument as whitespace-separated list of strings.
|
| 604 |
+
|
| 605 |
+
It also parses argument as comma-separated list of strings if requested.
|
| 606 |
+
|
| 607 |
+
Args:
|
| 608 |
+
argument: string argument passed in the commandline.
|
| 609 |
+
|
| 610 |
+
Returns:
|
| 611 |
+
[str], the parsed flag value.
|
| 612 |
+
"""
|
| 613 |
+
if isinstance(argument, list):
|
| 614 |
+
return argument
|
| 615 |
+
elif not argument:
|
| 616 |
+
return []
|
| 617 |
+
else:
|
| 618 |
+
if self._comma_compat:
|
| 619 |
+
argument = argument.replace(',', ' ')
|
| 620 |
+
return argument.split()
|
| 621 |
+
|
| 622 |
+
def _custom_xml_dom_elements(
|
| 623 |
+
self, doc: minidom.Document
|
| 624 |
+
) -> List[minidom.Element]:
|
| 625 |
+
elements = super()._custom_xml_dom_elements(doc)
|
| 626 |
+
separators = list(string.whitespace)
|
| 627 |
+
if self._comma_compat:
|
| 628 |
+
separators.append(',')
|
| 629 |
+
separators.sort()
|
| 630 |
+
for sep_char in separators:
|
| 631 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 632 |
+
doc, 'list_separator', repr(sep_char)))
|
| 633 |
+
return elements
|
lib/python3.10/site-packages/absl/flags/_defines.py
ADDED
|
@@ -0,0 +1,1702 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""This modules contains flags DEFINE functions.
|
| 15 |
+
|
| 16 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 17 |
+
aliases defined at the package level instead.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import enum
|
| 21 |
+
import sys
|
| 22 |
+
import types
|
| 23 |
+
from typing import Any, Iterable, List, Literal, Optional, Type, TypeVar, Union, overload
|
| 24 |
+
|
| 25 |
+
from absl.flags import _argument_parser
|
| 26 |
+
from absl.flags import _exceptions
|
| 27 |
+
from absl.flags import _flag
|
| 28 |
+
from absl.flags import _flagvalues
|
| 29 |
+
from absl.flags import _helpers
|
| 30 |
+
from absl.flags import _validators
|
| 31 |
+
|
| 32 |
+
_helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
|
| 33 |
+
|
| 34 |
+
_T = TypeVar('_T')
|
| 35 |
+
_ET = TypeVar('_ET', bound=enum.Enum)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _register_bounds_validator_if_needed(parser, name, flag_values):
|
| 39 |
+
"""Enforces lower and upper bounds for numeric flags.
|
| 40 |
+
|
| 41 |
+
Args:
|
| 42 |
+
parser: NumericParser (either FloatParser or IntegerParser), provides lower
|
| 43 |
+
and upper bounds, and help text to display.
|
| 44 |
+
name: str, name of the flag
|
| 45 |
+
flag_values: FlagValues.
|
| 46 |
+
"""
|
| 47 |
+
if parser.lower_bound is not None or parser.upper_bound is not None:
|
| 48 |
+
|
| 49 |
+
def checker(value):
|
| 50 |
+
if value is not None and parser.is_outside_bounds(value):
|
| 51 |
+
message = '%s is not %s' % (value, parser.syntactic_help)
|
| 52 |
+
raise _exceptions.ValidationError(message)
|
| 53 |
+
return True
|
| 54 |
+
|
| 55 |
+
_validators.register_validator(name, checker, flag_values=flag_values)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
@overload
|
| 59 |
+
def DEFINE( # pylint: disable=invalid-name
|
| 60 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 61 |
+
name: str,
|
| 62 |
+
default: Any,
|
| 63 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 64 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 65 |
+
serializer: Optional[_argument_parser.ArgumentSerializer[_T]] = ...,
|
| 66 |
+
module_name: Optional[str] = ...,
|
| 67 |
+
required: Literal[True] = ...,
|
| 68 |
+
**args: Any
|
| 69 |
+
) -> _flagvalues.FlagHolder[_T]:
|
| 70 |
+
...
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@overload
|
| 74 |
+
def DEFINE( # pylint: disable=invalid-name
|
| 75 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 76 |
+
name: str,
|
| 77 |
+
default: Optional[Any],
|
| 78 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 79 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 80 |
+
serializer: Optional[_argument_parser.ArgumentSerializer[_T]] = ...,
|
| 81 |
+
module_name: Optional[str] = ...,
|
| 82 |
+
required: bool = ...,
|
| 83 |
+
**args: Any
|
| 84 |
+
) -> _flagvalues.FlagHolder[Optional[_T]]:
|
| 85 |
+
...
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def DEFINE( # pylint: disable=invalid-name
|
| 89 |
+
parser,
|
| 90 |
+
name,
|
| 91 |
+
default,
|
| 92 |
+
help, # pylint: disable=redefined-builtin
|
| 93 |
+
flag_values=_flagvalues.FLAGS,
|
| 94 |
+
serializer=None,
|
| 95 |
+
module_name=None,
|
| 96 |
+
required=False,
|
| 97 |
+
**args):
|
| 98 |
+
"""Registers a generic Flag object.
|
| 99 |
+
|
| 100 |
+
NOTE: in the docstrings of all DEFINE* functions, "registers" is short
|
| 101 |
+
for "creates a new flag and registers it".
|
| 102 |
+
|
| 103 |
+
Auxiliary function: clients should use the specialized ``DEFINE_<type>``
|
| 104 |
+
function instead.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
parser: :class:`ArgumentParser`, used to parse the flag arguments.
|
| 108 |
+
name: str, the flag name.
|
| 109 |
+
default: The default value of the flag.
|
| 110 |
+
help: str, the help message.
|
| 111 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 112 |
+
flag will be registered. This should almost never need to be overridden.
|
| 113 |
+
serializer: :class:`ArgumentSerializer`, the flag serializer instance.
|
| 114 |
+
module_name: str, the name of the Python module declaring this flag. If not
|
| 115 |
+
provided, it will be computed using the stack trace of this call.
|
| 116 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 117 |
+
argument.
|
| 118 |
+
**args: dict, the extra keyword args that are passed to ``Flag.__init__``.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
a handle to defined flag.
|
| 122 |
+
"""
|
| 123 |
+
return DEFINE_flag(
|
| 124 |
+
_flag.Flag(parser, serializer, name, default, help, **args),
|
| 125 |
+
flag_values,
|
| 126 |
+
module_name,
|
| 127 |
+
required=True if required else False,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
@overload
|
| 132 |
+
def DEFINE_flag( # pylint: disable=invalid-name
|
| 133 |
+
flag: _flag.Flag[_T],
|
| 134 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 135 |
+
module_name: Optional[str] = ...,
|
| 136 |
+
required: Literal[True] = ...,
|
| 137 |
+
) -> _flagvalues.FlagHolder[_T]:
|
| 138 |
+
...
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
@overload
|
| 142 |
+
def DEFINE_flag( # pylint: disable=invalid-name
|
| 143 |
+
flag: _flag.Flag[_T],
|
| 144 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 145 |
+
module_name: Optional[str] = ...,
|
| 146 |
+
required: bool = ...,
|
| 147 |
+
) -> _flagvalues.FlagHolder[Optional[_T]]:
|
| 148 |
+
...
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def DEFINE_flag( # pylint: disable=invalid-name
|
| 152 |
+
flag,
|
| 153 |
+
flag_values=_flagvalues.FLAGS,
|
| 154 |
+
module_name=None,
|
| 155 |
+
required=False):
|
| 156 |
+
"""Registers a :class:`Flag` object with a :class:`FlagValues` object.
|
| 157 |
+
|
| 158 |
+
By default, the global :const:`FLAGS` ``FlagValue`` object is used.
|
| 159 |
+
|
| 160 |
+
Typical users will use one of the more specialized DEFINE_xxx
|
| 161 |
+
functions, such as :func:`DEFINE_string` or :func:`DEFINE_integer`. But
|
| 162 |
+
developers who need to create :class:`Flag` objects themselves should use
|
| 163 |
+
this function to register their flags.
|
| 164 |
+
|
| 165 |
+
Args:
|
| 166 |
+
flag: :class:`Flag`, a flag that is key to the module.
|
| 167 |
+
flag_values: :class:`FlagValues`, the ``FlagValues`` instance with which the
|
| 168 |
+
flag will be registered. This should almost never need to be overridden.
|
| 169 |
+
module_name: str, the name of the Python module declaring this flag. If not
|
| 170 |
+
provided, it will be computed using the stack trace of this call.
|
| 171 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 172 |
+
argument.
|
| 173 |
+
|
| 174 |
+
Returns:
|
| 175 |
+
a handle to defined flag.
|
| 176 |
+
"""
|
| 177 |
+
if required and flag.default is not None:
|
| 178 |
+
raise ValueError(
|
| 179 |
+
'Required flag --%s needs to have None as default' % flag.name
|
| 180 |
+
)
|
| 181 |
+
# Copying the reference to flag_values prevents pychecker warnings.
|
| 182 |
+
fv = flag_values
|
| 183 |
+
fv[flag.name] = flag
|
| 184 |
+
# Tell flag_values who's defining the flag.
|
| 185 |
+
if module_name:
|
| 186 |
+
module = sys.modules.get(module_name)
|
| 187 |
+
else:
|
| 188 |
+
module, module_name = _helpers.get_calling_module_object_and_name()
|
| 189 |
+
flag_values.register_flag_by_module(module_name, flag)
|
| 190 |
+
flag_values.register_flag_by_module_id(id(module), flag)
|
| 191 |
+
if required:
|
| 192 |
+
_validators.mark_flag_as_required(flag.name, fv)
|
| 193 |
+
ensure_non_none_value = (flag.default is not None) or required
|
| 194 |
+
return _flagvalues.FlagHolder(
|
| 195 |
+
fv, flag, ensure_non_none_value=ensure_non_none_value)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def set_default(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
|
| 199 |
+
"""Changes the default value of the provided flag object.
|
| 200 |
+
|
| 201 |
+
The flag's current value is also updated if the flag is currently using
|
| 202 |
+
the default value, i.e. not specified in the command line, and not set
|
| 203 |
+
by FLAGS.name = value.
|
| 204 |
+
|
| 205 |
+
Args:
|
| 206 |
+
flag_holder: FlagHolder, the flag to modify.
|
| 207 |
+
value: The new default value.
|
| 208 |
+
|
| 209 |
+
Raises:
|
| 210 |
+
IllegalFlagValueError: Raised when value is not valid.
|
| 211 |
+
"""
|
| 212 |
+
flag_holder._flagvalues.set_default(flag_holder.name, value) # pylint: disable=protected-access
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def override_value(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
|
| 216 |
+
"""Overrides the value of the provided flag.
|
| 217 |
+
|
| 218 |
+
This value takes precedent over the default value and, when called after flag
|
| 219 |
+
parsing, any value provided at the command line.
|
| 220 |
+
|
| 221 |
+
Args:
|
| 222 |
+
flag_holder: FlagHolder, the flag to modify.
|
| 223 |
+
value: The new value.
|
| 224 |
+
|
| 225 |
+
Raises:
|
| 226 |
+
IllegalFlagValueError: The value did not pass the flag parser or validators.
|
| 227 |
+
"""
|
| 228 |
+
fv = flag_holder._flagvalues # pylint: disable=protected-access
|
| 229 |
+
# Ensure the new value satisfies the flag's parser while avoiding side
|
| 230 |
+
# effects of calling parse().
|
| 231 |
+
parsed = fv[flag_holder.name]._parse(value) # pylint: disable=protected-access
|
| 232 |
+
if parsed != value:
|
| 233 |
+
raise _exceptions.IllegalFlagValueError(
|
| 234 |
+
'flag %s: parsed value %r not equal to original %r'
|
| 235 |
+
% (flag_holder.name, parsed, value)
|
| 236 |
+
)
|
| 237 |
+
setattr(fv, flag_holder.name, value)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def _internal_declare_key_flags(
|
| 241 |
+
flag_names: List[str],
|
| 242 |
+
flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,
|
| 243 |
+
key_flag_values: Optional[_flagvalues.FlagValues] = None,
|
| 244 |
+
) -> None:
|
| 245 |
+
"""Declares a flag as key for the calling module.
|
| 246 |
+
|
| 247 |
+
Internal function. User code should call declare_key_flag or
|
| 248 |
+
adopt_module_key_flags instead.
|
| 249 |
+
|
| 250 |
+
Args:
|
| 251 |
+
flag_names: [str], a list of names of already-registered Flag objects.
|
| 252 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 253 |
+
flags listed in flag_names have registered (the value of the flag_values
|
| 254 |
+
argument from the ``DEFINE_*`` calls that defined those flags). This
|
| 255 |
+
should almost never need to be overridden.
|
| 256 |
+
key_flag_values: :class:`FlagValues`, the FlagValues instance that (among
|
| 257 |
+
possibly many other things) keeps track of the key flags for each module.
|
| 258 |
+
Default ``None`` means "same as flag_values". This should almost never
|
| 259 |
+
need to be overridden.
|
| 260 |
+
|
| 261 |
+
Raises:
|
| 262 |
+
UnrecognizedFlagError: Raised when the flag is not defined.
|
| 263 |
+
"""
|
| 264 |
+
key_flag_values = key_flag_values or flag_values
|
| 265 |
+
|
| 266 |
+
module = _helpers.get_calling_module()
|
| 267 |
+
|
| 268 |
+
for flag_name in flag_names:
|
| 269 |
+
key_flag_values.register_key_flag_for_module(module, flag_values[flag_name])
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def declare_key_flag(
|
| 273 |
+
flag_name: Union[str, _flagvalues.FlagHolder],
|
| 274 |
+
flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,
|
| 275 |
+
) -> None:
|
| 276 |
+
"""Declares one flag as key to the current module.
|
| 277 |
+
|
| 278 |
+
Key flags are flags that are deemed really important for a module.
|
| 279 |
+
They are important when listing help messages; e.g., if the
|
| 280 |
+
--helpshort command-line flag is used, then only the key flags of the
|
| 281 |
+
main module are listed (instead of all flags, as in the case of
|
| 282 |
+
--helpfull).
|
| 283 |
+
|
| 284 |
+
Sample usage::
|
| 285 |
+
|
| 286 |
+
flags.declare_key_flag('flag_1')
|
| 287 |
+
|
| 288 |
+
Args:
|
| 289 |
+
flag_name: str | :class:`FlagHolder`, the name or holder of an already
|
| 290 |
+
declared flag. (Redeclaring flags as key, including flags implicitly key
|
| 291 |
+
because they were declared in this module, is a no-op.)
|
| 292 |
+
Positional-only parameter.
|
| 293 |
+
flag_values: :class:`FlagValues`, the FlagValues instance in which the
|
| 294 |
+
flag will be declared as a key flag. This should almost never need to be
|
| 295 |
+
overridden.
|
| 296 |
+
|
| 297 |
+
Raises:
|
| 298 |
+
ValueError: Raised if flag_name not defined as a Python flag.
|
| 299 |
+
"""
|
| 300 |
+
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
|
| 301 |
+
if flag_name in _helpers.SPECIAL_FLAGS:
|
| 302 |
+
# Take care of the special flags, e.g., --flagfile, --undefok.
|
| 303 |
+
# These flags are defined in SPECIAL_FLAGS, and are treated
|
| 304 |
+
# specially during flag parsing, taking precedence over the
|
| 305 |
+
# user-defined flags.
|
| 306 |
+
_internal_declare_key_flags([flag_name],
|
| 307 |
+
flag_values=_helpers.SPECIAL_FLAGS,
|
| 308 |
+
key_flag_values=flag_values)
|
| 309 |
+
return
|
| 310 |
+
try:
|
| 311 |
+
_internal_declare_key_flags([flag_name], flag_values=flag_values)
|
| 312 |
+
except KeyError:
|
| 313 |
+
raise ValueError('Flag --%s is undefined. To set a flag as a key flag '
|
| 314 |
+
'first define it in Python.' % flag_name)
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def adopt_module_key_flags(
|
| 318 |
+
module: Any, flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS
|
| 319 |
+
) -> None:
|
| 320 |
+
"""Declares that all flags key to a module are key to the current module.
|
| 321 |
+
|
| 322 |
+
Args:
|
| 323 |
+
module: module, the module object from which all key flags will be declared
|
| 324 |
+
as key flags to the current module.
|
| 325 |
+
flag_values: :class:`FlagValues`, the FlagValues instance in which the
|
| 326 |
+
flags will be declared as key flags. This should almost never need to be
|
| 327 |
+
overridden.
|
| 328 |
+
|
| 329 |
+
Raises:
|
| 330 |
+
Error: Raised when given an argument that is a module name (a string),
|
| 331 |
+
instead of a module object.
|
| 332 |
+
"""
|
| 333 |
+
if not isinstance(module, types.ModuleType):
|
| 334 |
+
raise _exceptions.Error('Expected a module object, not %r.' % (module,))
|
| 335 |
+
_internal_declare_key_flags(
|
| 336 |
+
[f.name for f in flag_values.get_key_flags_for_module(module.__name__)],
|
| 337 |
+
flag_values=flag_values)
|
| 338 |
+
# If module is this flag module, take _helpers.SPECIAL_FLAGS into account.
|
| 339 |
+
if module == _helpers.FLAGS_MODULE:
|
| 340 |
+
_internal_declare_key_flags(
|
| 341 |
+
# As we associate flags with get_calling_module_object_and_name(), the
|
| 342 |
+
# special flags defined in this module are incorrectly registered with
|
| 343 |
+
# a different module. So, we can't use get_key_flags_for_module.
|
| 344 |
+
# Instead, we take all flags from _helpers.SPECIAL_FLAGS (a private
|
| 345 |
+
# FlagValues, where no other module should register flags).
|
| 346 |
+
[_helpers.SPECIAL_FLAGS[name].name for name in _helpers.SPECIAL_FLAGS],
|
| 347 |
+
flag_values=_helpers.SPECIAL_FLAGS,
|
| 348 |
+
key_flag_values=flag_values)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
def disclaim_key_flags() -> None:
|
| 352 |
+
"""Declares that the current module will not define any more key flags.
|
| 353 |
+
|
| 354 |
+
Normally, the module that calls the DEFINE_xxx functions claims the
|
| 355 |
+
flag to be its key flag. This is undesirable for modules that
|
| 356 |
+
define additional DEFINE_yyy functions with its own flag parsers and
|
| 357 |
+
serializers, since that module will accidentally claim flags defined
|
| 358 |
+
by DEFINE_yyy as its key flags. After calling this function, the
|
| 359 |
+
module disclaims flag definitions thereafter, so the key flags will
|
| 360 |
+
be correctly attributed to the caller of DEFINE_yyy.
|
| 361 |
+
|
| 362 |
+
After calling this function, the module will not be able to define
|
| 363 |
+
any more flags. This function will affect all FlagValues objects.
|
| 364 |
+
"""
|
| 365 |
+
globals_for_caller = sys._getframe(1).f_globals # pylint: disable=protected-access
|
| 366 |
+
module = _helpers.get_module_object_and_name(globals_for_caller)
|
| 367 |
+
if module is not None:
|
| 368 |
+
_helpers.disclaim_module_ids.add(id(module.module))
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
@overload
|
| 372 |
+
def DEFINE_string( # pylint: disable=invalid-name
|
| 373 |
+
name: str,
|
| 374 |
+
default: Optional[str],
|
| 375 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 376 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 377 |
+
*,
|
| 378 |
+
required: Literal[True],
|
| 379 |
+
**args: Any
|
| 380 |
+
) -> _flagvalues.FlagHolder[str]:
|
| 381 |
+
...
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
@overload
|
| 385 |
+
def DEFINE_string( # pylint: disable=invalid-name
|
| 386 |
+
name: str,
|
| 387 |
+
default: None,
|
| 388 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 389 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 390 |
+
required: bool = ...,
|
| 391 |
+
**args: Any
|
| 392 |
+
) -> _flagvalues.FlagHolder[Optional[str]]:
|
| 393 |
+
...
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
@overload
|
| 397 |
+
def DEFINE_string( # pylint: disable=invalid-name
|
| 398 |
+
name: str,
|
| 399 |
+
default: str,
|
| 400 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 401 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 402 |
+
required: bool = ...,
|
| 403 |
+
**args: Any
|
| 404 |
+
) -> _flagvalues.FlagHolder[str]:
|
| 405 |
+
...
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def DEFINE_string( # pylint: disable=invalid-name
|
| 409 |
+
name,
|
| 410 |
+
default,
|
| 411 |
+
help, # pylint: disable=redefined-builtin
|
| 412 |
+
flag_values=_flagvalues.FLAGS,
|
| 413 |
+
required=False,
|
| 414 |
+
**args
|
| 415 |
+
):
|
| 416 |
+
"""Registers a flag whose value can be any string."""
|
| 417 |
+
parser = _argument_parser.ArgumentParser[str]()
|
| 418 |
+
serializer = _argument_parser.ArgumentSerializer[str]()
|
| 419 |
+
return DEFINE(
|
| 420 |
+
parser,
|
| 421 |
+
name,
|
| 422 |
+
default,
|
| 423 |
+
help,
|
| 424 |
+
flag_values,
|
| 425 |
+
serializer,
|
| 426 |
+
required=True if required else False,
|
| 427 |
+
**args,
|
| 428 |
+
)
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
@overload
|
| 432 |
+
def DEFINE_boolean( # pylint: disable=invalid-name
|
| 433 |
+
name: str,
|
| 434 |
+
default: Union[None, str, bool, int],
|
| 435 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 436 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 437 |
+
module_name: Optional[str] = ...,
|
| 438 |
+
*,
|
| 439 |
+
required: Literal[True],
|
| 440 |
+
**args: Any
|
| 441 |
+
) -> _flagvalues.FlagHolder[bool]:
|
| 442 |
+
...
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
@overload
|
| 446 |
+
def DEFINE_boolean( # pylint: disable=invalid-name
|
| 447 |
+
name: str,
|
| 448 |
+
default: None,
|
| 449 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 450 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 451 |
+
module_name: Optional[str] = ...,
|
| 452 |
+
required: bool = ...,
|
| 453 |
+
**args: Any
|
| 454 |
+
) -> _flagvalues.FlagHolder[Optional[bool]]:
|
| 455 |
+
...
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
@overload
|
| 459 |
+
def DEFINE_boolean( # pylint: disable=invalid-name
|
| 460 |
+
name: str,
|
| 461 |
+
default: Union[str, bool, int],
|
| 462 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 463 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 464 |
+
module_name: Optional[str] = ...,
|
| 465 |
+
required: bool = ...,
|
| 466 |
+
**args: Any
|
| 467 |
+
) -> _flagvalues.FlagHolder[bool]:
|
| 468 |
+
...
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
# pytype: disable=bad-return-type
|
| 472 |
+
def DEFINE_boolean( # pylint: disable=invalid-name
|
| 473 |
+
name,
|
| 474 |
+
default,
|
| 475 |
+
help, # pylint: disable=redefined-builtin
|
| 476 |
+
flag_values=_flagvalues.FLAGS,
|
| 477 |
+
module_name=None,
|
| 478 |
+
required=False,
|
| 479 |
+
**args
|
| 480 |
+
):
|
| 481 |
+
"""Registers a boolean flag.
|
| 482 |
+
|
| 483 |
+
Such a boolean flag does not take an argument. If a user wants to
|
| 484 |
+
specify a false value explicitly, the long option beginning with 'no'
|
| 485 |
+
must be used: i.e. --noflag
|
| 486 |
+
|
| 487 |
+
This flag will have a value of None, True or False. None is possible
|
| 488 |
+
if default=None and the user does not specify the flag on the command
|
| 489 |
+
line.
|
| 490 |
+
|
| 491 |
+
Args:
|
| 492 |
+
name: str, the flag name.
|
| 493 |
+
default: bool|str|None, the default value of the flag.
|
| 494 |
+
help: str, the help message.
|
| 495 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 496 |
+
flag will be registered. This should almost never need to be overridden.
|
| 497 |
+
module_name: str, the name of the Python module declaring this flag. If not
|
| 498 |
+
provided, it will be computed using the stack trace of this call.
|
| 499 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 500 |
+
argument.
|
| 501 |
+
**args: dict, the extra keyword args that are passed to ``Flag.__init__``.
|
| 502 |
+
|
| 503 |
+
Returns:
|
| 504 |
+
a handle to defined flag.
|
| 505 |
+
"""
|
| 506 |
+
return DEFINE_flag(
|
| 507 |
+
_flag.BooleanFlag(name, default, help, **args),
|
| 508 |
+
flag_values,
|
| 509 |
+
module_name,
|
| 510 |
+
required=True if required else False,
|
| 511 |
+
)
|
| 512 |
+
|
| 513 |
+
|
| 514 |
+
@overload
|
| 515 |
+
def DEFINE_float( # pylint: disable=invalid-name
|
| 516 |
+
name: str,
|
| 517 |
+
default: Union[None, float, str],
|
| 518 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 519 |
+
lower_bound: Optional[float] = ...,
|
| 520 |
+
upper_bound: Optional[float] = ...,
|
| 521 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 522 |
+
*,
|
| 523 |
+
required: Literal[True],
|
| 524 |
+
**args: Any
|
| 525 |
+
) -> _flagvalues.FlagHolder[float]:
|
| 526 |
+
...
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
@overload
|
| 530 |
+
def DEFINE_float( # pylint: disable=invalid-name
|
| 531 |
+
name: str,
|
| 532 |
+
default: None,
|
| 533 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 534 |
+
lower_bound: Optional[float] = ...,
|
| 535 |
+
upper_bound: Optional[float] = ...,
|
| 536 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 537 |
+
required: bool = ...,
|
| 538 |
+
**args: Any
|
| 539 |
+
) -> _flagvalues.FlagHolder[Optional[float]]:
|
| 540 |
+
...
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
@overload
|
| 544 |
+
def DEFINE_float( # pylint: disable=invalid-name
|
| 545 |
+
name: str,
|
| 546 |
+
default: Union[float, str],
|
| 547 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 548 |
+
lower_bound: Optional[float] = ...,
|
| 549 |
+
upper_bound: Optional[float] = ...,
|
| 550 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 551 |
+
required: bool = ...,
|
| 552 |
+
**args: Any
|
| 553 |
+
) -> _flagvalues.FlagHolder[float]:
|
| 554 |
+
...
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
def DEFINE_float( # pylint: disable=invalid-name
|
| 558 |
+
name,
|
| 559 |
+
default,
|
| 560 |
+
help, # pylint: disable=redefined-builtin
|
| 561 |
+
lower_bound=None,
|
| 562 |
+
upper_bound=None,
|
| 563 |
+
flag_values=_flagvalues.FLAGS,
|
| 564 |
+
required=False,
|
| 565 |
+
**args
|
| 566 |
+
):
|
| 567 |
+
"""Registers a flag whose value must be a float.
|
| 568 |
+
|
| 569 |
+
If ``lower_bound`` or ``upper_bound`` are set, then this flag must be
|
| 570 |
+
within the given range.
|
| 571 |
+
|
| 572 |
+
Args:
|
| 573 |
+
name: str, the flag name.
|
| 574 |
+
default: float|str|None, the default value of the flag.
|
| 575 |
+
help: str, the help message.
|
| 576 |
+
lower_bound: float, min value of the flag.
|
| 577 |
+
upper_bound: float, max value of the flag.
|
| 578 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 579 |
+
flag will be registered. This should almost never need to be overridden.
|
| 580 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 581 |
+
argument.
|
| 582 |
+
**args: dict, the extra keyword args that are passed to :func:`DEFINE`.
|
| 583 |
+
|
| 584 |
+
Returns:
|
| 585 |
+
a handle to defined flag.
|
| 586 |
+
"""
|
| 587 |
+
parser = _argument_parser.FloatParser(lower_bound, upper_bound)
|
| 588 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 589 |
+
result = DEFINE(
|
| 590 |
+
parser,
|
| 591 |
+
name,
|
| 592 |
+
default,
|
| 593 |
+
help, # pylint: disable=redefined-builtin
|
| 594 |
+
flag_values,
|
| 595 |
+
serializer,
|
| 596 |
+
required=True if required else False,
|
| 597 |
+
**args,
|
| 598 |
+
)
|
| 599 |
+
_register_bounds_validator_if_needed(parser, name, flag_values=flag_values)
|
| 600 |
+
return result
|
| 601 |
+
|
| 602 |
+
|
| 603 |
+
@overload
|
| 604 |
+
def DEFINE_integer( # pylint: disable=invalid-name
|
| 605 |
+
name: str,
|
| 606 |
+
default: Union[None, int, str],
|
| 607 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 608 |
+
lower_bound: Optional[int] = ...,
|
| 609 |
+
upper_bound: Optional[int] = ...,
|
| 610 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 611 |
+
*,
|
| 612 |
+
required: Literal[True],
|
| 613 |
+
**args: Any
|
| 614 |
+
) -> _flagvalues.FlagHolder[int]:
|
| 615 |
+
...
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
@overload
|
| 619 |
+
def DEFINE_integer( # pylint: disable=invalid-name
|
| 620 |
+
name: str,
|
| 621 |
+
default: None,
|
| 622 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 623 |
+
lower_bound: Optional[int] = ...,
|
| 624 |
+
upper_bound: Optional[int] = ...,
|
| 625 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 626 |
+
required: bool = ...,
|
| 627 |
+
**args: Any
|
| 628 |
+
) -> _flagvalues.FlagHolder[Optional[int]]:
|
| 629 |
+
...
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
@overload
|
| 633 |
+
def DEFINE_integer( # pylint: disable=invalid-name
|
| 634 |
+
name: str,
|
| 635 |
+
default: Union[int, str],
|
| 636 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 637 |
+
lower_bound: Optional[int] = ...,
|
| 638 |
+
upper_bound: Optional[int] = ...,
|
| 639 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 640 |
+
required: bool = ...,
|
| 641 |
+
**args: Any
|
| 642 |
+
) -> _flagvalues.FlagHolder[int]:
|
| 643 |
+
...
|
| 644 |
+
|
| 645 |
+
|
| 646 |
+
def DEFINE_integer( # pylint: disable=invalid-name
|
| 647 |
+
name,
|
| 648 |
+
default,
|
| 649 |
+
help, # pylint: disable=redefined-builtin
|
| 650 |
+
lower_bound=None,
|
| 651 |
+
upper_bound=None,
|
| 652 |
+
flag_values=_flagvalues.FLAGS,
|
| 653 |
+
required=False,
|
| 654 |
+
**args
|
| 655 |
+
):
|
| 656 |
+
"""Registers a flag whose value must be an integer.
|
| 657 |
+
|
| 658 |
+
If ``lower_bound``, or ``upper_bound`` are set, then this flag must be
|
| 659 |
+
within the given range.
|
| 660 |
+
|
| 661 |
+
Args:
|
| 662 |
+
name: str, the flag name.
|
| 663 |
+
default: int|str|None, the default value of the flag.
|
| 664 |
+
help: str, the help message.
|
| 665 |
+
lower_bound: int, min value of the flag.
|
| 666 |
+
upper_bound: int, max value of the flag.
|
| 667 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 668 |
+
flag will be registered. This should almost never need to be overridden.
|
| 669 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 670 |
+
argument.
|
| 671 |
+
**args: dict, the extra keyword args that are passed to :func:`DEFINE`.
|
| 672 |
+
|
| 673 |
+
Returns:
|
| 674 |
+
a handle to defined flag.
|
| 675 |
+
"""
|
| 676 |
+
parser = _argument_parser.IntegerParser(lower_bound, upper_bound)
|
| 677 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 678 |
+
result = DEFINE(
|
| 679 |
+
parser,
|
| 680 |
+
name,
|
| 681 |
+
default,
|
| 682 |
+
help, # pylint: disable=redefined-builtin
|
| 683 |
+
flag_values,
|
| 684 |
+
serializer,
|
| 685 |
+
required=True if required else False,
|
| 686 |
+
**args,
|
| 687 |
+
)
|
| 688 |
+
_register_bounds_validator_if_needed(parser, name, flag_values=flag_values)
|
| 689 |
+
return result
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
@overload
|
| 693 |
+
def DEFINE_enum( # pylint: disable=invalid-name
|
| 694 |
+
name: str,
|
| 695 |
+
default: Optional[str],
|
| 696 |
+
enum_values: Iterable[str],
|
| 697 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 698 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 699 |
+
module_name: Optional[str] = ...,
|
| 700 |
+
*,
|
| 701 |
+
required: Literal[True],
|
| 702 |
+
**args: Any
|
| 703 |
+
) -> _flagvalues.FlagHolder[str]:
|
| 704 |
+
...
|
| 705 |
+
|
| 706 |
+
|
| 707 |
+
@overload
|
| 708 |
+
def DEFINE_enum( # pylint: disable=invalid-name
|
| 709 |
+
name: str,
|
| 710 |
+
default: None,
|
| 711 |
+
enum_values: Iterable[str],
|
| 712 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 713 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 714 |
+
module_name: Optional[str] = ...,
|
| 715 |
+
required: bool = ...,
|
| 716 |
+
**args: Any
|
| 717 |
+
) -> _flagvalues.FlagHolder[Optional[str]]:
|
| 718 |
+
...
|
| 719 |
+
|
| 720 |
+
|
| 721 |
+
@overload
|
| 722 |
+
def DEFINE_enum( # pylint: disable=invalid-name
|
| 723 |
+
name: str,
|
| 724 |
+
default: str,
|
| 725 |
+
enum_values: Iterable[str],
|
| 726 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 727 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 728 |
+
module_name: Optional[str] = ...,
|
| 729 |
+
required: bool = ...,
|
| 730 |
+
**args: Any
|
| 731 |
+
) -> _flagvalues.FlagHolder[str]:
|
| 732 |
+
...
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
def DEFINE_enum( # pylint: disable=invalid-name
|
| 736 |
+
name,
|
| 737 |
+
default,
|
| 738 |
+
enum_values,
|
| 739 |
+
help, # pylint: disable=redefined-builtin
|
| 740 |
+
flag_values=_flagvalues.FLAGS,
|
| 741 |
+
module_name=None,
|
| 742 |
+
required=False,
|
| 743 |
+
**args
|
| 744 |
+
):
|
| 745 |
+
"""Registers a flag whose value can be any string from enum_values.
|
| 746 |
+
|
| 747 |
+
Instead of a string enum, prefer `DEFINE_enum_class`, which allows
|
| 748 |
+
defining enums from an `enum.Enum` class.
|
| 749 |
+
|
| 750 |
+
Args:
|
| 751 |
+
name: str, the flag name.
|
| 752 |
+
default: str|None, the default value of the flag.
|
| 753 |
+
enum_values: [str], a non-empty list of strings with the possible values for
|
| 754 |
+
the flag.
|
| 755 |
+
help: str, the help message.
|
| 756 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 757 |
+
flag will be registered. This should almost never need to be overridden.
|
| 758 |
+
module_name: str, the name of the Python module declaring this flag. If not
|
| 759 |
+
provided, it will be computed using the stack trace of this call.
|
| 760 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 761 |
+
argument.
|
| 762 |
+
**args: dict, the extra keyword args that are passed to ``Flag.__init__``.
|
| 763 |
+
|
| 764 |
+
Returns:
|
| 765 |
+
a handle to defined flag.
|
| 766 |
+
"""
|
| 767 |
+
result = DEFINE_flag(
|
| 768 |
+
_flag.EnumFlag(name, default, help, enum_values, **args),
|
| 769 |
+
flag_values,
|
| 770 |
+
module_name,
|
| 771 |
+
required=True if required else False,
|
| 772 |
+
)
|
| 773 |
+
return result
|
| 774 |
+
|
| 775 |
+
|
| 776 |
+
@overload
|
| 777 |
+
def DEFINE_enum_class( # pylint: disable=invalid-name
|
| 778 |
+
name: str,
|
| 779 |
+
default: Union[None, _ET, str],
|
| 780 |
+
enum_class: Type[_ET],
|
| 781 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 782 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 783 |
+
module_name: Optional[str] = ...,
|
| 784 |
+
case_sensitive: bool = ...,
|
| 785 |
+
*,
|
| 786 |
+
required: Literal[True],
|
| 787 |
+
**args: Any
|
| 788 |
+
) -> _flagvalues.FlagHolder[_ET]:
|
| 789 |
+
...
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
@overload
|
| 793 |
+
def DEFINE_enum_class( # pylint: disable=invalid-name
|
| 794 |
+
name: str,
|
| 795 |
+
default: None,
|
| 796 |
+
enum_class: Type[_ET],
|
| 797 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 798 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 799 |
+
module_name: Optional[str] = ...,
|
| 800 |
+
case_sensitive: bool = ...,
|
| 801 |
+
required: bool = ...,
|
| 802 |
+
**args: Any
|
| 803 |
+
) -> _flagvalues.FlagHolder[Optional[_ET]]:
|
| 804 |
+
...
|
| 805 |
+
|
| 806 |
+
|
| 807 |
+
@overload
|
| 808 |
+
def DEFINE_enum_class( # pylint: disable=invalid-name
|
| 809 |
+
name: str,
|
| 810 |
+
default: Union[_ET, str],
|
| 811 |
+
enum_class: Type[_ET],
|
| 812 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 813 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 814 |
+
module_name: Optional[str] = ...,
|
| 815 |
+
case_sensitive: bool = ...,
|
| 816 |
+
required: bool = ...,
|
| 817 |
+
**args: Any
|
| 818 |
+
) -> _flagvalues.FlagHolder[_ET]:
|
| 819 |
+
...
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
def DEFINE_enum_class( # pylint: disable=invalid-name
|
| 823 |
+
name,
|
| 824 |
+
default,
|
| 825 |
+
enum_class,
|
| 826 |
+
help, # pylint: disable=redefined-builtin
|
| 827 |
+
flag_values=_flagvalues.FLAGS,
|
| 828 |
+
module_name=None,
|
| 829 |
+
case_sensitive=False,
|
| 830 |
+
required=False,
|
| 831 |
+
**args
|
| 832 |
+
):
|
| 833 |
+
"""Registers a flag whose value can be the name of enum members.
|
| 834 |
+
|
| 835 |
+
Args:
|
| 836 |
+
name: str, the flag name.
|
| 837 |
+
default: Enum|str|None, the default value of the flag.
|
| 838 |
+
enum_class: class, the Enum class with all the possible values for the flag.
|
| 839 |
+
help: str, the help message.
|
| 840 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 841 |
+
flag will be registered. This should almost never need to be overridden.
|
| 842 |
+
module_name: str, the name of the Python module declaring this flag. If not
|
| 843 |
+
provided, it will be computed using the stack trace of this call.
|
| 844 |
+
case_sensitive: bool, whether to map strings to members of the enum_class
|
| 845 |
+
without considering case.
|
| 846 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 847 |
+
argument.
|
| 848 |
+
**args: dict, the extra keyword args that are passed to ``Flag.__init__``.
|
| 849 |
+
|
| 850 |
+
Returns:
|
| 851 |
+
a handle to defined flag.
|
| 852 |
+
"""
|
| 853 |
+
# NOTE: pytype fails if this is a direct return.
|
| 854 |
+
result = DEFINE_flag(
|
| 855 |
+
_flag.EnumClassFlag(
|
| 856 |
+
name, default, help, enum_class, case_sensitive=case_sensitive, **args
|
| 857 |
+
),
|
| 858 |
+
flag_values,
|
| 859 |
+
module_name,
|
| 860 |
+
required=True if required else False,
|
| 861 |
+
)
|
| 862 |
+
return result
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
@overload
|
| 866 |
+
def DEFINE_list( # pylint: disable=invalid-name
|
| 867 |
+
name: str,
|
| 868 |
+
default: Union[None, Iterable[str], str],
|
| 869 |
+
help: str, # pylint: disable=redefined-builtin
|
| 870 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 871 |
+
*,
|
| 872 |
+
required: Literal[True],
|
| 873 |
+
**args: Any
|
| 874 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 875 |
+
...
|
| 876 |
+
|
| 877 |
+
|
| 878 |
+
@overload
|
| 879 |
+
def DEFINE_list( # pylint: disable=invalid-name
|
| 880 |
+
name: str,
|
| 881 |
+
default: None,
|
| 882 |
+
help: str, # pylint: disable=redefined-builtin
|
| 883 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 884 |
+
required: bool = ...,
|
| 885 |
+
**args: Any
|
| 886 |
+
) -> _flagvalues.FlagHolder[Optional[List[str]]]:
|
| 887 |
+
...
|
| 888 |
+
|
| 889 |
+
|
| 890 |
+
@overload
|
| 891 |
+
def DEFINE_list( # pylint: disable=invalid-name
|
| 892 |
+
name: str,
|
| 893 |
+
default: Union[Iterable[str], str],
|
| 894 |
+
help: str, # pylint: disable=redefined-builtin
|
| 895 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 896 |
+
required: bool = ...,
|
| 897 |
+
**args: Any
|
| 898 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 899 |
+
...
|
| 900 |
+
|
| 901 |
+
|
| 902 |
+
def DEFINE_list( # pylint: disable=invalid-name
|
| 903 |
+
name,
|
| 904 |
+
default,
|
| 905 |
+
help, # pylint: disable=redefined-builtin
|
| 906 |
+
flag_values=_flagvalues.FLAGS,
|
| 907 |
+
required=False,
|
| 908 |
+
**args
|
| 909 |
+
):
|
| 910 |
+
"""Registers a flag whose value is a comma-separated list of strings.
|
| 911 |
+
|
| 912 |
+
The flag value is parsed with a CSV parser.
|
| 913 |
+
|
| 914 |
+
Args:
|
| 915 |
+
name: str, the flag name.
|
| 916 |
+
default: list|str|None, the default value of the flag.
|
| 917 |
+
help: str, the help message.
|
| 918 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 919 |
+
flag will be registered. This should almost never need to be overridden.
|
| 920 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 921 |
+
argument.
|
| 922 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 923 |
+
``Flag.__init__``.
|
| 924 |
+
|
| 925 |
+
Returns:
|
| 926 |
+
a handle to defined flag.
|
| 927 |
+
"""
|
| 928 |
+
parser = _argument_parser.ListParser()
|
| 929 |
+
serializer = _argument_parser.CsvListSerializer(',')
|
| 930 |
+
return DEFINE(
|
| 931 |
+
parser,
|
| 932 |
+
name,
|
| 933 |
+
default,
|
| 934 |
+
help,
|
| 935 |
+
flag_values,
|
| 936 |
+
serializer,
|
| 937 |
+
required=True if required else False,
|
| 938 |
+
**args,
|
| 939 |
+
)
|
| 940 |
+
|
| 941 |
+
|
| 942 |
+
@overload
|
| 943 |
+
def DEFINE_spaceseplist( # pylint: disable=invalid-name
|
| 944 |
+
name: str,
|
| 945 |
+
default: Union[None, Iterable[str], str],
|
| 946 |
+
help: str, # pylint: disable=redefined-builtin
|
| 947 |
+
comma_compat: bool = ...,
|
| 948 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 949 |
+
*,
|
| 950 |
+
required: Literal[True],
|
| 951 |
+
**args: Any
|
| 952 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 953 |
+
...
|
| 954 |
+
|
| 955 |
+
|
| 956 |
+
@overload
|
| 957 |
+
def DEFINE_spaceseplist( # pylint: disable=invalid-name
|
| 958 |
+
name: str,
|
| 959 |
+
default: None,
|
| 960 |
+
help: str, # pylint: disable=redefined-builtin
|
| 961 |
+
comma_compat: bool = ...,
|
| 962 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 963 |
+
required: bool = ...,
|
| 964 |
+
**args: Any
|
| 965 |
+
) -> _flagvalues.FlagHolder[Optional[List[str]]]:
|
| 966 |
+
...
|
| 967 |
+
|
| 968 |
+
|
| 969 |
+
@overload
|
| 970 |
+
def DEFINE_spaceseplist( # pylint: disable=invalid-name
|
| 971 |
+
name: str,
|
| 972 |
+
default: Union[Iterable[str], str],
|
| 973 |
+
help: str, # pylint: disable=redefined-builtin
|
| 974 |
+
comma_compat: bool = ...,
|
| 975 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 976 |
+
required: bool = ...,
|
| 977 |
+
**args: Any
|
| 978 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 979 |
+
...
|
| 980 |
+
|
| 981 |
+
|
| 982 |
+
def DEFINE_spaceseplist( # pylint: disable=invalid-name
|
| 983 |
+
name,
|
| 984 |
+
default,
|
| 985 |
+
help, # pylint: disable=redefined-builtin
|
| 986 |
+
comma_compat=False,
|
| 987 |
+
flag_values=_flagvalues.FLAGS,
|
| 988 |
+
required=False,
|
| 989 |
+
**args
|
| 990 |
+
):
|
| 991 |
+
"""Registers a flag whose value is a whitespace-separated list of strings.
|
| 992 |
+
|
| 993 |
+
Any whitespace can be used as a separator.
|
| 994 |
+
|
| 995 |
+
Args:
|
| 996 |
+
name: str, the flag name.
|
| 997 |
+
default: list|str|None, the default value of the flag.
|
| 998 |
+
help: str, the help message.
|
| 999 |
+
comma_compat: bool - Whether to support comma as an additional separator. If
|
| 1000 |
+
false then only whitespace is supported. This is intended only for
|
| 1001 |
+
backwards compatibility with flags that used to be comma-separated.
|
| 1002 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1003 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1004 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1005 |
+
argument.
|
| 1006 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1007 |
+
``Flag.__init__``.
|
| 1008 |
+
|
| 1009 |
+
Returns:
|
| 1010 |
+
a handle to defined flag.
|
| 1011 |
+
"""
|
| 1012 |
+
parser = _argument_parser.WhitespaceSeparatedListParser(
|
| 1013 |
+
comma_compat=comma_compat)
|
| 1014 |
+
serializer = _argument_parser.ListSerializer(' ')
|
| 1015 |
+
return DEFINE(
|
| 1016 |
+
parser,
|
| 1017 |
+
name,
|
| 1018 |
+
default,
|
| 1019 |
+
help,
|
| 1020 |
+
flag_values,
|
| 1021 |
+
serializer,
|
| 1022 |
+
required=True if required else False,
|
| 1023 |
+
**args,
|
| 1024 |
+
)
|
| 1025 |
+
|
| 1026 |
+
|
| 1027 |
+
@overload
|
| 1028 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1029 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 1030 |
+
serializer: _argument_parser.ArgumentSerializer[_T],
|
| 1031 |
+
name: str,
|
| 1032 |
+
default: Iterable[_T],
|
| 1033 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1034 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1035 |
+
module_name: Optional[str] = ...,
|
| 1036 |
+
*,
|
| 1037 |
+
required: Literal[True],
|
| 1038 |
+
**args: Any
|
| 1039 |
+
) -> _flagvalues.FlagHolder[List[_T]]:
|
| 1040 |
+
...
|
| 1041 |
+
|
| 1042 |
+
|
| 1043 |
+
@overload
|
| 1044 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1045 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 1046 |
+
serializer: _argument_parser.ArgumentSerializer[_T],
|
| 1047 |
+
name: str,
|
| 1048 |
+
default: Union[None, _T],
|
| 1049 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1050 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1051 |
+
module_name: Optional[str] = ...,
|
| 1052 |
+
*,
|
| 1053 |
+
required: Literal[True],
|
| 1054 |
+
**args: Any
|
| 1055 |
+
) -> _flagvalues.FlagHolder[List[_T]]:
|
| 1056 |
+
...
|
| 1057 |
+
|
| 1058 |
+
|
| 1059 |
+
@overload
|
| 1060 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1061 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 1062 |
+
serializer: _argument_parser.ArgumentSerializer[_T],
|
| 1063 |
+
name: str,
|
| 1064 |
+
default: None,
|
| 1065 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1066 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1067 |
+
module_name: Optional[str] = ...,
|
| 1068 |
+
required: bool = ...,
|
| 1069 |
+
**args: Any
|
| 1070 |
+
) -> _flagvalues.FlagHolder[Optional[List[_T]]]:
|
| 1071 |
+
...
|
| 1072 |
+
|
| 1073 |
+
|
| 1074 |
+
@overload
|
| 1075 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1076 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 1077 |
+
serializer: _argument_parser.ArgumentSerializer[_T],
|
| 1078 |
+
name: str,
|
| 1079 |
+
default: Iterable[_T],
|
| 1080 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1081 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1082 |
+
module_name: Optional[str] = ...,
|
| 1083 |
+
required: bool = ...,
|
| 1084 |
+
**args: Any
|
| 1085 |
+
) -> _flagvalues.FlagHolder[List[_T]]:
|
| 1086 |
+
...
|
| 1087 |
+
|
| 1088 |
+
|
| 1089 |
+
@overload
|
| 1090 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1091 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 1092 |
+
serializer: _argument_parser.ArgumentSerializer[_T],
|
| 1093 |
+
name: str,
|
| 1094 |
+
default: _T,
|
| 1095 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1096 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1097 |
+
module_name: Optional[str] = ...,
|
| 1098 |
+
required: bool = ...,
|
| 1099 |
+
**args: Any
|
| 1100 |
+
) -> _flagvalues.FlagHolder[List[_T]]:
|
| 1101 |
+
...
|
| 1102 |
+
|
| 1103 |
+
|
| 1104 |
+
def DEFINE_multi( # pylint: disable=invalid-name
|
| 1105 |
+
parser,
|
| 1106 |
+
serializer,
|
| 1107 |
+
name,
|
| 1108 |
+
default,
|
| 1109 |
+
help, # pylint: disable=redefined-builtin
|
| 1110 |
+
flag_values=_flagvalues.FLAGS,
|
| 1111 |
+
module_name=None,
|
| 1112 |
+
required=False,
|
| 1113 |
+
**args
|
| 1114 |
+
):
|
| 1115 |
+
"""Registers a generic MultiFlag that parses its args with a given parser.
|
| 1116 |
+
|
| 1117 |
+
Auxiliary function. Normal users should NOT use it directly.
|
| 1118 |
+
|
| 1119 |
+
Developers who need to create their own 'Parser' classes for options
|
| 1120 |
+
which can appear multiple times can call this module function to
|
| 1121 |
+
register their flags.
|
| 1122 |
+
|
| 1123 |
+
Args:
|
| 1124 |
+
parser: ArgumentParser, used to parse the flag arguments.
|
| 1125 |
+
serializer: ArgumentSerializer, the flag serializer instance.
|
| 1126 |
+
name: str, the flag name.
|
| 1127 |
+
default: Union[Iterable[T], str, None], the default value of the flag. If
|
| 1128 |
+
the value is text, it will be parsed as if it was provided from the
|
| 1129 |
+
command line. If the value is a non-string iterable, it will be iterated
|
| 1130 |
+
over to create a shallow copy of the values. If it is None, it is left
|
| 1131 |
+
as-is.
|
| 1132 |
+
help: str, the help message.
|
| 1133 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1134 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1135 |
+
module_name: A string, the name of the Python module declaring this flag. If
|
| 1136 |
+
not provided, it will be computed using the stack trace of this call.
|
| 1137 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1138 |
+
argument.
|
| 1139 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1140 |
+
``Flag.__init__``.
|
| 1141 |
+
|
| 1142 |
+
Returns:
|
| 1143 |
+
a handle to defined flag.
|
| 1144 |
+
"""
|
| 1145 |
+
result = DEFINE_flag(
|
| 1146 |
+
_flag.MultiFlag(parser, serializer, name, default, help, **args),
|
| 1147 |
+
flag_values,
|
| 1148 |
+
module_name,
|
| 1149 |
+
required=True if required else False,
|
| 1150 |
+
)
|
| 1151 |
+
return result
|
| 1152 |
+
|
| 1153 |
+
|
| 1154 |
+
@overload
|
| 1155 |
+
def DEFINE_multi_string( # pylint: disable=invalid-name
|
| 1156 |
+
name: str,
|
| 1157 |
+
default: Union[None, Iterable[str], str],
|
| 1158 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1159 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1160 |
+
*,
|
| 1161 |
+
required: Literal[True],
|
| 1162 |
+
**args: Any
|
| 1163 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 1164 |
+
...
|
| 1165 |
+
|
| 1166 |
+
|
| 1167 |
+
@overload
|
| 1168 |
+
def DEFINE_multi_string( # pylint: disable=invalid-name
|
| 1169 |
+
name: str,
|
| 1170 |
+
default: None,
|
| 1171 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1172 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1173 |
+
required: bool = ...,
|
| 1174 |
+
**args: Any
|
| 1175 |
+
) -> _flagvalues.FlagHolder[Optional[List[str]]]:
|
| 1176 |
+
...
|
| 1177 |
+
|
| 1178 |
+
|
| 1179 |
+
@overload
|
| 1180 |
+
def DEFINE_multi_string( # pylint: disable=invalid-name
|
| 1181 |
+
name: str,
|
| 1182 |
+
default: Union[Iterable[str], str],
|
| 1183 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1184 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1185 |
+
required: bool = ...,
|
| 1186 |
+
**args: Any
|
| 1187 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 1188 |
+
...
|
| 1189 |
+
|
| 1190 |
+
|
| 1191 |
+
def DEFINE_multi_string( # pylint: disable=invalid-name
|
| 1192 |
+
name,
|
| 1193 |
+
default,
|
| 1194 |
+
help, # pylint: disable=redefined-builtin
|
| 1195 |
+
flag_values=_flagvalues.FLAGS,
|
| 1196 |
+
required=False,
|
| 1197 |
+
**args
|
| 1198 |
+
):
|
| 1199 |
+
"""Registers a flag whose value can be a list of any strings.
|
| 1200 |
+
|
| 1201 |
+
Use the flag on the command line multiple times to place multiple
|
| 1202 |
+
string values into the list. The 'default' may be a single string
|
| 1203 |
+
(which will be converted into a single-element list) or a list of
|
| 1204 |
+
strings.
|
| 1205 |
+
|
| 1206 |
+
|
| 1207 |
+
Args:
|
| 1208 |
+
name: str, the flag name.
|
| 1209 |
+
default: Union[Iterable[str], str, None], the default value of the flag; see
|
| 1210 |
+
:func:`DEFINE_multi`.
|
| 1211 |
+
help: str, the help message.
|
| 1212 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1213 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1214 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1215 |
+
argument.
|
| 1216 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1217 |
+
``Flag.__init__``.
|
| 1218 |
+
|
| 1219 |
+
Returns:
|
| 1220 |
+
a handle to defined flag.
|
| 1221 |
+
"""
|
| 1222 |
+
parser = _argument_parser.ArgumentParser()
|
| 1223 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 1224 |
+
return DEFINE_multi(
|
| 1225 |
+
parser,
|
| 1226 |
+
serializer,
|
| 1227 |
+
name,
|
| 1228 |
+
default,
|
| 1229 |
+
help, # pylint: disable=redefined-builtin
|
| 1230 |
+
flag_values,
|
| 1231 |
+
required=True if required else False,
|
| 1232 |
+
**args,
|
| 1233 |
+
)
|
| 1234 |
+
|
| 1235 |
+
|
| 1236 |
+
@overload
|
| 1237 |
+
def DEFINE_multi_integer( # pylint: disable=invalid-name
|
| 1238 |
+
name: str,
|
| 1239 |
+
default: Union[None, Iterable[int], int, str],
|
| 1240 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1241 |
+
lower_bound: Optional[int] = ...,
|
| 1242 |
+
upper_bound: Optional[int] = ...,
|
| 1243 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1244 |
+
*,
|
| 1245 |
+
required: Literal[True],
|
| 1246 |
+
**args: Any
|
| 1247 |
+
) -> _flagvalues.FlagHolder[List[int]]:
|
| 1248 |
+
...
|
| 1249 |
+
|
| 1250 |
+
|
| 1251 |
+
@overload
|
| 1252 |
+
def DEFINE_multi_integer( # pylint: disable=invalid-name
|
| 1253 |
+
name: str,
|
| 1254 |
+
default: None,
|
| 1255 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1256 |
+
lower_bound: Optional[int] = ...,
|
| 1257 |
+
upper_bound: Optional[int] = ...,
|
| 1258 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1259 |
+
required: bool = ...,
|
| 1260 |
+
**args: Any
|
| 1261 |
+
) -> _flagvalues.FlagHolder[Optional[List[int]]]:
|
| 1262 |
+
...
|
| 1263 |
+
|
| 1264 |
+
|
| 1265 |
+
@overload
|
| 1266 |
+
def DEFINE_multi_integer( # pylint: disable=invalid-name
|
| 1267 |
+
name: str,
|
| 1268 |
+
default: Union[Iterable[int], int, str],
|
| 1269 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1270 |
+
lower_bound: Optional[int] = ...,
|
| 1271 |
+
upper_bound: Optional[int] = ...,
|
| 1272 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1273 |
+
required: bool = ...,
|
| 1274 |
+
**args: Any
|
| 1275 |
+
) -> _flagvalues.FlagHolder[List[int]]:
|
| 1276 |
+
...
|
| 1277 |
+
|
| 1278 |
+
|
| 1279 |
+
def DEFINE_multi_integer( # pylint: disable=invalid-name
|
| 1280 |
+
name,
|
| 1281 |
+
default,
|
| 1282 |
+
help, # pylint: disable=redefined-builtin
|
| 1283 |
+
lower_bound=None,
|
| 1284 |
+
upper_bound=None,
|
| 1285 |
+
flag_values=_flagvalues.FLAGS,
|
| 1286 |
+
required=False,
|
| 1287 |
+
**args
|
| 1288 |
+
):
|
| 1289 |
+
"""Registers a flag whose value can be a list of arbitrary integers.
|
| 1290 |
+
|
| 1291 |
+
Use the flag on the command line multiple times to place multiple
|
| 1292 |
+
integer values into the list. The 'default' may be a single integer
|
| 1293 |
+
(which will be converted into a single-element list) or a list of
|
| 1294 |
+
integers.
|
| 1295 |
+
|
| 1296 |
+
Args:
|
| 1297 |
+
name: str, the flag name.
|
| 1298 |
+
default: Union[Iterable[int], str, None], the default value of the flag; see
|
| 1299 |
+
`DEFINE_multi`.
|
| 1300 |
+
help: str, the help message.
|
| 1301 |
+
lower_bound: int, min values of the flag.
|
| 1302 |
+
upper_bound: int, max values of the flag.
|
| 1303 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1304 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1305 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1306 |
+
argument.
|
| 1307 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1308 |
+
``Flag.__init__``.
|
| 1309 |
+
|
| 1310 |
+
Returns:
|
| 1311 |
+
a handle to defined flag.
|
| 1312 |
+
"""
|
| 1313 |
+
parser = _argument_parser.IntegerParser(lower_bound, upper_bound)
|
| 1314 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 1315 |
+
return DEFINE_multi(
|
| 1316 |
+
parser,
|
| 1317 |
+
serializer,
|
| 1318 |
+
name,
|
| 1319 |
+
default,
|
| 1320 |
+
help, # pylint: disable=redefined-builtin
|
| 1321 |
+
flag_values,
|
| 1322 |
+
required=True if required else False,
|
| 1323 |
+
**args,
|
| 1324 |
+
)
|
| 1325 |
+
|
| 1326 |
+
|
| 1327 |
+
@overload
|
| 1328 |
+
def DEFINE_multi_float( # pylint: disable=invalid-name
|
| 1329 |
+
name: str,
|
| 1330 |
+
default: Union[None, Iterable[float], float, str],
|
| 1331 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1332 |
+
lower_bound: Optional[float] = ...,
|
| 1333 |
+
upper_bound: Optional[float] = ...,
|
| 1334 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1335 |
+
*,
|
| 1336 |
+
required: Literal[True],
|
| 1337 |
+
**args: Any
|
| 1338 |
+
) -> _flagvalues.FlagHolder[List[float]]:
|
| 1339 |
+
...
|
| 1340 |
+
|
| 1341 |
+
|
| 1342 |
+
@overload
|
| 1343 |
+
def DEFINE_multi_float( # pylint: disable=invalid-name
|
| 1344 |
+
name: str,
|
| 1345 |
+
default: None,
|
| 1346 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1347 |
+
lower_bound: Optional[float] = ...,
|
| 1348 |
+
upper_bound: Optional[float] = ...,
|
| 1349 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1350 |
+
required: bool = ...,
|
| 1351 |
+
**args: Any
|
| 1352 |
+
) -> _flagvalues.FlagHolder[Optional[List[float]]]:
|
| 1353 |
+
...
|
| 1354 |
+
|
| 1355 |
+
|
| 1356 |
+
@overload
|
| 1357 |
+
def DEFINE_multi_float( # pylint: disable=invalid-name
|
| 1358 |
+
name: str,
|
| 1359 |
+
default: Union[Iterable[float], float, str],
|
| 1360 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1361 |
+
lower_bound: Optional[float] = ...,
|
| 1362 |
+
upper_bound: Optional[float] = ...,
|
| 1363 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1364 |
+
required: bool = ...,
|
| 1365 |
+
**args: Any
|
| 1366 |
+
) -> _flagvalues.FlagHolder[List[float]]:
|
| 1367 |
+
...
|
| 1368 |
+
|
| 1369 |
+
|
| 1370 |
+
def DEFINE_multi_float( # pylint: disable=invalid-name
|
| 1371 |
+
name,
|
| 1372 |
+
default,
|
| 1373 |
+
help, # pylint: disable=redefined-builtin
|
| 1374 |
+
lower_bound=None,
|
| 1375 |
+
upper_bound=None,
|
| 1376 |
+
flag_values=_flagvalues.FLAGS,
|
| 1377 |
+
required=False,
|
| 1378 |
+
**args
|
| 1379 |
+
):
|
| 1380 |
+
"""Registers a flag whose value can be a list of arbitrary floats.
|
| 1381 |
+
|
| 1382 |
+
Use the flag on the command line multiple times to place multiple
|
| 1383 |
+
float values into the list. The 'default' may be a single float
|
| 1384 |
+
(which will be converted into a single-element list) or a list of
|
| 1385 |
+
floats.
|
| 1386 |
+
|
| 1387 |
+
Args:
|
| 1388 |
+
name: str, the flag name.
|
| 1389 |
+
default: Union[Iterable[float], str, None], the default value of the flag;
|
| 1390 |
+
see `DEFINE_multi`.
|
| 1391 |
+
help: str, the help message.
|
| 1392 |
+
lower_bound: float, min values of the flag.
|
| 1393 |
+
upper_bound: float, max values of the flag.
|
| 1394 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1395 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1396 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1397 |
+
argument.
|
| 1398 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1399 |
+
``Flag.__init__``.
|
| 1400 |
+
|
| 1401 |
+
Returns:
|
| 1402 |
+
a handle to defined flag.
|
| 1403 |
+
"""
|
| 1404 |
+
parser = _argument_parser.FloatParser(lower_bound, upper_bound)
|
| 1405 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 1406 |
+
return DEFINE_multi(
|
| 1407 |
+
parser,
|
| 1408 |
+
serializer,
|
| 1409 |
+
name,
|
| 1410 |
+
default,
|
| 1411 |
+
help,
|
| 1412 |
+
flag_values,
|
| 1413 |
+
required=True if required else False,
|
| 1414 |
+
**args,
|
| 1415 |
+
)
|
| 1416 |
+
|
| 1417 |
+
|
| 1418 |
+
@overload
|
| 1419 |
+
def DEFINE_multi_enum( # pylint: disable=invalid-name
|
| 1420 |
+
name: str,
|
| 1421 |
+
default: Union[None, Iterable[str], str],
|
| 1422 |
+
enum_values: Iterable[str],
|
| 1423 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1424 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1425 |
+
*,
|
| 1426 |
+
required: Literal[True],
|
| 1427 |
+
**args: Any
|
| 1428 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 1429 |
+
...
|
| 1430 |
+
|
| 1431 |
+
|
| 1432 |
+
@overload
|
| 1433 |
+
def DEFINE_multi_enum( # pylint: disable=invalid-name
|
| 1434 |
+
name: str,
|
| 1435 |
+
default: None,
|
| 1436 |
+
enum_values: Iterable[str],
|
| 1437 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1438 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1439 |
+
required: bool = ...,
|
| 1440 |
+
**args: Any
|
| 1441 |
+
) -> _flagvalues.FlagHolder[Optional[List[str]]]:
|
| 1442 |
+
...
|
| 1443 |
+
|
| 1444 |
+
|
| 1445 |
+
@overload
|
| 1446 |
+
def DEFINE_multi_enum( # pylint: disable=invalid-name
|
| 1447 |
+
name: str,
|
| 1448 |
+
default: Union[Iterable[str], str],
|
| 1449 |
+
enum_values: Iterable[str],
|
| 1450 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1451 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1452 |
+
required: bool = ...,
|
| 1453 |
+
**args: Any
|
| 1454 |
+
) -> _flagvalues.FlagHolder[List[str]]:
|
| 1455 |
+
...
|
| 1456 |
+
|
| 1457 |
+
|
| 1458 |
+
def DEFINE_multi_enum( # pylint: disable=invalid-name
|
| 1459 |
+
name,
|
| 1460 |
+
default,
|
| 1461 |
+
enum_values,
|
| 1462 |
+
help, # pylint: disable=redefined-builtin
|
| 1463 |
+
flag_values=_flagvalues.FLAGS,
|
| 1464 |
+
case_sensitive=True,
|
| 1465 |
+
required=False,
|
| 1466 |
+
**args
|
| 1467 |
+
):
|
| 1468 |
+
"""Registers a flag whose value can be a list strings from enum_values.
|
| 1469 |
+
|
| 1470 |
+
Use the flag on the command line multiple times to place multiple
|
| 1471 |
+
enum values into the list. The 'default' may be a single string
|
| 1472 |
+
(which will be converted into a single-element list) or a list of
|
| 1473 |
+
strings.
|
| 1474 |
+
|
| 1475 |
+
Args:
|
| 1476 |
+
name: str, the flag name.
|
| 1477 |
+
default: Union[Iterable[str], str, None], the default value of the flag; see
|
| 1478 |
+
`DEFINE_multi`.
|
| 1479 |
+
enum_values: [str], a non-empty list of strings with the possible values for
|
| 1480 |
+
the flag.
|
| 1481 |
+
help: str, the help message.
|
| 1482 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1483 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1484 |
+
case_sensitive: Whether or not the enum is to be case-sensitive.
|
| 1485 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1486 |
+
argument.
|
| 1487 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1488 |
+
``Flag.__init__``.
|
| 1489 |
+
|
| 1490 |
+
Returns:
|
| 1491 |
+
a handle to defined flag.
|
| 1492 |
+
"""
|
| 1493 |
+
parser = _argument_parser.EnumParser(enum_values, case_sensitive)
|
| 1494 |
+
serializer = _argument_parser.ArgumentSerializer()
|
| 1495 |
+
return DEFINE_multi(
|
| 1496 |
+
parser,
|
| 1497 |
+
serializer,
|
| 1498 |
+
name,
|
| 1499 |
+
default,
|
| 1500 |
+
'<%s>: %s' % ('|'.join(enum_values), help),
|
| 1501 |
+
flag_values,
|
| 1502 |
+
required=True if required else False,
|
| 1503 |
+
**args,
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
|
| 1507 |
+
@overload
|
| 1508 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1509 |
+
name: str,
|
| 1510 |
+
# This is separate from `Union[None, _ET, Iterable[str], str]` to avoid a
|
| 1511 |
+
# Pytype issue inferring the return value to
|
| 1512 |
+
# FlagHolder[List[Union[_ET, enum.Enum]]] when an iterable of concrete enum
|
| 1513 |
+
# subclasses are used.
|
| 1514 |
+
default: Iterable[_ET],
|
| 1515 |
+
enum_class: Type[_ET],
|
| 1516 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1517 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1518 |
+
module_name: Optional[str] = ...,
|
| 1519 |
+
*,
|
| 1520 |
+
required: Literal[True],
|
| 1521 |
+
**args: Any
|
| 1522 |
+
) -> _flagvalues.FlagHolder[List[_ET]]:
|
| 1523 |
+
...
|
| 1524 |
+
|
| 1525 |
+
|
| 1526 |
+
@overload
|
| 1527 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1528 |
+
name: str,
|
| 1529 |
+
default: Union[None, _ET, Iterable[str], str],
|
| 1530 |
+
enum_class: Type[_ET],
|
| 1531 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1532 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1533 |
+
module_name: Optional[str] = ...,
|
| 1534 |
+
*,
|
| 1535 |
+
required: Literal[True],
|
| 1536 |
+
**args: Any
|
| 1537 |
+
) -> _flagvalues.FlagHolder[List[_ET]]:
|
| 1538 |
+
...
|
| 1539 |
+
|
| 1540 |
+
|
| 1541 |
+
@overload
|
| 1542 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1543 |
+
name: str,
|
| 1544 |
+
default: None,
|
| 1545 |
+
enum_class: Type[_ET],
|
| 1546 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1547 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1548 |
+
module_name: Optional[str] = ...,
|
| 1549 |
+
required: bool = ...,
|
| 1550 |
+
**args: Any
|
| 1551 |
+
) -> _flagvalues.FlagHolder[Optional[List[_ET]]]:
|
| 1552 |
+
...
|
| 1553 |
+
|
| 1554 |
+
|
| 1555 |
+
@overload
|
| 1556 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1557 |
+
name: str,
|
| 1558 |
+
# This is separate from `Union[None, _ET, Iterable[str], str]` to avoid a
|
| 1559 |
+
# Pytype issue inferring the return value to
|
| 1560 |
+
# FlagHolder[List[Union[_ET, enum.Enum]]] when an iterable of concrete enum
|
| 1561 |
+
# subclasses are used.
|
| 1562 |
+
default: Iterable[_ET],
|
| 1563 |
+
enum_class: Type[_ET],
|
| 1564 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1565 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1566 |
+
module_name: Optional[str] = ...,
|
| 1567 |
+
required: bool = ...,
|
| 1568 |
+
**args: Any
|
| 1569 |
+
) -> _flagvalues.FlagHolder[List[_ET]]:
|
| 1570 |
+
...
|
| 1571 |
+
|
| 1572 |
+
|
| 1573 |
+
@overload
|
| 1574 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1575 |
+
name: str,
|
| 1576 |
+
default: Union[_ET, Iterable[str], str],
|
| 1577 |
+
enum_class: Type[_ET],
|
| 1578 |
+
help: str, # pylint: disable=redefined-builtin
|
| 1579 |
+
flag_values: _flagvalues.FlagValues = ...,
|
| 1580 |
+
module_name: Optional[str] = ...,
|
| 1581 |
+
required: bool = ...,
|
| 1582 |
+
**args: Any
|
| 1583 |
+
) -> _flagvalues.FlagHolder[List[_ET]]:
|
| 1584 |
+
...
|
| 1585 |
+
|
| 1586 |
+
|
| 1587 |
+
def DEFINE_multi_enum_class( # pylint: disable=invalid-name
|
| 1588 |
+
name,
|
| 1589 |
+
default,
|
| 1590 |
+
enum_class,
|
| 1591 |
+
help, # pylint: disable=redefined-builtin
|
| 1592 |
+
flag_values=_flagvalues.FLAGS,
|
| 1593 |
+
module_name=None,
|
| 1594 |
+
case_sensitive=False,
|
| 1595 |
+
required=False,
|
| 1596 |
+
**args
|
| 1597 |
+
):
|
| 1598 |
+
"""Registers a flag whose value can be a list of enum members.
|
| 1599 |
+
|
| 1600 |
+
Use the flag on the command line multiple times to place multiple
|
| 1601 |
+
enum values into the list.
|
| 1602 |
+
|
| 1603 |
+
Args:
|
| 1604 |
+
name: str, the flag name.
|
| 1605 |
+
default: Union[Iterable[Enum], Iterable[str], Enum, str, None], the default
|
| 1606 |
+
value of the flag; see `DEFINE_multi`; only differences are documented
|
| 1607 |
+
here. If the value is a single Enum, it is treated as a single-item list
|
| 1608 |
+
of that Enum value. If it is an iterable, text values within the iterable
|
| 1609 |
+
will be converted to the equivalent Enum objects.
|
| 1610 |
+
enum_class: class, the Enum class with all the possible values for the flag.
|
| 1611 |
+
help: str, the help message.
|
| 1612 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1613 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1614 |
+
module_name: A string, the name of the Python module declaring this flag. If
|
| 1615 |
+
not provided, it will be computed using the stack trace of this call.
|
| 1616 |
+
case_sensitive: bool, whether to map strings to members of the enum_class
|
| 1617 |
+
without considering case.
|
| 1618 |
+
required: bool, is this a required flag. This must be used as a keyword
|
| 1619 |
+
argument.
|
| 1620 |
+
**args: Dictionary with extra keyword args that are passed to the
|
| 1621 |
+
``Flag.__init__``.
|
| 1622 |
+
|
| 1623 |
+
Returns:
|
| 1624 |
+
a handle to defined flag.
|
| 1625 |
+
"""
|
| 1626 |
+
# NOTE: pytype fails if this is a direct return.
|
| 1627 |
+
result = DEFINE_flag(
|
| 1628 |
+
_flag.MultiEnumClassFlag(
|
| 1629 |
+
name,
|
| 1630 |
+
default,
|
| 1631 |
+
help,
|
| 1632 |
+
enum_class,
|
| 1633 |
+
case_sensitive=case_sensitive,
|
| 1634 |
+
**args,
|
| 1635 |
+
),
|
| 1636 |
+
flag_values,
|
| 1637 |
+
module_name,
|
| 1638 |
+
required=True if required else False,
|
| 1639 |
+
)
|
| 1640 |
+
return result
|
| 1641 |
+
|
| 1642 |
+
|
| 1643 |
+
def DEFINE_alias( # pylint: disable=invalid-name
|
| 1644 |
+
name: str,
|
| 1645 |
+
original_name: str,
|
| 1646 |
+
flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,
|
| 1647 |
+
module_name: Optional[str] = None,
|
| 1648 |
+
) -> _flagvalues.FlagHolder[Any]:
|
| 1649 |
+
"""Defines an alias flag for an existing one.
|
| 1650 |
+
|
| 1651 |
+
Args:
|
| 1652 |
+
name: str, the flag name.
|
| 1653 |
+
original_name: str, the original flag name.
|
| 1654 |
+
flag_values: :class:`FlagValues`, the FlagValues instance with which the
|
| 1655 |
+
flag will be registered. This should almost never need to be overridden.
|
| 1656 |
+
module_name: A string, the name of the module that defines this flag.
|
| 1657 |
+
|
| 1658 |
+
Returns:
|
| 1659 |
+
a handle to defined flag.
|
| 1660 |
+
|
| 1661 |
+
Raises:
|
| 1662 |
+
flags.FlagError:
|
| 1663 |
+
UnrecognizedFlagError: if the referenced flag doesn't exist.
|
| 1664 |
+
DuplicateFlagError: if the alias name has been used by some existing flag.
|
| 1665 |
+
"""
|
| 1666 |
+
if original_name not in flag_values:
|
| 1667 |
+
raise _exceptions.UnrecognizedFlagError(original_name)
|
| 1668 |
+
flag = flag_values[original_name]
|
| 1669 |
+
|
| 1670 |
+
class _FlagAlias(_flag.Flag):
|
| 1671 |
+
"""Overrides Flag class so alias value is copy of original flag value."""
|
| 1672 |
+
|
| 1673 |
+
def parse(self, argument):
|
| 1674 |
+
flag.parse(argument)
|
| 1675 |
+
self.present += 1
|
| 1676 |
+
|
| 1677 |
+
def _parse_from_default(self, value):
|
| 1678 |
+
# The value was already parsed by the aliased flag, so there is no
|
| 1679 |
+
# need to call the parser on it a second time.
|
| 1680 |
+
# Additionally, because of how MultiFlag parses and merges values,
|
| 1681 |
+
# it isn't possible to delegate to the aliased flag and still get
|
| 1682 |
+
# the correct values.
|
| 1683 |
+
return value
|
| 1684 |
+
|
| 1685 |
+
@property
|
| 1686 |
+
def value(self):
|
| 1687 |
+
return flag.value
|
| 1688 |
+
|
| 1689 |
+
@value.setter
|
| 1690 |
+
def value(self, value):
|
| 1691 |
+
flag.value = value
|
| 1692 |
+
|
| 1693 |
+
help_msg = 'Alias for --%s.' % flag.name
|
| 1694 |
+
# If alias_name has been used, flags.DuplicatedFlag will be raised.
|
| 1695 |
+
return DEFINE_flag(
|
| 1696 |
+
_FlagAlias(
|
| 1697 |
+
flag.parser,
|
| 1698 |
+
flag.serializer,
|
| 1699 |
+
name,
|
| 1700 |
+
flag.default,
|
| 1701 |
+
help_msg,
|
| 1702 |
+
boolean=flag.boolean), flag_values, module_name)
|
lib/python3.10/site-packages/absl/flags/_exceptions.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Exception classes in ABSL flags library.
|
| 16 |
+
|
| 17 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 18 |
+
aliases defined at the package level instead.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import sys
|
| 22 |
+
|
| 23 |
+
from absl.flags import _helpers
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
_helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class Error(Exception):
|
| 30 |
+
"""The base class for all flags errors."""
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class CantOpenFlagFileError(Error):
|
| 34 |
+
"""Raised when flagfile fails to open.
|
| 35 |
+
|
| 36 |
+
E.g. the file doesn't exist, or has wrong permissions.
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class DuplicateFlagError(Error):
|
| 41 |
+
"""Raised if there is a flag naming conflict."""
|
| 42 |
+
|
| 43 |
+
@classmethod
|
| 44 |
+
def from_flag(cls, flagname, flag_values, other_flag_values=None):
|
| 45 |
+
"""Creates a DuplicateFlagError by providing flag name and values.
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
flagname: str, the name of the flag being redefined.
|
| 49 |
+
flag_values: :class:`FlagValues`, the FlagValues instance containing the
|
| 50 |
+
first definition of flagname.
|
| 51 |
+
other_flag_values: :class:`FlagValues`, if it is not None, it should be
|
| 52 |
+
the FlagValues object where the second definition of flagname occurs.
|
| 53 |
+
If it is None, we assume that we're being called when attempting to
|
| 54 |
+
create the flag a second time, and we use the module calling this one
|
| 55 |
+
as the source of the second definition.
|
| 56 |
+
|
| 57 |
+
Returns:
|
| 58 |
+
An instance of DuplicateFlagError.
|
| 59 |
+
"""
|
| 60 |
+
first_module = flag_values.find_module_defining_flag(
|
| 61 |
+
flagname, default='<unknown>')
|
| 62 |
+
if other_flag_values is None:
|
| 63 |
+
second_module = _helpers.get_calling_module()
|
| 64 |
+
else:
|
| 65 |
+
second_module = other_flag_values.find_module_defining_flag(
|
| 66 |
+
flagname, default='<unknown>')
|
| 67 |
+
flag_summary = flag_values[flagname].help
|
| 68 |
+
msg = ("The flag '%s' is defined twice. First from %s, Second from %s. "
|
| 69 |
+
"Description from first occurrence: %s") % (
|
| 70 |
+
flagname, first_module, second_module, flag_summary)
|
| 71 |
+
return cls(msg)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class IllegalFlagValueError(Error):
|
| 75 |
+
"""Raised when the flag command line argument is illegal."""
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class UnrecognizedFlagError(Error):
|
| 79 |
+
"""Raised when a flag is unrecognized.
|
| 80 |
+
|
| 81 |
+
Attributes:
|
| 82 |
+
flagname: str, the name of the unrecognized flag.
|
| 83 |
+
flagvalue: The value of the flag, empty if the flag is not defined.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
def __init__(self, flagname, flagvalue='', suggestions=None):
|
| 87 |
+
self.flagname = flagname
|
| 88 |
+
self.flagvalue = flagvalue
|
| 89 |
+
if suggestions:
|
| 90 |
+
# Space before the question mark is intentional to not include it in the
|
| 91 |
+
# selection when copy-pasting the suggestion from (some) terminals.
|
| 92 |
+
tip = '. Did you mean: %s ?' % ', '.join(suggestions)
|
| 93 |
+
else:
|
| 94 |
+
tip = ''
|
| 95 |
+
super().__init__("Unknown command line flag '%s'%s" % (flagname, tip))
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class UnparsedFlagAccessError(Error):
|
| 99 |
+
"""Raised when accessing the flag value from unparsed :class:`FlagValues`."""
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class ValidationError(Error):
|
| 103 |
+
"""Raised when flag validator constraint is not satisfied."""
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class FlagNameConflictsWithMethodError(Error):
|
| 107 |
+
"""Raised when a flag name conflicts with :class:`FlagValues` methods."""
|
lib/python3.10/site-packages/absl/flags/_flag.py
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Contains Flag class - information about single command-line flag.
|
| 16 |
+
|
| 17 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 18 |
+
aliases defined at the package level instead.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
from collections import abc
|
| 22 |
+
import copy
|
| 23 |
+
import enum
|
| 24 |
+
import functools
|
| 25 |
+
from typing import Any, Dict, Generic, Iterable, List, Optional, Type, TypeVar, Union
|
| 26 |
+
from xml.dom import minidom
|
| 27 |
+
|
| 28 |
+
from absl.flags import _argument_parser
|
| 29 |
+
from absl.flags import _exceptions
|
| 30 |
+
from absl.flags import _helpers
|
| 31 |
+
|
| 32 |
+
_T = TypeVar('_T')
|
| 33 |
+
_ET = TypeVar('_ET', bound=enum.Enum)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@functools.total_ordering
|
| 37 |
+
class Flag(Generic[_T]):
|
| 38 |
+
"""Information about a command-line flag.
|
| 39 |
+
|
| 40 |
+
Attributes:
|
| 41 |
+
name: the name for this flag
|
| 42 |
+
default: the default value for this flag
|
| 43 |
+
default_unparsed: the unparsed default value for this flag.
|
| 44 |
+
default_as_str: default value as repr'd string, e.g., "'true'"
|
| 45 |
+
(or None)
|
| 46 |
+
value: the most recent parsed value of this flag set by :meth:`parse`
|
| 47 |
+
help: a help string or None if no help is available
|
| 48 |
+
short_name: the single letter alias for this flag (or None)
|
| 49 |
+
boolean: if 'true', this flag does not accept arguments
|
| 50 |
+
present: true if this flag was parsed from command line flags
|
| 51 |
+
parser: an :class:`~absl.flags.ArgumentParser` object
|
| 52 |
+
serializer: an ArgumentSerializer object
|
| 53 |
+
allow_override: the flag may be redefined without raising an error,
|
| 54 |
+
and newly defined flag overrides the old one.
|
| 55 |
+
allow_override_cpp: use the flag from C++ if available the flag
|
| 56 |
+
definition is replaced by the C++ flag after init
|
| 57 |
+
allow_hide_cpp: use the Python flag despite having a C++ flag with
|
| 58 |
+
the same name (ignore the C++ flag)
|
| 59 |
+
using_default_value: the flag value has not been set by user
|
| 60 |
+
allow_overwrite: the flag may be parsed more than once without
|
| 61 |
+
raising an error, the last set value will be used
|
| 62 |
+
allow_using_method_names: whether this flag can be defined even if
|
| 63 |
+
it has a name that conflicts with a FlagValues method.
|
| 64 |
+
validators: list of the flag validators.
|
| 65 |
+
|
| 66 |
+
The only public method of a ``Flag`` object is :meth:`parse`, but it is
|
| 67 |
+
typically only called by a :class:`~absl.flags.FlagValues` object. The
|
| 68 |
+
:meth:`parse` method is a thin wrapper around the
|
| 69 |
+
:meth:`ArgumentParser.parse()<absl.flags.ArgumentParser.parse>` method. The
|
| 70 |
+
parsed value is saved in ``.value``, and the ``.present`` attribute is
|
| 71 |
+
updated. If this flag was already present, an Error is raised.
|
| 72 |
+
|
| 73 |
+
:meth:`parse` is also called during ``__init__`` to parse the default value
|
| 74 |
+
and initialize the ``.value`` attribute. This enables other python modules to
|
| 75 |
+
safely use flags even if the ``__main__`` module neglects to parse the
|
| 76 |
+
command line arguments. The ``.present`` attribute is cleared after
|
| 77 |
+
``__init__`` parsing. If the default value is set to ``None``, then the
|
| 78 |
+
``__init__`` parsing step is skipped and the ``.value`` attribute is
|
| 79 |
+
initialized to None.
|
| 80 |
+
|
| 81 |
+
Note: The default value is also presented to the user in the help
|
| 82 |
+
string, so it is important that it be a legal value for this flag.
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
# NOTE: pytype doesn't find defaults without this.
|
| 86 |
+
default: Optional[_T]
|
| 87 |
+
default_as_str: Optional[str]
|
| 88 |
+
default_unparsed: Union[Optional[_T], str]
|
| 89 |
+
|
| 90 |
+
parser: _argument_parser.ArgumentParser[_T]
|
| 91 |
+
|
| 92 |
+
def __init__(
|
| 93 |
+
self,
|
| 94 |
+
parser: _argument_parser.ArgumentParser[_T],
|
| 95 |
+
serializer: Optional[_argument_parser.ArgumentSerializer[_T]],
|
| 96 |
+
name: str,
|
| 97 |
+
default: Union[Optional[_T], str],
|
| 98 |
+
help_string: Optional[str],
|
| 99 |
+
short_name: Optional[str] = None,
|
| 100 |
+
boolean: bool = False,
|
| 101 |
+
allow_override: bool = False,
|
| 102 |
+
allow_override_cpp: bool = False,
|
| 103 |
+
allow_hide_cpp: bool = False,
|
| 104 |
+
allow_overwrite: bool = True,
|
| 105 |
+
allow_using_method_names: bool = False,
|
| 106 |
+
) -> None:
|
| 107 |
+
self.name = name
|
| 108 |
+
|
| 109 |
+
if not help_string:
|
| 110 |
+
help_string = '(no help available)'
|
| 111 |
+
|
| 112 |
+
self.help = help_string
|
| 113 |
+
self.short_name = short_name
|
| 114 |
+
self.boolean = boolean
|
| 115 |
+
self.present = 0
|
| 116 |
+
self.parser = parser # type: ignore[annotation-type-mismatch]
|
| 117 |
+
self.serializer = serializer
|
| 118 |
+
self.allow_override = allow_override
|
| 119 |
+
self.allow_override_cpp = allow_override_cpp
|
| 120 |
+
self.allow_hide_cpp = allow_hide_cpp
|
| 121 |
+
self.allow_overwrite = allow_overwrite
|
| 122 |
+
self.allow_using_method_names = allow_using_method_names
|
| 123 |
+
|
| 124 |
+
self.using_default_value = True
|
| 125 |
+
self._value: Optional[_T] = None
|
| 126 |
+
self.validators: List[Any] = []
|
| 127 |
+
if self.allow_hide_cpp and self.allow_override_cpp:
|
| 128 |
+
raise _exceptions.Error(
|
| 129 |
+
"Can't have both allow_hide_cpp (means use Python flag) and "
|
| 130 |
+
'allow_override_cpp (means use C++ flag after InitGoogle)')
|
| 131 |
+
|
| 132 |
+
self._set_default(default)
|
| 133 |
+
|
| 134 |
+
@property
|
| 135 |
+
def value(self) -> Optional[_T]:
|
| 136 |
+
return self._value
|
| 137 |
+
|
| 138 |
+
@value.setter
|
| 139 |
+
def value(self, value: Optional[_T]):
|
| 140 |
+
self._value = value
|
| 141 |
+
|
| 142 |
+
def __hash__(self):
|
| 143 |
+
return hash(id(self))
|
| 144 |
+
|
| 145 |
+
def __eq__(self, other):
|
| 146 |
+
return self is other
|
| 147 |
+
|
| 148 |
+
def __lt__(self, other):
|
| 149 |
+
if isinstance(other, Flag):
|
| 150 |
+
return id(self) < id(other)
|
| 151 |
+
return NotImplemented
|
| 152 |
+
|
| 153 |
+
def __bool__(self):
|
| 154 |
+
raise TypeError('A Flag instance would always be True. '
|
| 155 |
+
'Did you mean to test the `.value` attribute?')
|
| 156 |
+
|
| 157 |
+
def __getstate__(self):
|
| 158 |
+
raise TypeError("can't pickle Flag objects")
|
| 159 |
+
|
| 160 |
+
def __copy__(self):
|
| 161 |
+
raise TypeError('%s does not support shallow copies. '
|
| 162 |
+
'Use copy.deepcopy instead.' % type(self).__name__)
|
| 163 |
+
|
| 164 |
+
def __deepcopy__(self, memo: Dict[int, Any]) -> 'Flag[_T]':
|
| 165 |
+
result = object.__new__(type(self))
|
| 166 |
+
result.__dict__ = copy.deepcopy(self.__dict__, memo)
|
| 167 |
+
return result
|
| 168 |
+
|
| 169 |
+
def _get_parsed_value_as_string(self, value: Optional[_T]) -> Optional[str]:
|
| 170 |
+
"""Returns parsed flag value as string."""
|
| 171 |
+
if value is None:
|
| 172 |
+
return None
|
| 173 |
+
if self.serializer:
|
| 174 |
+
return repr(self.serializer.serialize(value))
|
| 175 |
+
if self.boolean:
|
| 176 |
+
if value:
|
| 177 |
+
return repr('true')
|
| 178 |
+
else:
|
| 179 |
+
return repr('false')
|
| 180 |
+
return repr(str(value))
|
| 181 |
+
|
| 182 |
+
def parse(self, argument: Union[str, _T]) -> None:
|
| 183 |
+
"""Parses string and sets flag value.
|
| 184 |
+
|
| 185 |
+
Args:
|
| 186 |
+
argument: str or the correct flag value type, argument to be parsed.
|
| 187 |
+
"""
|
| 188 |
+
if self.present and not self.allow_overwrite:
|
| 189 |
+
raise _exceptions.IllegalFlagValueError(
|
| 190 |
+
'flag --%s=%s: already defined as %s' % (
|
| 191 |
+
self.name, argument, self.value))
|
| 192 |
+
self.value = self._parse(argument)
|
| 193 |
+
self.present += 1
|
| 194 |
+
|
| 195 |
+
def _parse(self, argument: Union[str, _T]) -> Optional[_T]:
|
| 196 |
+
"""Internal parse function.
|
| 197 |
+
|
| 198 |
+
It returns the parsed value, and does not modify class states.
|
| 199 |
+
|
| 200 |
+
Args:
|
| 201 |
+
argument: str or the correct flag value type, argument to be parsed.
|
| 202 |
+
|
| 203 |
+
Returns:
|
| 204 |
+
The parsed value.
|
| 205 |
+
"""
|
| 206 |
+
try:
|
| 207 |
+
return self.parser.parse(argument) # type: ignore[arg-type]
|
| 208 |
+
except (TypeError, ValueError, OverflowError) as e:
|
| 209 |
+
# Recast as IllegalFlagValueError.
|
| 210 |
+
raise _exceptions.IllegalFlagValueError(
|
| 211 |
+
'flag --%s=%s: %s' % (self.name, argument, e))
|
| 212 |
+
|
| 213 |
+
def unparse(self) -> None:
|
| 214 |
+
self.value = self.default
|
| 215 |
+
self.using_default_value = True
|
| 216 |
+
self.present = 0
|
| 217 |
+
|
| 218 |
+
def serialize(self) -> str:
|
| 219 |
+
"""Serializes the flag."""
|
| 220 |
+
return self._serialize(self.value)
|
| 221 |
+
|
| 222 |
+
def _serialize(self, value: Optional[_T]) -> str:
|
| 223 |
+
"""Internal serialize function."""
|
| 224 |
+
if value is None:
|
| 225 |
+
return ''
|
| 226 |
+
if self.boolean:
|
| 227 |
+
if value:
|
| 228 |
+
return '--%s' % self.name
|
| 229 |
+
else:
|
| 230 |
+
return '--no%s' % self.name
|
| 231 |
+
else:
|
| 232 |
+
if not self.serializer:
|
| 233 |
+
raise _exceptions.Error(
|
| 234 |
+
'Serializer not present for flag %s' % self.name)
|
| 235 |
+
return '--%s=%s' % (self.name, self.serializer.serialize(value))
|
| 236 |
+
|
| 237 |
+
def _set_default(self, value: Union[Optional[_T], str]) -> None:
|
| 238 |
+
"""Changes the default value (and current value too) for this Flag."""
|
| 239 |
+
self.default_unparsed = value
|
| 240 |
+
if value is None:
|
| 241 |
+
self.default = None
|
| 242 |
+
else:
|
| 243 |
+
self.default = self._parse_from_default(value)
|
| 244 |
+
self.default_as_str = self._get_parsed_value_as_string(self.default)
|
| 245 |
+
if self.using_default_value:
|
| 246 |
+
self.value = self.default
|
| 247 |
+
|
| 248 |
+
# This is split out so that aliases can skip regular parsing of the default
|
| 249 |
+
# value.
|
| 250 |
+
def _parse_from_default(self, value: Union[str, _T]) -> Optional[_T]:
|
| 251 |
+
return self._parse(value)
|
| 252 |
+
|
| 253 |
+
def flag_type(self) -> str:
|
| 254 |
+
"""Returns a str that describes the type of the flag.
|
| 255 |
+
|
| 256 |
+
NOTE: we use strings, and not the types.*Type constants because
|
| 257 |
+
our flags can have more exotic types, e.g., 'comma separated list
|
| 258 |
+
of strings', 'whitespace separated list of strings', etc.
|
| 259 |
+
"""
|
| 260 |
+
return self.parser.flag_type()
|
| 261 |
+
|
| 262 |
+
def _create_xml_dom_element(
|
| 263 |
+
self, doc: minidom.Document, module_name: str, is_key: bool = False
|
| 264 |
+
) -> minidom.Element:
|
| 265 |
+
"""Returns an XML element that contains this flag's information.
|
| 266 |
+
|
| 267 |
+
This is information that is relevant to all flags (e.g., name,
|
| 268 |
+
meaning, etc.). If you defined a flag that has some other pieces of
|
| 269 |
+
info, then please override _ExtraXMLInfo.
|
| 270 |
+
|
| 271 |
+
Please do NOT override this method.
|
| 272 |
+
|
| 273 |
+
Args:
|
| 274 |
+
doc: minidom.Document, the DOM document it should create nodes from.
|
| 275 |
+
module_name: str,, the name of the module that defines this flag.
|
| 276 |
+
is_key: boolean, True iff this flag is key for main module.
|
| 277 |
+
|
| 278 |
+
Returns:
|
| 279 |
+
A minidom.Element instance.
|
| 280 |
+
"""
|
| 281 |
+
element = doc.createElement('flag')
|
| 282 |
+
if is_key:
|
| 283 |
+
element.appendChild(_helpers.create_xml_dom_element(doc, 'key', 'yes'))
|
| 284 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 285 |
+
doc, 'file', module_name))
|
| 286 |
+
# Adds flag features that are relevant for all flags.
|
| 287 |
+
element.appendChild(_helpers.create_xml_dom_element(doc, 'name', self.name))
|
| 288 |
+
if self.short_name:
|
| 289 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 290 |
+
doc, 'short_name', self.short_name))
|
| 291 |
+
if self.help:
|
| 292 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 293 |
+
doc, 'meaning', self.help))
|
| 294 |
+
# The default flag value can either be represented as a string like on the
|
| 295 |
+
# command line, or as a Python object. We serialize this value in the
|
| 296 |
+
# latter case in order to remain consistent.
|
| 297 |
+
if self.serializer and not isinstance(self.default, str):
|
| 298 |
+
if self.default is not None:
|
| 299 |
+
default_serialized = self.serializer.serialize(self.default)
|
| 300 |
+
else:
|
| 301 |
+
default_serialized = ''
|
| 302 |
+
else:
|
| 303 |
+
default_serialized = self.default # type: ignore[assignment]
|
| 304 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 305 |
+
doc, 'default', default_serialized))
|
| 306 |
+
value_serialized = self._serialize_value_for_xml(self.value)
|
| 307 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 308 |
+
doc, 'current', value_serialized))
|
| 309 |
+
element.appendChild(_helpers.create_xml_dom_element(
|
| 310 |
+
doc, 'type', self.flag_type()))
|
| 311 |
+
# Adds extra flag features this flag may have.
|
| 312 |
+
for e in self._extra_xml_dom_elements(doc):
|
| 313 |
+
element.appendChild(e)
|
| 314 |
+
return element
|
| 315 |
+
|
| 316 |
+
def _serialize_value_for_xml(self, value: Optional[_T]) -> Any:
|
| 317 |
+
"""Returns the serialized value, for use in an XML help text."""
|
| 318 |
+
return value
|
| 319 |
+
|
| 320 |
+
def _extra_xml_dom_elements(
|
| 321 |
+
self, doc: minidom.Document
|
| 322 |
+
) -> List[minidom.Element]:
|
| 323 |
+
"""Returns extra info about this flag in XML.
|
| 324 |
+
|
| 325 |
+
"Extra" means "not already included by _create_xml_dom_element above."
|
| 326 |
+
|
| 327 |
+
Args:
|
| 328 |
+
doc: minidom.Document, the DOM document it should create nodes from.
|
| 329 |
+
|
| 330 |
+
Returns:
|
| 331 |
+
A list of minidom.Element.
|
| 332 |
+
"""
|
| 333 |
+
# Usually, the parser knows the extra details about the flag, so
|
| 334 |
+
# we just forward the call to it.
|
| 335 |
+
return self.parser._custom_xml_dom_elements(doc) # pylint: disable=protected-access
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
class BooleanFlag(Flag[bool]):
|
| 339 |
+
"""Basic boolean flag.
|
| 340 |
+
|
| 341 |
+
Boolean flags do not take any arguments, and their value is either
|
| 342 |
+
``True`` (1) or ``False`` (0). The false value is specified on the command
|
| 343 |
+
line by prepending the word ``'no'`` to either the long or the short flag
|
| 344 |
+
name.
|
| 345 |
+
|
| 346 |
+
For example, if a Boolean flag was created whose long name was
|
| 347 |
+
``'update'`` and whose short name was ``'x'``, then this flag could be
|
| 348 |
+
explicitly unset through either ``--noupdate`` or ``--nox``.
|
| 349 |
+
"""
|
| 350 |
+
|
| 351 |
+
def __init__(
|
| 352 |
+
self,
|
| 353 |
+
name: str,
|
| 354 |
+
default: Union[Optional[bool], str],
|
| 355 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 356 |
+
short_name: Optional[str] = None,
|
| 357 |
+
**args
|
| 358 |
+
) -> None:
|
| 359 |
+
p = _argument_parser.BooleanParser()
|
| 360 |
+
super().__init__(p, None, name, default, help, short_name, True, **args)
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
class EnumFlag(Flag[str]):
|
| 364 |
+
"""Basic enum flag; its value can be any string from list of enum_values."""
|
| 365 |
+
|
| 366 |
+
parser: _argument_parser.EnumParser
|
| 367 |
+
|
| 368 |
+
def __init__(
|
| 369 |
+
self,
|
| 370 |
+
name: str,
|
| 371 |
+
default: Optional[str],
|
| 372 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 373 |
+
enum_values: Iterable[str],
|
| 374 |
+
short_name: Optional[str] = None,
|
| 375 |
+
case_sensitive: bool = True,
|
| 376 |
+
**args
|
| 377 |
+
):
|
| 378 |
+
p = _argument_parser.EnumParser(enum_values, case_sensitive)
|
| 379 |
+
g: _argument_parser.ArgumentSerializer[str]
|
| 380 |
+
g = _argument_parser.ArgumentSerializer()
|
| 381 |
+
super().__init__(p, g, name, default, help, short_name, **args)
|
| 382 |
+
self.parser = p
|
| 383 |
+
self.help = '<%s>: %s' % ('|'.join(p.enum_values), self.help)
|
| 384 |
+
|
| 385 |
+
def _extra_xml_dom_elements(
|
| 386 |
+
self, doc: minidom.Document
|
| 387 |
+
) -> List[minidom.Element]:
|
| 388 |
+
elements = []
|
| 389 |
+
for enum_value in self.parser.enum_values:
|
| 390 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 391 |
+
doc, 'enum_value', enum_value))
|
| 392 |
+
return elements
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
class EnumClassFlag(Flag[_ET]):
|
| 396 |
+
"""Basic enum flag; its value is an enum class's member."""
|
| 397 |
+
|
| 398 |
+
parser: _argument_parser.EnumClassParser
|
| 399 |
+
|
| 400 |
+
def __init__(
|
| 401 |
+
self,
|
| 402 |
+
name: str,
|
| 403 |
+
default: Union[Optional[_ET], str],
|
| 404 |
+
help: Optional[str], # pylint: disable=redefined-builtin
|
| 405 |
+
enum_class: Type[_ET],
|
| 406 |
+
short_name: Optional[str] = None,
|
| 407 |
+
case_sensitive: bool = False,
|
| 408 |
+
**args
|
| 409 |
+
):
|
| 410 |
+
p = _argument_parser.EnumClassParser(
|
| 411 |
+
enum_class, case_sensitive=case_sensitive
|
| 412 |
+
)
|
| 413 |
+
g: _argument_parser.EnumClassSerializer[_ET]
|
| 414 |
+
g = _argument_parser.EnumClassSerializer(lowercase=not case_sensitive)
|
| 415 |
+
super().__init__(p, g, name, default, help, short_name, **args)
|
| 416 |
+
self.parser = p
|
| 417 |
+
self.help = '<%s>: %s' % ('|'.join(p.member_names), self.help)
|
| 418 |
+
|
| 419 |
+
def _extra_xml_dom_elements(
|
| 420 |
+
self, doc: minidom.Document
|
| 421 |
+
) -> List[minidom.Element]:
|
| 422 |
+
elements = []
|
| 423 |
+
for enum_value in self.parser.enum_class.__members__.keys():
|
| 424 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 425 |
+
doc, 'enum_value', enum_value))
|
| 426 |
+
return elements
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
class MultiFlag(Generic[_T], Flag[List[_T]]):
|
| 430 |
+
"""A flag that can appear multiple time on the command-line.
|
| 431 |
+
|
| 432 |
+
The value of such a flag is a list that contains the individual values
|
| 433 |
+
from all the appearances of that flag on the command-line.
|
| 434 |
+
|
| 435 |
+
See the __doc__ for Flag for most behavior of this class. Only
|
| 436 |
+
differences in behavior are described here:
|
| 437 |
+
|
| 438 |
+
* The default value may be either a single value or an iterable of values.
|
| 439 |
+
A single value is transformed into a single-item list of that value.
|
| 440 |
+
|
| 441 |
+
* The value of the flag is always a list, even if the option was
|
| 442 |
+
only supplied once, and even if the default value is a single
|
| 443 |
+
value
|
| 444 |
+
"""
|
| 445 |
+
|
| 446 |
+
def __init__(self, *args, **kwargs):
|
| 447 |
+
super().__init__(*args, **kwargs)
|
| 448 |
+
self.help += ';\n repeat this option to specify a list of values'
|
| 449 |
+
|
| 450 |
+
def parse(self, arguments: Union[str, _T, Iterable[_T]]): # pylint: disable=arguments-renamed
|
| 451 |
+
"""Parses one or more arguments with the installed parser.
|
| 452 |
+
|
| 453 |
+
Args:
|
| 454 |
+
arguments: a single argument or a list of arguments (typically a
|
| 455 |
+
list of default values); a single argument is converted
|
| 456 |
+
internally into a list containing one item.
|
| 457 |
+
"""
|
| 458 |
+
new_values = self._parse(arguments)
|
| 459 |
+
if self.present:
|
| 460 |
+
assert self.value is not None
|
| 461 |
+
self.value.extend(new_values)
|
| 462 |
+
else:
|
| 463 |
+
self.value = new_values
|
| 464 |
+
self.present += len(new_values)
|
| 465 |
+
|
| 466 |
+
def _parse(self, arguments: Union[str, _T, Iterable[_T]]) -> List[_T]: # pylint: disable=arguments-renamed
|
| 467 |
+
arguments_list: List[Union[str, _T]]
|
| 468 |
+
|
| 469 |
+
if isinstance(arguments, str):
|
| 470 |
+
arguments_list = [arguments]
|
| 471 |
+
|
| 472 |
+
elif isinstance(arguments, abc.Iterable):
|
| 473 |
+
arguments_list = list(arguments)
|
| 474 |
+
|
| 475 |
+
else:
|
| 476 |
+
# Default value may be a list of values. Most other arguments
|
| 477 |
+
# will not be, so convert them into a single-item list to make
|
| 478 |
+
# processing simpler below.
|
| 479 |
+
arguments_list = [arguments]
|
| 480 |
+
|
| 481 |
+
return [super(MultiFlag, self)._parse(item) for item in arguments_list] # type: ignore
|
| 482 |
+
|
| 483 |
+
def _serialize(self, value: Optional[List[_T]]) -> str:
|
| 484 |
+
"""See base class."""
|
| 485 |
+
if not self.serializer:
|
| 486 |
+
raise _exceptions.Error(
|
| 487 |
+
'Serializer not present for flag %s' % self.name)
|
| 488 |
+
if value is None:
|
| 489 |
+
return ''
|
| 490 |
+
|
| 491 |
+
serialized_items = [
|
| 492 |
+
super(MultiFlag, self)._serialize(value_item) # type: ignore[arg-type]
|
| 493 |
+
for value_item in value
|
| 494 |
+
]
|
| 495 |
+
|
| 496 |
+
return '\n'.join(serialized_items)
|
| 497 |
+
|
| 498 |
+
def flag_type(self):
|
| 499 |
+
"""See base class."""
|
| 500 |
+
return 'multi ' + self.parser.flag_type()
|
| 501 |
+
|
| 502 |
+
def _extra_xml_dom_elements(
|
| 503 |
+
self, doc: minidom.Document
|
| 504 |
+
) -> List[minidom.Element]:
|
| 505 |
+
elements = []
|
| 506 |
+
if hasattr(self.parser, 'enum_values'):
|
| 507 |
+
for enum_value in self.parser.enum_values: # pytype: disable=attribute-error
|
| 508 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 509 |
+
doc, 'enum_value', enum_value))
|
| 510 |
+
return elements
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
class MultiEnumClassFlag(MultiFlag[_ET]): # pytype: disable=not-indexable
|
| 514 |
+
"""A multi_enum_class flag.
|
| 515 |
+
|
| 516 |
+
See the __doc__ for MultiFlag for most behaviors of this class. In addition,
|
| 517 |
+
this class knows how to handle enum.Enum instances as values for this flag
|
| 518 |
+
type.
|
| 519 |
+
"""
|
| 520 |
+
|
| 521 |
+
parser: _argument_parser.EnumClassParser[_ET] # type: ignore[assignment]
|
| 522 |
+
|
| 523 |
+
def __init__(
|
| 524 |
+
self,
|
| 525 |
+
name: str,
|
| 526 |
+
default: Union[None, Iterable[_ET], _ET, Iterable[str], str],
|
| 527 |
+
help_string: str,
|
| 528 |
+
enum_class: Type[_ET],
|
| 529 |
+
case_sensitive: bool = False,
|
| 530 |
+
**args
|
| 531 |
+
):
|
| 532 |
+
p = _argument_parser.EnumClassParser(
|
| 533 |
+
enum_class, case_sensitive=case_sensitive)
|
| 534 |
+
g: _argument_parser.EnumClassListSerializer
|
| 535 |
+
g = _argument_parser.EnumClassListSerializer(
|
| 536 |
+
list_sep=',', lowercase=not case_sensitive)
|
| 537 |
+
super().__init__(p, g, name, default, help_string, **args)
|
| 538 |
+
# NOTE: parser should be typed EnumClassParser[_ET] but the constructor
|
| 539 |
+
# restricts the available interface to ArgumentParser[str].
|
| 540 |
+
self.parser = p
|
| 541 |
+
# NOTE: serializer should be non-Optional but this isn't inferred.
|
| 542 |
+
self.serializer = g
|
| 543 |
+
self.help = (
|
| 544 |
+
'<%s>: %s;\n repeat this option to specify a list of values' %
|
| 545 |
+
('|'.join(p.member_names), help_string or '(no help available)'))
|
| 546 |
+
|
| 547 |
+
def _extra_xml_dom_elements(
|
| 548 |
+
self, doc: minidom.Document
|
| 549 |
+
) -> List[minidom.Element]:
|
| 550 |
+
elements = []
|
| 551 |
+
for enum_value in self.parser.enum_class.__members__.keys(): # pytype: disable=attribute-error
|
| 552 |
+
elements.append(_helpers.create_xml_dom_element(
|
| 553 |
+
doc, 'enum_value', enum_value))
|
| 554 |
+
return elements
|
| 555 |
+
|
| 556 |
+
def _serialize_value_for_xml(self, value):
|
| 557 |
+
"""See base class."""
|
| 558 |
+
if value is not None:
|
| 559 |
+
if not self.serializer:
|
| 560 |
+
raise _exceptions.Error(
|
| 561 |
+
'Serializer not present for flag %s' % self.name
|
| 562 |
+
)
|
| 563 |
+
value_serialized = self.serializer.serialize(value)
|
| 564 |
+
else:
|
| 565 |
+
value_serialized = ''
|
| 566 |
+
return value_serialized
|
lib/python3.10/site-packages/absl/flags/_flagvalues.py
ADDED
|
@@ -0,0 +1,1486 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Defines the FlagValues class - registry of 'Flag' objects.
|
| 15 |
+
|
| 16 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 17 |
+
aliases defined at the package level instead.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import copy
|
| 21 |
+
import logging
|
| 22 |
+
import os
|
| 23 |
+
import sys
|
| 24 |
+
from typing import Any, Callable, Dict, Generic, Iterable, Iterator, List, Optional, Sequence, Set, TextIO, Tuple, TypeVar, Union
|
| 25 |
+
from xml.dom import minidom
|
| 26 |
+
|
| 27 |
+
from absl.flags import _exceptions
|
| 28 |
+
from absl.flags import _flag
|
| 29 |
+
from absl.flags import _helpers
|
| 30 |
+
from absl.flags import _validators_classes
|
| 31 |
+
from absl.flags._flag import Flag
|
| 32 |
+
|
| 33 |
+
# Add flagvalues module to disclaimed module ids.
|
| 34 |
+
_helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
|
| 35 |
+
|
| 36 |
+
_T = TypeVar('_T')
|
| 37 |
+
_T_co = TypeVar('_T_co', covariant=True) # pytype: disable=not-supported-yet
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class FlagValues:
|
| 41 |
+
"""Registry of :class:`~absl.flags.Flag` objects.
|
| 42 |
+
|
| 43 |
+
A :class:`FlagValues` can then scan command line arguments, passing flag
|
| 44 |
+
arguments through to the 'Flag' objects that it owns. It also
|
| 45 |
+
provides easy access to the flag values. Typically only one
|
| 46 |
+
:class:`FlagValues` object is needed by an application:
|
| 47 |
+
:const:`FLAGS`.
|
| 48 |
+
|
| 49 |
+
This class is heavily overloaded:
|
| 50 |
+
|
| 51 |
+
:class:`Flag` objects are registered via ``__setitem__``::
|
| 52 |
+
|
| 53 |
+
FLAGS['longname'] = x # register a new flag
|
| 54 |
+
|
| 55 |
+
The ``.value`` attribute of the registered :class:`~absl.flags.Flag` objects
|
| 56 |
+
can be accessed as attributes of this :class:`FlagValues` object, through
|
| 57 |
+
``__getattr__``. Both the long and short name of the original
|
| 58 |
+
:class:`~absl.flags.Flag` objects can be used to access its value::
|
| 59 |
+
|
| 60 |
+
FLAGS.longname # parsed flag value
|
| 61 |
+
FLAGS.x # parsed flag value (short name)
|
| 62 |
+
|
| 63 |
+
Command line arguments are scanned and passed to the registered
|
| 64 |
+
:class:`~absl.flags.Flag` objects through the ``__call__`` method. Unparsed
|
| 65 |
+
arguments, including ``argv[0]`` (e.g. the program name) are returned::
|
| 66 |
+
|
| 67 |
+
argv = FLAGS(sys.argv) # scan command line arguments
|
| 68 |
+
|
| 69 |
+
The original registered :class:`~absl.flags.Flag` objects can be retrieved
|
| 70 |
+
through the use of the dictionary-like operator, ``__getitem__``::
|
| 71 |
+
|
| 72 |
+
x = FLAGS['longname'] # access the registered Flag object
|
| 73 |
+
|
| 74 |
+
The ``str()`` operator of a :class:`absl.flags.FlagValues` object provides
|
| 75 |
+
help for all of the registered :class:`~absl.flags.Flag` objects.
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
_HAS_DYNAMIC_ATTRIBUTES = True
|
| 79 |
+
|
| 80 |
+
# A note on collections.abc.Mapping:
|
| 81 |
+
# FlagValues defines __getitem__, __iter__, and __len__. It makes perfect
|
| 82 |
+
# sense to let it be a collections.abc.Mapping class. However, we are not
|
| 83 |
+
# able to do so. The mixin methods, e.g. keys, values, are not uncommon flag
|
| 84 |
+
# names. Those flag values would not be accessible via the FLAGS.xxx form.
|
| 85 |
+
|
| 86 |
+
__dict__: Dict[str, Any]
|
| 87 |
+
|
| 88 |
+
def __init__(self):
|
| 89 |
+
# Since everything in this class is so heavily overloaded, the only
|
| 90 |
+
# way of defining and using fields is to access __dict__ directly.
|
| 91 |
+
|
| 92 |
+
# Dictionary: flag name (string) -> Flag object.
|
| 93 |
+
self.__dict__['__flags'] = {}
|
| 94 |
+
|
| 95 |
+
# Set: name of hidden flag (string).
|
| 96 |
+
# Holds flags that should not be directly accessible from Python.
|
| 97 |
+
self.__dict__['__hiddenflags'] = set()
|
| 98 |
+
|
| 99 |
+
# Dictionary: module name (string) -> list of Flag objects that are defined
|
| 100 |
+
# by that module.
|
| 101 |
+
self.__dict__['__flags_by_module'] = {}
|
| 102 |
+
# Dictionary: module id (int) -> list of Flag objects that are defined by
|
| 103 |
+
# that module.
|
| 104 |
+
self.__dict__['__flags_by_module_id'] = {}
|
| 105 |
+
# Dictionary: module name (string) -> list of Flag objects that are
|
| 106 |
+
# key for that module.
|
| 107 |
+
self.__dict__['__key_flags_by_module'] = {}
|
| 108 |
+
|
| 109 |
+
# Bool: True if flags were parsed.
|
| 110 |
+
self.__dict__['__flags_parsed'] = False
|
| 111 |
+
|
| 112 |
+
# Bool: True if unparse_flags() was called.
|
| 113 |
+
self.__dict__['__unparse_flags_called'] = False
|
| 114 |
+
|
| 115 |
+
# None or Method(name, value) to call from __setattr__ for an unknown flag.
|
| 116 |
+
self.__dict__['__set_unknown'] = None
|
| 117 |
+
|
| 118 |
+
# A set of banned flag names. This is to prevent users from accidentally
|
| 119 |
+
# defining a flag that has the same name as a method on this class.
|
| 120 |
+
# Users can still allow defining the flag by passing
|
| 121 |
+
# allow_using_method_names=True in DEFINE_xxx functions.
|
| 122 |
+
self.__dict__['__banned_flag_names'] = frozenset(dir(FlagValues))
|
| 123 |
+
|
| 124 |
+
# Bool: Whether to use GNU style scanning.
|
| 125 |
+
self.__dict__['__use_gnu_getopt'] = True
|
| 126 |
+
|
| 127 |
+
# Bool: Whether use_gnu_getopt has been explicitly set by the user.
|
| 128 |
+
self.__dict__['__use_gnu_getopt_explicitly_set'] = False
|
| 129 |
+
|
| 130 |
+
# Function: Takes a flag name as parameter, returns a tuple
|
| 131 |
+
# (is_retired, type_is_bool).
|
| 132 |
+
self.__dict__['__is_retired_flag_func'] = None
|
| 133 |
+
|
| 134 |
+
def set_gnu_getopt(self, gnu_getopt: bool = True) -> None:
|
| 135 |
+
"""Sets whether or not to use GNU style scanning.
|
| 136 |
+
|
| 137 |
+
GNU style allows mixing of flag and non-flag arguments. See
|
| 138 |
+
http://docs.python.org/library/getopt.html#getopt.gnu_getopt
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
gnu_getopt: bool, whether or not to use GNU style scanning.
|
| 142 |
+
"""
|
| 143 |
+
self.__dict__['__use_gnu_getopt'] = gnu_getopt
|
| 144 |
+
self.__dict__['__use_gnu_getopt_explicitly_set'] = True
|
| 145 |
+
|
| 146 |
+
def is_gnu_getopt(self) -> bool:
|
| 147 |
+
return self.__dict__['__use_gnu_getopt']
|
| 148 |
+
|
| 149 |
+
def _flags(self) -> Dict[str, Flag]:
|
| 150 |
+
return self.__dict__['__flags']
|
| 151 |
+
|
| 152 |
+
def flags_by_module_dict(self) -> Dict[str, List[Flag]]:
|
| 153 |
+
"""Returns the dictionary of module_name -> list of defined flags.
|
| 154 |
+
|
| 155 |
+
Returns:
|
| 156 |
+
A dictionary. Its keys are module names (strings). Its values
|
| 157 |
+
are lists of Flag objects.
|
| 158 |
+
"""
|
| 159 |
+
return self.__dict__['__flags_by_module']
|
| 160 |
+
|
| 161 |
+
def flags_by_module_id_dict(self) -> Dict[int, List[Flag]]:
|
| 162 |
+
"""Returns the dictionary of module_id -> list of defined flags.
|
| 163 |
+
|
| 164 |
+
Returns:
|
| 165 |
+
A dictionary. Its keys are module IDs (ints). Its values
|
| 166 |
+
are lists of Flag objects.
|
| 167 |
+
"""
|
| 168 |
+
return self.__dict__['__flags_by_module_id']
|
| 169 |
+
|
| 170 |
+
def key_flags_by_module_dict(self) -> Dict[str, List[Flag]]:
|
| 171 |
+
"""Returns the dictionary of module_name -> list of key flags.
|
| 172 |
+
|
| 173 |
+
Returns:
|
| 174 |
+
A dictionary. Its keys are module names (strings). Its values
|
| 175 |
+
are lists of Flag objects.
|
| 176 |
+
"""
|
| 177 |
+
return self.__dict__['__key_flags_by_module']
|
| 178 |
+
|
| 179 |
+
def register_flag_by_module(self, module_name: str, flag: Flag) -> None:
|
| 180 |
+
"""Records the module that defines a specific flag.
|
| 181 |
+
|
| 182 |
+
We keep track of which flag is defined by which module so that we
|
| 183 |
+
can later sort the flags by module.
|
| 184 |
+
|
| 185 |
+
Args:
|
| 186 |
+
module_name: str, the name of a Python module.
|
| 187 |
+
flag: Flag, the Flag instance that is key to the module.
|
| 188 |
+
"""
|
| 189 |
+
flags_by_module = self.flags_by_module_dict()
|
| 190 |
+
flags_by_module.setdefault(module_name, []).append(flag)
|
| 191 |
+
|
| 192 |
+
def register_flag_by_module_id(self, module_id: int, flag: Flag) -> None:
|
| 193 |
+
"""Records the module that defines a specific flag.
|
| 194 |
+
|
| 195 |
+
Args:
|
| 196 |
+
module_id: int, the ID of the Python module.
|
| 197 |
+
flag: Flag, the Flag instance that is key to the module.
|
| 198 |
+
"""
|
| 199 |
+
flags_by_module_id = self.flags_by_module_id_dict()
|
| 200 |
+
flags_by_module_id.setdefault(module_id, []).append(flag)
|
| 201 |
+
|
| 202 |
+
def register_key_flag_for_module(self, module_name: str, flag: Flag) -> None:
|
| 203 |
+
"""Specifies that a flag is a key flag for a module.
|
| 204 |
+
|
| 205 |
+
Args:
|
| 206 |
+
module_name: str, the name of a Python module.
|
| 207 |
+
flag: Flag, the Flag instance that is key to the module.
|
| 208 |
+
"""
|
| 209 |
+
key_flags_by_module = self.key_flags_by_module_dict()
|
| 210 |
+
# The list of key flags for the module named module_name.
|
| 211 |
+
key_flags = key_flags_by_module.setdefault(module_name, [])
|
| 212 |
+
# Add flag, but avoid duplicates.
|
| 213 |
+
if flag not in key_flags:
|
| 214 |
+
key_flags.append(flag)
|
| 215 |
+
|
| 216 |
+
def _flag_is_registered(self, flag_obj: Flag) -> bool:
|
| 217 |
+
"""Checks whether a Flag object is registered under long name or short name.
|
| 218 |
+
|
| 219 |
+
Args:
|
| 220 |
+
flag_obj: Flag, the Flag instance to check for.
|
| 221 |
+
|
| 222 |
+
Returns:
|
| 223 |
+
bool, True iff flag_obj is registered under long name or short name.
|
| 224 |
+
"""
|
| 225 |
+
flag_dict = self._flags()
|
| 226 |
+
# Check whether flag_obj is registered under its long name.
|
| 227 |
+
name = flag_obj.name
|
| 228 |
+
if name in flag_dict and flag_dict[name] == flag_obj:
|
| 229 |
+
return True
|
| 230 |
+
# Check whether flag_obj is registered under its short name.
|
| 231 |
+
short_name = flag_obj.short_name
|
| 232 |
+
if (
|
| 233 |
+
short_name is not None
|
| 234 |
+
and short_name in flag_dict
|
| 235 |
+
and flag_dict[short_name] == flag_obj
|
| 236 |
+
):
|
| 237 |
+
return True
|
| 238 |
+
return False
|
| 239 |
+
|
| 240 |
+
def _cleanup_unregistered_flag_from_module_dicts(
|
| 241 |
+
self, flag_obj: Flag
|
| 242 |
+
) -> None:
|
| 243 |
+
"""Cleans up unregistered flags from all module -> [flags] dictionaries.
|
| 244 |
+
|
| 245 |
+
If flag_obj is registered under either its long name or short name, it
|
| 246 |
+
won't be removed from the dictionaries.
|
| 247 |
+
|
| 248 |
+
Args:
|
| 249 |
+
flag_obj: Flag, the Flag instance to clean up for.
|
| 250 |
+
"""
|
| 251 |
+
if self._flag_is_registered(flag_obj):
|
| 252 |
+
return
|
| 253 |
+
# Materialize dict values to list to avoid concurrent modification.
|
| 254 |
+
for flags_in_module in [
|
| 255 |
+
*self.flags_by_module_dict().values(),
|
| 256 |
+
*self.flags_by_module_id_dict().values(),
|
| 257 |
+
*self.key_flags_by_module_dict().values(),
|
| 258 |
+
]:
|
| 259 |
+
# While (as opposed to if) takes care of multiple occurrences of a
|
| 260 |
+
# flag in the list for the same module.
|
| 261 |
+
while flag_obj in flags_in_module:
|
| 262 |
+
flags_in_module.remove(flag_obj)
|
| 263 |
+
|
| 264 |
+
def get_flags_for_module(self, module: Union[str, Any]) -> List[Flag]:
|
| 265 |
+
"""Returns the list of flags defined by a module.
|
| 266 |
+
|
| 267 |
+
Args:
|
| 268 |
+
module: module|str, the module to get flags from.
|
| 269 |
+
|
| 270 |
+
Returns:
|
| 271 |
+
[Flag], a new list of Flag instances. Caller may update this list as
|
| 272 |
+
desired: none of those changes will affect the internals of this
|
| 273 |
+
FlagValue instance.
|
| 274 |
+
"""
|
| 275 |
+
if not isinstance(module, str):
|
| 276 |
+
module = module.__name__
|
| 277 |
+
if module == '__main__':
|
| 278 |
+
module = sys.argv[0]
|
| 279 |
+
|
| 280 |
+
return list(self.flags_by_module_dict().get(module, []))
|
| 281 |
+
|
| 282 |
+
def get_key_flags_for_module(self, module: Union[str, Any]) -> List[Flag]:
|
| 283 |
+
"""Returns the list of key flags for a module.
|
| 284 |
+
|
| 285 |
+
Args:
|
| 286 |
+
module: module|str, the module to get key flags from.
|
| 287 |
+
|
| 288 |
+
Returns:
|
| 289 |
+
[Flag], a new list of Flag instances. Caller may update this list as
|
| 290 |
+
desired: none of those changes will affect the internals of this
|
| 291 |
+
FlagValue instance.
|
| 292 |
+
"""
|
| 293 |
+
if not isinstance(module, str):
|
| 294 |
+
module = module.__name__
|
| 295 |
+
if module == '__main__':
|
| 296 |
+
module = sys.argv[0]
|
| 297 |
+
|
| 298 |
+
# Any flag is a key flag for the module that defined it. NOTE:
|
| 299 |
+
# key_flags is a fresh list: we can update it without affecting the
|
| 300 |
+
# internals of this FlagValues object.
|
| 301 |
+
key_flags = self.get_flags_for_module(module)
|
| 302 |
+
|
| 303 |
+
# Take into account flags explicitly declared as key for a module.
|
| 304 |
+
for flag in self.key_flags_by_module_dict().get(module, []):
|
| 305 |
+
if flag not in key_flags:
|
| 306 |
+
key_flags.append(flag)
|
| 307 |
+
return key_flags
|
| 308 |
+
|
| 309 |
+
# TODO(yileiyang): Restrict default to Optional[str].
|
| 310 |
+
def find_module_defining_flag(
|
| 311 |
+
self, flagname: str, default: Optional[_T] = None
|
| 312 |
+
) -> Union[str, Optional[_T]]:
|
| 313 |
+
"""Return the name of the module defining this flag, or default.
|
| 314 |
+
|
| 315 |
+
Args:
|
| 316 |
+
flagname: str, name of the flag to lookup.
|
| 317 |
+
default: Value to return if flagname is not defined. Defaults to None.
|
| 318 |
+
|
| 319 |
+
Returns:
|
| 320 |
+
The name of the module which registered the flag with this name.
|
| 321 |
+
If no such module exists (i.e. no flag with this name exists),
|
| 322 |
+
we return default.
|
| 323 |
+
"""
|
| 324 |
+
registered_flag = self._flags().get(flagname)
|
| 325 |
+
if registered_flag is None:
|
| 326 |
+
return default
|
| 327 |
+
for module, flags in self.flags_by_module_dict().items():
|
| 328 |
+
for flag in flags:
|
| 329 |
+
# It must compare the flag with the one in _flags. This is because a
|
| 330 |
+
# flag might be overridden only for its long name (or short name),
|
| 331 |
+
# and only its short name (or long name) is considered registered.
|
| 332 |
+
if (flag.name == registered_flag.name and
|
| 333 |
+
flag.short_name == registered_flag.short_name):
|
| 334 |
+
return module
|
| 335 |
+
return default
|
| 336 |
+
|
| 337 |
+
# TODO(yileiyang): Restrict default to Optional[str].
|
| 338 |
+
def find_module_id_defining_flag(
|
| 339 |
+
self, flagname: str, default: Optional[_T] = None
|
| 340 |
+
) -> Union[int, Optional[_T]]:
|
| 341 |
+
"""Return the ID of the module defining this flag, or default.
|
| 342 |
+
|
| 343 |
+
Args:
|
| 344 |
+
flagname: str, name of the flag to lookup.
|
| 345 |
+
default: Value to return if flagname is not defined. Defaults to None.
|
| 346 |
+
|
| 347 |
+
Returns:
|
| 348 |
+
The ID of the module which registered the flag with this name.
|
| 349 |
+
If no such module exists (i.e. no flag with this name exists),
|
| 350 |
+
we return default.
|
| 351 |
+
"""
|
| 352 |
+
registered_flag = self._flags().get(flagname)
|
| 353 |
+
if registered_flag is None:
|
| 354 |
+
return default
|
| 355 |
+
for module_id, flags in self.flags_by_module_id_dict().items():
|
| 356 |
+
for flag in flags:
|
| 357 |
+
# It must compare the flag with the one in _flags. This is because a
|
| 358 |
+
# flag might be overridden only for its long name (or short name),
|
| 359 |
+
# and only its short name (or long name) is considered registered.
|
| 360 |
+
if (flag.name == registered_flag.name and
|
| 361 |
+
flag.short_name == registered_flag.short_name):
|
| 362 |
+
return module_id
|
| 363 |
+
return default
|
| 364 |
+
|
| 365 |
+
def _register_unknown_flag_setter(
|
| 366 |
+
self, setter: Callable[[str, Any], None]
|
| 367 |
+
) -> None:
|
| 368 |
+
"""Allow set default values for undefined flags.
|
| 369 |
+
|
| 370 |
+
Args:
|
| 371 |
+
setter: Method(name, value) to call to __setattr__ an unknown flag. Must
|
| 372 |
+
raise NameError or ValueError for invalid name/value.
|
| 373 |
+
"""
|
| 374 |
+
self.__dict__['__set_unknown'] = setter
|
| 375 |
+
|
| 376 |
+
def _set_unknown_flag(self, name: str, value: _T) -> _T:
|
| 377 |
+
"""Returns value if setting flag |name| to |value| returned True.
|
| 378 |
+
|
| 379 |
+
Args:
|
| 380 |
+
name: str, name of the flag to set.
|
| 381 |
+
value: Value to set.
|
| 382 |
+
|
| 383 |
+
Returns:
|
| 384 |
+
Flag value on successful call.
|
| 385 |
+
|
| 386 |
+
Raises:
|
| 387 |
+
UnrecognizedFlagError
|
| 388 |
+
IllegalFlagValueError
|
| 389 |
+
"""
|
| 390 |
+
setter = self.__dict__['__set_unknown']
|
| 391 |
+
if setter:
|
| 392 |
+
try:
|
| 393 |
+
setter(name, value)
|
| 394 |
+
return value
|
| 395 |
+
except (TypeError, ValueError): # Flag value is not valid.
|
| 396 |
+
raise _exceptions.IllegalFlagValueError(
|
| 397 |
+
f'"{value}" is not valid for --{name}'
|
| 398 |
+
)
|
| 399 |
+
except NameError: # Flag name is not valid.
|
| 400 |
+
pass
|
| 401 |
+
raise _exceptions.UnrecognizedFlagError(name, value)
|
| 402 |
+
|
| 403 |
+
def append_flag_values(self, flag_values: 'FlagValues') -> None:
|
| 404 |
+
"""Appends flags registered in another FlagValues instance.
|
| 405 |
+
|
| 406 |
+
Args:
|
| 407 |
+
flag_values: FlagValues, the FlagValues instance from which to copy flags.
|
| 408 |
+
"""
|
| 409 |
+
for flag_name, flag in flag_values._flags().items(): # pylint: disable=protected-access
|
| 410 |
+
# Each flags with short_name appears here twice (once under its
|
| 411 |
+
# normal name, and again with its short name). To prevent
|
| 412 |
+
# problems (DuplicateFlagError) with double flag registration, we
|
| 413 |
+
# perform a check to make sure that the entry we're looking at is
|
| 414 |
+
# for its normal name.
|
| 415 |
+
if flag_name == flag.name:
|
| 416 |
+
try:
|
| 417 |
+
self[flag_name] = flag
|
| 418 |
+
except _exceptions.DuplicateFlagError:
|
| 419 |
+
raise _exceptions.DuplicateFlagError.from_flag(
|
| 420 |
+
flag_name, self, other_flag_values=flag_values)
|
| 421 |
+
|
| 422 |
+
def remove_flag_values(
|
| 423 |
+
self, flag_values: 'Union[FlagValues, Iterable[str]]'
|
| 424 |
+
) -> None:
|
| 425 |
+
"""Remove flags that were previously appended from another FlagValues.
|
| 426 |
+
|
| 427 |
+
Args:
|
| 428 |
+
flag_values: FlagValues, the FlagValues instance containing flags to
|
| 429 |
+
remove.
|
| 430 |
+
"""
|
| 431 |
+
for flag_name in flag_values:
|
| 432 |
+
self.__delattr__(flag_name)
|
| 433 |
+
|
| 434 |
+
def __setitem__(self, name: str, flag: Flag) -> None:
|
| 435 |
+
"""Registers a new flag variable."""
|
| 436 |
+
fl = self._flags()
|
| 437 |
+
if not isinstance(flag, _flag.Flag):
|
| 438 |
+
raise _exceptions.IllegalFlagValueError(
|
| 439 |
+
f'Expect Flag instances, found type {type(flag)}. '
|
| 440 |
+
"Maybe you didn't mean to use FlagValue.__setitem__?")
|
| 441 |
+
if not isinstance(name, str):
|
| 442 |
+
raise _exceptions.Error('Flag name must be a string')
|
| 443 |
+
if not name:
|
| 444 |
+
raise _exceptions.Error('Flag name cannot be empty')
|
| 445 |
+
if ' ' in name:
|
| 446 |
+
raise _exceptions.Error('Flag name cannot contain a space')
|
| 447 |
+
self._check_method_name_conflicts(name, flag)
|
| 448 |
+
if name in fl and not flag.allow_override and not fl[name].allow_override:
|
| 449 |
+
module, module_name = _helpers.get_calling_module_object_and_name()
|
| 450 |
+
if (self.find_module_defining_flag(name) == module_name and
|
| 451 |
+
id(module) != self.find_module_id_defining_flag(name)):
|
| 452 |
+
# If the flag has already been defined by a module with the same name,
|
| 453 |
+
# but a different ID, we can stop here because it indicates that the
|
| 454 |
+
# module is simply being imported a subsequent time.
|
| 455 |
+
return
|
| 456 |
+
raise _exceptions.DuplicateFlagError.from_flag(name, self)
|
| 457 |
+
# If a new flag overrides an old one, we need to cleanup the old flag's
|
| 458 |
+
# modules if it's not registered.
|
| 459 |
+
flags_to_cleanup = set()
|
| 460 |
+
short_name: Optional[str] = flag.short_name
|
| 461 |
+
if short_name is not None:
|
| 462 |
+
if (short_name in fl and not flag.allow_override and
|
| 463 |
+
not fl[short_name].allow_override):
|
| 464 |
+
raise _exceptions.DuplicateFlagError.from_flag(short_name, self)
|
| 465 |
+
if short_name in fl and fl[short_name] != flag:
|
| 466 |
+
flags_to_cleanup.add(fl[short_name])
|
| 467 |
+
fl[short_name] = flag
|
| 468 |
+
if (name not in fl # new flag
|
| 469 |
+
or fl[name].using_default_value or not flag.using_default_value):
|
| 470 |
+
if name in fl and fl[name] != flag:
|
| 471 |
+
flags_to_cleanup.add(fl[name])
|
| 472 |
+
fl[name] = flag
|
| 473 |
+
for f in flags_to_cleanup:
|
| 474 |
+
self._cleanup_unregistered_flag_from_module_dicts(f)
|
| 475 |
+
|
| 476 |
+
def __dir__(self) -> List[str]:
|
| 477 |
+
"""Returns list of names of all defined flags.
|
| 478 |
+
|
| 479 |
+
Useful for TAB-completion in ipython.
|
| 480 |
+
|
| 481 |
+
Returns:
|
| 482 |
+
[str], a list of names of all defined flags.
|
| 483 |
+
"""
|
| 484 |
+
return sorted(self._flags())
|
| 485 |
+
|
| 486 |
+
def __getitem__(self, name: str) -> Flag:
|
| 487 |
+
"""Returns the Flag object for the flag --name."""
|
| 488 |
+
return self._flags()[name]
|
| 489 |
+
|
| 490 |
+
def _hide_flag(self, name):
|
| 491 |
+
"""Marks the flag --name as hidden."""
|
| 492 |
+
self.__dict__['__hiddenflags'].add(name)
|
| 493 |
+
|
| 494 |
+
def __getattr__(self, name: str) -> Any:
|
| 495 |
+
"""Retrieves the 'value' attribute of the flag --name."""
|
| 496 |
+
flag_entry = self._flags().get(name)
|
| 497 |
+
if flag_entry is None:
|
| 498 |
+
raise AttributeError(name)
|
| 499 |
+
if name in self.__dict__['__hiddenflags']:
|
| 500 |
+
raise AttributeError(name)
|
| 501 |
+
|
| 502 |
+
if self.__dict__['__flags_parsed'] or flag_entry.present:
|
| 503 |
+
return flag_entry.value
|
| 504 |
+
else:
|
| 505 |
+
raise _exceptions.UnparsedFlagAccessError(
|
| 506 |
+
'Trying to access flag --%s before flags were parsed.' % name)
|
| 507 |
+
|
| 508 |
+
def __setattr__(self, name: str, value: _T) -> _T:
|
| 509 |
+
"""Sets the 'value' attribute of the flag --name."""
|
| 510 |
+
self._set_attributes(**{name: value})
|
| 511 |
+
return value
|
| 512 |
+
|
| 513 |
+
def _set_attributes(self, **attributes: Any) -> None:
|
| 514 |
+
"""Sets multiple flag values together, triggers validators afterwards."""
|
| 515 |
+
fl = self._flags()
|
| 516 |
+
known_flag_vals = {}
|
| 517 |
+
known_flag_used_defaults = {}
|
| 518 |
+
try:
|
| 519 |
+
for name, value in attributes.items():
|
| 520 |
+
if name in self.__dict__['__hiddenflags']:
|
| 521 |
+
raise AttributeError(name)
|
| 522 |
+
flag_entry = fl.get(name)
|
| 523 |
+
if flag_entry is not None:
|
| 524 |
+
orig = flag_entry.value
|
| 525 |
+
flag_entry.value = value
|
| 526 |
+
known_flag_vals[name] = orig
|
| 527 |
+
else:
|
| 528 |
+
self._set_unknown_flag(name, value)
|
| 529 |
+
for name in known_flag_vals:
|
| 530 |
+
self._assert_validators(fl[name].validators)
|
| 531 |
+
known_flag_used_defaults[name] = fl[name].using_default_value
|
| 532 |
+
fl[name].using_default_value = False
|
| 533 |
+
except:
|
| 534 |
+
for name, orig in known_flag_vals.items():
|
| 535 |
+
fl[name].value = orig
|
| 536 |
+
for name, orig in known_flag_used_defaults.items():
|
| 537 |
+
fl[name].using_default_value = orig
|
| 538 |
+
# NOTE: We do not attempt to undo unknown flag side effects because we
|
| 539 |
+
# cannot reliably undo the user-configured behavior.
|
| 540 |
+
raise
|
| 541 |
+
|
| 542 |
+
def validate_all_flags(self) -> None:
|
| 543 |
+
"""Verifies whether all flags pass validation.
|
| 544 |
+
|
| 545 |
+
Raises:
|
| 546 |
+
AttributeError: Raised if validators work with a non-existing flag.
|
| 547 |
+
IllegalFlagValueError: Raised if validation fails for at least one
|
| 548 |
+
validator.
|
| 549 |
+
"""
|
| 550 |
+
all_validators = set()
|
| 551 |
+
for flag in self._flags().values():
|
| 552 |
+
all_validators.update(flag.validators)
|
| 553 |
+
self._assert_validators(all_validators)
|
| 554 |
+
|
| 555 |
+
def _assert_validators(
|
| 556 |
+
self, validators: Iterable[_validators_classes.Validator]
|
| 557 |
+
) -> None:
|
| 558 |
+
"""Asserts if all validators in the list are satisfied.
|
| 559 |
+
|
| 560 |
+
It asserts validators in the order they were created.
|
| 561 |
+
|
| 562 |
+
Args:
|
| 563 |
+
validators: Iterable(validators.Validator), validators to be verified.
|
| 564 |
+
|
| 565 |
+
Raises:
|
| 566 |
+
AttributeError: Raised if validators work with a non-existing flag.
|
| 567 |
+
IllegalFlagValueError: Raised if validation fails for at least one
|
| 568 |
+
validator.
|
| 569 |
+
"""
|
| 570 |
+
messages = []
|
| 571 |
+
bad_flags: Set[str] = set()
|
| 572 |
+
for validator in sorted(
|
| 573 |
+
validators, key=lambda validator: validator.insertion_index):
|
| 574 |
+
try:
|
| 575 |
+
if isinstance(validator, _validators_classes.SingleFlagValidator):
|
| 576 |
+
if validator.flag_name in bad_flags:
|
| 577 |
+
continue
|
| 578 |
+
elif isinstance(validator, _validators_classes.MultiFlagsValidator):
|
| 579 |
+
if bad_flags & set(validator.flag_names):
|
| 580 |
+
continue
|
| 581 |
+
validator.verify(self)
|
| 582 |
+
except _exceptions.ValidationError as e:
|
| 583 |
+
if isinstance(validator, _validators_classes.SingleFlagValidator):
|
| 584 |
+
bad_flags.add(validator.flag_name)
|
| 585 |
+
elif isinstance(validator, _validators_classes.MultiFlagsValidator):
|
| 586 |
+
bad_flags.update(set(validator.flag_names))
|
| 587 |
+
message = validator.print_flags_with_values(self)
|
| 588 |
+
messages.append('%s: %s' % (message, str(e)))
|
| 589 |
+
if messages:
|
| 590 |
+
raise _exceptions.IllegalFlagValueError('\n'.join(messages))
|
| 591 |
+
|
| 592 |
+
def __delattr__(self, flag_name: str) -> None:
|
| 593 |
+
"""Deletes a previously-defined flag from a flag object.
|
| 594 |
+
|
| 595 |
+
This method makes sure we can delete a flag by using
|
| 596 |
+
|
| 597 |
+
del FLAGS.<flag_name>
|
| 598 |
+
|
| 599 |
+
E.g.,
|
| 600 |
+
|
| 601 |
+
flags.DEFINE_integer('foo', 1, 'Integer flag.')
|
| 602 |
+
del flags.FLAGS.foo
|
| 603 |
+
|
| 604 |
+
If a flag is also registered by its the other name (long name or short
|
| 605 |
+
name), the other name won't be deleted.
|
| 606 |
+
|
| 607 |
+
Args:
|
| 608 |
+
flag_name: str, the name of the flag to be deleted.
|
| 609 |
+
|
| 610 |
+
Raises:
|
| 611 |
+
AttributeError: Raised when there is no registered flag named flag_name.
|
| 612 |
+
"""
|
| 613 |
+
fl = self._flags()
|
| 614 |
+
flag_entry = fl.get(flag_name)
|
| 615 |
+
if flag_entry is None:
|
| 616 |
+
raise AttributeError(flag_name)
|
| 617 |
+
del fl[flag_name]
|
| 618 |
+
|
| 619 |
+
self._cleanup_unregistered_flag_from_module_dicts(flag_entry)
|
| 620 |
+
|
| 621 |
+
def set_default(self, name: str, value: Any) -> None:
|
| 622 |
+
"""Changes the default value of the named flag object.
|
| 623 |
+
|
| 624 |
+
The flag's current value is also updated if the flag is currently using
|
| 625 |
+
the default value, i.e. not specified in the command line, and not set
|
| 626 |
+
by FLAGS.name = value.
|
| 627 |
+
|
| 628 |
+
Args:
|
| 629 |
+
name: str, the name of the flag to modify.
|
| 630 |
+
value: The new default value.
|
| 631 |
+
|
| 632 |
+
Raises:
|
| 633 |
+
UnrecognizedFlagError: Raised when there is no registered flag named name.
|
| 634 |
+
IllegalFlagValueError: Raised when value is not valid.
|
| 635 |
+
"""
|
| 636 |
+
fl = self._flags()
|
| 637 |
+
flag_entry = fl.get(name)
|
| 638 |
+
if flag_entry is None:
|
| 639 |
+
self._set_unknown_flag(name, value)
|
| 640 |
+
return
|
| 641 |
+
flag_entry._set_default(value) # pylint: disable=protected-access
|
| 642 |
+
self._assert_validators(flag_entry.validators)
|
| 643 |
+
|
| 644 |
+
def __contains__(self, name: str) -> bool:
|
| 645 |
+
"""Returns True if name is a value (flag) in the dict."""
|
| 646 |
+
return name in self._flags()
|
| 647 |
+
|
| 648 |
+
def __len__(self) -> int:
|
| 649 |
+
return len(self.__dict__['__flags'])
|
| 650 |
+
|
| 651 |
+
def __iter__(self) -> Iterator[str]:
|
| 652 |
+
return iter(self._flags())
|
| 653 |
+
|
| 654 |
+
def __call__(
|
| 655 |
+
self, argv: Sequence[str], known_only: bool = False
|
| 656 |
+
) -> List[str]:
|
| 657 |
+
"""Parses flags from argv; stores parsed flags into this FlagValues object.
|
| 658 |
+
|
| 659 |
+
All unparsed arguments are returned.
|
| 660 |
+
|
| 661 |
+
Args:
|
| 662 |
+
argv: a tuple/list of strings.
|
| 663 |
+
known_only: bool, if True, parse and remove known flags; return the rest
|
| 664 |
+
untouched. Unknown flags specified by --undefok are not returned.
|
| 665 |
+
|
| 666 |
+
Returns:
|
| 667 |
+
The list of arguments not parsed as options, including argv[0].
|
| 668 |
+
|
| 669 |
+
Raises:
|
| 670 |
+
Error: Raised on any parsing error.
|
| 671 |
+
TypeError: Raised on passing wrong type of arguments.
|
| 672 |
+
ValueError: Raised on flag value parsing error.
|
| 673 |
+
"""
|
| 674 |
+
if isinstance(argv, (str, bytes)):
|
| 675 |
+
raise TypeError(
|
| 676 |
+
'argv should be a tuple/list of strings, not bytes or string.')
|
| 677 |
+
if not argv:
|
| 678 |
+
raise ValueError(
|
| 679 |
+
'argv cannot be an empty list, and must contain the program name as '
|
| 680 |
+
'the first element.')
|
| 681 |
+
|
| 682 |
+
# This pre parses the argv list for --flagfile=<> options.
|
| 683 |
+
program_name = argv[0]
|
| 684 |
+
args = self.read_flags_from_files(argv[1:], force_gnu=False)
|
| 685 |
+
|
| 686 |
+
# Parse the arguments.
|
| 687 |
+
unknown_flags, unparsed_args = self._parse_args(args, known_only)
|
| 688 |
+
|
| 689 |
+
# Handle unknown flags by raising UnrecognizedFlagError.
|
| 690 |
+
# Note some users depend on us raising this particular error.
|
| 691 |
+
for name, value in unknown_flags:
|
| 692 |
+
suggestions = _helpers.get_flag_suggestions(name, list(self))
|
| 693 |
+
raise _exceptions.UnrecognizedFlagError(
|
| 694 |
+
name, value, suggestions=suggestions)
|
| 695 |
+
|
| 696 |
+
self.mark_as_parsed()
|
| 697 |
+
self.validate_all_flags()
|
| 698 |
+
return [program_name] + unparsed_args
|
| 699 |
+
|
| 700 |
+
def __getstate__(self) -> Any:
|
| 701 |
+
raise TypeError("can't pickle FlagValues")
|
| 702 |
+
|
| 703 |
+
def __copy__(self) -> Any:
|
| 704 |
+
raise TypeError('FlagValues does not support shallow copies. '
|
| 705 |
+
'Use absl.testing.flagsaver or copy.deepcopy instead.')
|
| 706 |
+
|
| 707 |
+
def __deepcopy__(self, memo) -> Any:
|
| 708 |
+
result = object.__new__(type(self))
|
| 709 |
+
result.__dict__.update(copy.deepcopy(self.__dict__, memo))
|
| 710 |
+
return result
|
| 711 |
+
|
| 712 |
+
def _set_is_retired_flag_func(self, is_retired_flag_func):
|
| 713 |
+
"""Sets a function for checking retired flags.
|
| 714 |
+
|
| 715 |
+
Do not use it. This is a private absl API used to check retired flags
|
| 716 |
+
registered by the absl C++ flags library.
|
| 717 |
+
|
| 718 |
+
Args:
|
| 719 |
+
is_retired_flag_func: Callable(str) -> (bool, bool), a function takes flag
|
| 720 |
+
name as parameter, returns a tuple (is_retired, type_is_bool).
|
| 721 |
+
"""
|
| 722 |
+
self.__dict__['__is_retired_flag_func'] = is_retired_flag_func
|
| 723 |
+
|
| 724 |
+
def _parse_args(
|
| 725 |
+
self, args: List[str], known_only: bool
|
| 726 |
+
) -> Tuple[List[Tuple[str, Any]], List[str]]:
|
| 727 |
+
"""Helper function to do the main argument parsing.
|
| 728 |
+
|
| 729 |
+
This function goes through args and does the bulk of the flag parsing.
|
| 730 |
+
It will find the corresponding flag in our flag dictionary, and call its
|
| 731 |
+
.parse() method on the flag value.
|
| 732 |
+
|
| 733 |
+
Args:
|
| 734 |
+
args: [str], a list of strings with the arguments to parse.
|
| 735 |
+
known_only: bool, if True, parse and remove known flags; return the rest
|
| 736 |
+
untouched. Unknown flags specified by --undefok are not returned.
|
| 737 |
+
|
| 738 |
+
Returns:
|
| 739 |
+
A tuple with the following:
|
| 740 |
+
unknown_flags: List of (flag name, arg) for flags we don't know about.
|
| 741 |
+
unparsed_args: List of arguments we did not parse.
|
| 742 |
+
|
| 743 |
+
Raises:
|
| 744 |
+
Error: Raised on any parsing error.
|
| 745 |
+
ValueError: Raised on flag value parsing error.
|
| 746 |
+
"""
|
| 747 |
+
unparsed_names_and_args: List[Tuple[Optional[str], str]] = []
|
| 748 |
+
undefok: Set[str] = set()
|
| 749 |
+
retired_flag_func = self.__dict__['__is_retired_flag_func']
|
| 750 |
+
|
| 751 |
+
flag_dict = self._flags()
|
| 752 |
+
args_it = iter(args)
|
| 753 |
+
del args
|
| 754 |
+
for arg in args_it:
|
| 755 |
+
value = None
|
| 756 |
+
|
| 757 |
+
def get_value() -> str:
|
| 758 |
+
try:
|
| 759 |
+
return next(args_it) if value is None else value # pylint: disable=cell-var-from-loop
|
| 760 |
+
except StopIteration:
|
| 761 |
+
raise _exceptions.Error('Missing value for flag ' + arg) from None # pylint: disable=cell-var-from-loop
|
| 762 |
+
|
| 763 |
+
if not arg.startswith('-'):
|
| 764 |
+
# A non-argument: default is break, GNU is skip.
|
| 765 |
+
unparsed_names_and_args.append((None, arg))
|
| 766 |
+
if self.is_gnu_getopt():
|
| 767 |
+
continue
|
| 768 |
+
else:
|
| 769 |
+
break
|
| 770 |
+
|
| 771 |
+
if arg == '--':
|
| 772 |
+
if known_only:
|
| 773 |
+
unparsed_names_and_args.append((None, arg))
|
| 774 |
+
break
|
| 775 |
+
|
| 776 |
+
# At this point, arg must start with '-'.
|
| 777 |
+
if arg.startswith('--'):
|
| 778 |
+
arg_without_dashes = arg[2:]
|
| 779 |
+
else:
|
| 780 |
+
arg_without_dashes = arg[1:]
|
| 781 |
+
|
| 782 |
+
if '=' in arg_without_dashes:
|
| 783 |
+
name, value = arg_without_dashes.split('=', 1)
|
| 784 |
+
else:
|
| 785 |
+
name, value = arg_without_dashes, None
|
| 786 |
+
|
| 787 |
+
if not name:
|
| 788 |
+
# The argument is all dashes (including one dash).
|
| 789 |
+
unparsed_names_and_args.append((None, arg))
|
| 790 |
+
if self.is_gnu_getopt():
|
| 791 |
+
continue
|
| 792 |
+
else:
|
| 793 |
+
break
|
| 794 |
+
|
| 795 |
+
# --undefok is a special case.
|
| 796 |
+
if name == 'undefok':
|
| 797 |
+
value = get_value()
|
| 798 |
+
undefok.update(v.strip() for v in value.split(','))
|
| 799 |
+
undefok.update('no' + v.strip() for v in value.split(','))
|
| 800 |
+
continue
|
| 801 |
+
|
| 802 |
+
flag = flag_dict.get(name)
|
| 803 |
+
if flag is not None:
|
| 804 |
+
if flag.boolean and value is None:
|
| 805 |
+
value = 'true'
|
| 806 |
+
else:
|
| 807 |
+
value = get_value()
|
| 808 |
+
elif name.startswith('no') and len(name) > 2:
|
| 809 |
+
# Boolean flags can take the form of --noflag, with no value.
|
| 810 |
+
noflag = flag_dict.get(name[2:])
|
| 811 |
+
if noflag is not None and noflag.boolean:
|
| 812 |
+
if value is not None:
|
| 813 |
+
raise ValueError(arg + ' does not take an argument')
|
| 814 |
+
flag = noflag
|
| 815 |
+
value = 'false'
|
| 816 |
+
|
| 817 |
+
if retired_flag_func and flag is None:
|
| 818 |
+
is_retired, is_bool = retired_flag_func(name)
|
| 819 |
+
|
| 820 |
+
# If we didn't recognize that flag, but it starts with
|
| 821 |
+
# "no" then maybe it was a boolean flag specified in the
|
| 822 |
+
# --nofoo form.
|
| 823 |
+
if not is_retired and name.startswith('no'):
|
| 824 |
+
is_retired, is_bool = retired_flag_func(name[2:])
|
| 825 |
+
is_retired = is_retired and is_bool
|
| 826 |
+
|
| 827 |
+
if is_retired:
|
| 828 |
+
if not is_bool and value is None:
|
| 829 |
+
# This happens when a non-bool retired flag is specified
|
| 830 |
+
# in format of "--flag value".
|
| 831 |
+
get_value()
|
| 832 |
+
logging.error(
|
| 833 |
+
'Flag "%s" is retired and should no longer be specified. See '
|
| 834 |
+
'https://abseil.io/tips/90.',
|
| 835 |
+
name,
|
| 836 |
+
)
|
| 837 |
+
continue
|
| 838 |
+
|
| 839 |
+
if flag is not None:
|
| 840 |
+
# LINT.IfChange
|
| 841 |
+
flag.parse(value)
|
| 842 |
+
flag.using_default_value = False
|
| 843 |
+
# LINT.ThenChange(../testing/flagsaver.py:flag_override_parsing)
|
| 844 |
+
else:
|
| 845 |
+
unparsed_names_and_args.append((name, arg))
|
| 846 |
+
|
| 847 |
+
unknown_flags = []
|
| 848 |
+
unparsed_args = []
|
| 849 |
+
for arg_name, arg in unparsed_names_and_args:
|
| 850 |
+
if arg_name is None:
|
| 851 |
+
# Positional arguments.
|
| 852 |
+
unparsed_args.append(arg)
|
| 853 |
+
elif arg_name in undefok:
|
| 854 |
+
# Remove undefok flags.
|
| 855 |
+
continue
|
| 856 |
+
else:
|
| 857 |
+
# This is an unknown flag.
|
| 858 |
+
if known_only:
|
| 859 |
+
unparsed_args.append(arg)
|
| 860 |
+
else:
|
| 861 |
+
unknown_flags.append((arg_name, arg))
|
| 862 |
+
|
| 863 |
+
unparsed_args.extend(list(args_it))
|
| 864 |
+
return unknown_flags, unparsed_args
|
| 865 |
+
|
| 866 |
+
def is_parsed(self) -> bool:
|
| 867 |
+
"""Returns whether flags were parsed."""
|
| 868 |
+
return self.__dict__['__flags_parsed']
|
| 869 |
+
|
| 870 |
+
def mark_as_parsed(self) -> None:
|
| 871 |
+
"""Explicitly marks flags as parsed.
|
| 872 |
+
|
| 873 |
+
Use this when the caller knows that this FlagValues has been parsed as if
|
| 874 |
+
a ``__call__()`` invocation has happened. This is only a public method for
|
| 875 |
+
use by things like appcommands which do additional command like parsing.
|
| 876 |
+
"""
|
| 877 |
+
self.__dict__['__flags_parsed'] = True
|
| 878 |
+
|
| 879 |
+
def unparse_flags(self) -> None:
|
| 880 |
+
"""Unparses all flags to the point before any FLAGS(argv) was called."""
|
| 881 |
+
for f in self._flags().values():
|
| 882 |
+
f.unparse()
|
| 883 |
+
# We log this message before marking flags as unparsed to avoid a
|
| 884 |
+
# problem when the logging library causes flags access.
|
| 885 |
+
logging.info('unparse_flags() called; flags access will now raise errors.')
|
| 886 |
+
self.__dict__['__flags_parsed'] = False
|
| 887 |
+
self.__dict__['__unparse_flags_called'] = True
|
| 888 |
+
|
| 889 |
+
def flag_values_dict(self) -> Dict[str, Any]:
|
| 890 |
+
"""Returns a dictionary that maps flag names to flag values."""
|
| 891 |
+
return {name: flag.value for name, flag in list(self._flags().items())}
|
| 892 |
+
|
| 893 |
+
def __str__(self):
|
| 894 |
+
"""Returns a help string for all known flags."""
|
| 895 |
+
return self.get_help()
|
| 896 |
+
|
| 897 |
+
def get_help(
|
| 898 |
+
self, prefix: str = '', include_special_flags: bool = True
|
| 899 |
+
) -> str:
|
| 900 |
+
"""Returns a help string for all known flags.
|
| 901 |
+
|
| 902 |
+
Args:
|
| 903 |
+
prefix: str, per-line output prefix.
|
| 904 |
+
include_special_flags: bool, whether to include description of
|
| 905 |
+
SPECIAL_FLAGS, i.e. --flagfile and --undefok.
|
| 906 |
+
|
| 907 |
+
Returns:
|
| 908 |
+
str, formatted help message.
|
| 909 |
+
"""
|
| 910 |
+
flags_by_module = self.flags_by_module_dict()
|
| 911 |
+
if flags_by_module:
|
| 912 |
+
modules = sorted(flags_by_module)
|
| 913 |
+
# Print the help for the main module first, if possible.
|
| 914 |
+
main_module = sys.argv[0]
|
| 915 |
+
if main_module in modules:
|
| 916 |
+
modules.remove(main_module)
|
| 917 |
+
modules = [main_module] + modules
|
| 918 |
+
return self._get_help_for_modules(modules, prefix, include_special_flags)
|
| 919 |
+
else:
|
| 920 |
+
output_lines: List[str] = []
|
| 921 |
+
# Just print one long list of flags.
|
| 922 |
+
values = list(self._flags().values())
|
| 923 |
+
if include_special_flags:
|
| 924 |
+
values.extend(_helpers.SPECIAL_FLAGS._flags().values()) # pylint: disable=protected-access
|
| 925 |
+
self._render_flag_list(values, output_lines, prefix)
|
| 926 |
+
return '\n'.join(output_lines)
|
| 927 |
+
|
| 928 |
+
def _get_help_for_modules(self, modules, prefix, include_special_flags):
|
| 929 |
+
"""Returns the help string for a list of modules.
|
| 930 |
+
|
| 931 |
+
Private to absl.flags package.
|
| 932 |
+
|
| 933 |
+
Args:
|
| 934 |
+
modules: List[str], a list of modules to get the help string for.
|
| 935 |
+
prefix: str, a string that is prepended to each generated help line.
|
| 936 |
+
include_special_flags: bool, whether to include description of
|
| 937 |
+
SPECIAL_FLAGS, i.e. --flagfile and --undefok.
|
| 938 |
+
"""
|
| 939 |
+
output_lines = []
|
| 940 |
+
for module in modules:
|
| 941 |
+
self._render_our_module_flags(module, output_lines, prefix)
|
| 942 |
+
if include_special_flags:
|
| 943 |
+
self._render_module_flags(
|
| 944 |
+
'absl.flags',
|
| 945 |
+
_helpers.SPECIAL_FLAGS._flags().values(), # pylint: disable=protected-access # pytype: disable=attribute-error
|
| 946 |
+
output_lines,
|
| 947 |
+
prefix,
|
| 948 |
+
)
|
| 949 |
+
return '\n'.join(output_lines)
|
| 950 |
+
|
| 951 |
+
def _render_module_flags(self, module, flags, output_lines, prefix=''):
|
| 952 |
+
"""Returns a help string for a given module."""
|
| 953 |
+
if not isinstance(module, str):
|
| 954 |
+
module = module.__name__
|
| 955 |
+
output_lines.append('\n%s%s:' % (prefix, module))
|
| 956 |
+
self._render_flag_list(flags, output_lines, prefix + ' ')
|
| 957 |
+
|
| 958 |
+
def _render_our_module_flags(self, module, output_lines, prefix=''):
|
| 959 |
+
"""Returns a help string for a given module."""
|
| 960 |
+
flags = self.get_flags_for_module(module)
|
| 961 |
+
if flags:
|
| 962 |
+
self._render_module_flags(module, flags, output_lines, prefix)
|
| 963 |
+
|
| 964 |
+
def _render_our_module_key_flags(self, module, output_lines, prefix=''):
|
| 965 |
+
"""Returns a help string for the key flags of a given module.
|
| 966 |
+
|
| 967 |
+
Args:
|
| 968 |
+
module: module|str, the module to render key flags for.
|
| 969 |
+
output_lines: [str], a list of strings. The generated help message lines
|
| 970 |
+
will be appended to this list.
|
| 971 |
+
prefix: str, a string that is prepended to each generated help line.
|
| 972 |
+
"""
|
| 973 |
+
key_flags = self.get_key_flags_for_module(module)
|
| 974 |
+
if key_flags:
|
| 975 |
+
self._render_module_flags(module, key_flags, output_lines, prefix)
|
| 976 |
+
|
| 977 |
+
def module_help(self, module: Any) -> str:
|
| 978 |
+
"""Describes the key flags of a module.
|
| 979 |
+
|
| 980 |
+
Args:
|
| 981 |
+
module: module|str, the module to describe the key flags for.
|
| 982 |
+
|
| 983 |
+
Returns:
|
| 984 |
+
str, describing the key flags of a module.
|
| 985 |
+
"""
|
| 986 |
+
helplist: List[str] = []
|
| 987 |
+
self._render_our_module_key_flags(module, helplist)
|
| 988 |
+
return '\n'.join(helplist)
|
| 989 |
+
|
| 990 |
+
def main_module_help(self) -> str:
|
| 991 |
+
"""Describes the key flags of the main module.
|
| 992 |
+
|
| 993 |
+
Returns:
|
| 994 |
+
str, describing the key flags of the main module.
|
| 995 |
+
"""
|
| 996 |
+
return self.module_help(sys.argv[0])
|
| 997 |
+
|
| 998 |
+
def _render_flag_list(self, flaglist, output_lines, prefix=' '):
|
| 999 |
+
fl = self._flags()
|
| 1000 |
+
special_fl = _helpers.SPECIAL_FLAGS._flags() # pylint: disable=protected-access # pytype: disable=attribute-error
|
| 1001 |
+
flaglist = [(flag.name, flag) for flag in flaglist]
|
| 1002 |
+
flaglist.sort()
|
| 1003 |
+
flagset = {}
|
| 1004 |
+
for (name, flag) in flaglist:
|
| 1005 |
+
# It's possible this flag got deleted or overridden since being
|
| 1006 |
+
# registered in the per-module flaglist. Check now against the
|
| 1007 |
+
# canonical source of current flag information, the _flags.
|
| 1008 |
+
if fl.get(name, None) != flag and special_fl.get(name, None) != flag:
|
| 1009 |
+
# a different flag is using this name now
|
| 1010 |
+
continue
|
| 1011 |
+
# only print help once
|
| 1012 |
+
if flag in flagset:
|
| 1013 |
+
continue
|
| 1014 |
+
flagset[flag] = 1
|
| 1015 |
+
flaghelp = ''
|
| 1016 |
+
if flag.short_name:
|
| 1017 |
+
flaghelp += '-%s,' % flag.short_name
|
| 1018 |
+
if flag.boolean:
|
| 1019 |
+
flaghelp += '--[no]%s:' % flag.name
|
| 1020 |
+
else:
|
| 1021 |
+
flaghelp += '--%s:' % flag.name
|
| 1022 |
+
flaghelp += ' '
|
| 1023 |
+
if flag.help:
|
| 1024 |
+
flaghelp += flag.help
|
| 1025 |
+
flaghelp = _helpers.text_wrap(
|
| 1026 |
+
flaghelp, indent=prefix + ' ', firstline_indent=prefix)
|
| 1027 |
+
if flag.default_as_str:
|
| 1028 |
+
flaghelp += '\n'
|
| 1029 |
+
flaghelp += _helpers.text_wrap(
|
| 1030 |
+
'(default: %s)' % flag.default_as_str, indent=prefix + ' ')
|
| 1031 |
+
if flag.parser.syntactic_help:
|
| 1032 |
+
flaghelp += '\n'
|
| 1033 |
+
flaghelp += _helpers.text_wrap(
|
| 1034 |
+
'(%s)' % flag.parser.syntactic_help, indent=prefix + ' ')
|
| 1035 |
+
output_lines.append(flaghelp)
|
| 1036 |
+
|
| 1037 |
+
def get_flag_value(self, name: str, default: Any) -> Any: # pylint: disable=invalid-name
|
| 1038 |
+
"""Returns the value of a flag (if not None) or a default value.
|
| 1039 |
+
|
| 1040 |
+
Args:
|
| 1041 |
+
name: str, the name of a flag.
|
| 1042 |
+
default: Default value to use if the flag value is None.
|
| 1043 |
+
|
| 1044 |
+
Returns:
|
| 1045 |
+
Requested flag value or default.
|
| 1046 |
+
"""
|
| 1047 |
+
|
| 1048 |
+
value = self.__getattr__(name)
|
| 1049 |
+
if value is not None: # Can't do if not value, b/c value might be '0' or ""
|
| 1050 |
+
return value
|
| 1051 |
+
else:
|
| 1052 |
+
return default
|
| 1053 |
+
|
| 1054 |
+
def _is_flag_file_directive(self, flag_string):
|
| 1055 |
+
"""Checks whether flag_string contain a --flagfile=<foo> directive."""
|
| 1056 |
+
if isinstance(flag_string, str):
|
| 1057 |
+
if flag_string.startswith('--flagfile='):
|
| 1058 |
+
return 1
|
| 1059 |
+
elif flag_string == '--flagfile':
|
| 1060 |
+
return 1
|
| 1061 |
+
elif flag_string.startswith('-flagfile='):
|
| 1062 |
+
return 1
|
| 1063 |
+
elif flag_string == '-flagfile':
|
| 1064 |
+
return 1
|
| 1065 |
+
else:
|
| 1066 |
+
return 0
|
| 1067 |
+
return 0
|
| 1068 |
+
|
| 1069 |
+
def _extract_filename(self, flagfile_str):
|
| 1070 |
+
"""Returns filename from a flagfile_str of form -[-]flagfile=filename.
|
| 1071 |
+
|
| 1072 |
+
The cases of --flagfile foo and -flagfile foo shouldn't be hitting
|
| 1073 |
+
this function, as they are dealt with in the level above this
|
| 1074 |
+
function.
|
| 1075 |
+
|
| 1076 |
+
Args:
|
| 1077 |
+
flagfile_str: str, the flagfile string.
|
| 1078 |
+
|
| 1079 |
+
Returns:
|
| 1080 |
+
str, the filename from a flagfile_str of form -[-]flagfile=filename.
|
| 1081 |
+
|
| 1082 |
+
Raises:
|
| 1083 |
+
Error: Raised when illegal --flagfile is provided.
|
| 1084 |
+
"""
|
| 1085 |
+
if flagfile_str.startswith('--flagfile='):
|
| 1086 |
+
return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip())
|
| 1087 |
+
elif flagfile_str.startswith('-flagfile='):
|
| 1088 |
+
return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip())
|
| 1089 |
+
else:
|
| 1090 |
+
raise _exceptions.Error('Hit illegal --flagfile type: %s' % flagfile_str)
|
| 1091 |
+
|
| 1092 |
+
def _get_flag_file_lines(self, filename, parsed_file_stack=None):
|
| 1093 |
+
"""Returns the useful (!=comments, etc) lines from a file with flags.
|
| 1094 |
+
|
| 1095 |
+
Args:
|
| 1096 |
+
filename: str, the name of the flag file.
|
| 1097 |
+
parsed_file_stack: [str], a list of the names of the files that we have
|
| 1098 |
+
recursively encountered at the current depth. MUTATED BY THIS FUNCTION
|
| 1099 |
+
(but the original value is preserved upon successfully returning from
|
| 1100 |
+
function call).
|
| 1101 |
+
|
| 1102 |
+
Returns:
|
| 1103 |
+
List of strings. See the note below.
|
| 1104 |
+
|
| 1105 |
+
NOTE(springer): This function checks for a nested --flagfile=<foo>
|
| 1106 |
+
tag and handles the lower file recursively. It returns a list of
|
| 1107 |
+
all the lines that _could_ contain command flags. This is
|
| 1108 |
+
EVERYTHING except whitespace lines and comments (lines starting
|
| 1109 |
+
with '#' or '//').
|
| 1110 |
+
"""
|
| 1111 |
+
# For consistency with the cpp version, ignore empty values.
|
| 1112 |
+
if not filename:
|
| 1113 |
+
return []
|
| 1114 |
+
if parsed_file_stack is None:
|
| 1115 |
+
parsed_file_stack = []
|
| 1116 |
+
# We do a little safety check for reparsing a file we've already encountered
|
| 1117 |
+
# at a previous depth.
|
| 1118 |
+
if filename in parsed_file_stack:
|
| 1119 |
+
sys.stderr.write('Warning: Hit circular flagfile dependency. Ignoring'
|
| 1120 |
+
' flagfile: %s\n' % (filename,))
|
| 1121 |
+
return []
|
| 1122 |
+
else:
|
| 1123 |
+
parsed_file_stack.append(filename)
|
| 1124 |
+
|
| 1125 |
+
line_list = [] # All line from flagfile.
|
| 1126 |
+
flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags.
|
| 1127 |
+
try:
|
| 1128 |
+
file_obj = open(filename)
|
| 1129 |
+
except OSError as e_msg:
|
| 1130 |
+
raise _exceptions.CantOpenFlagFileError(
|
| 1131 |
+
'ERROR:: Unable to open flagfile: %s' % e_msg)
|
| 1132 |
+
|
| 1133 |
+
with file_obj:
|
| 1134 |
+
line_list = file_obj.readlines()
|
| 1135 |
+
|
| 1136 |
+
# This is where we check each line in the file we just read.
|
| 1137 |
+
for line in line_list:
|
| 1138 |
+
if line.isspace():
|
| 1139 |
+
pass
|
| 1140 |
+
# Checks for comment (a line that starts with '#').
|
| 1141 |
+
elif line.startswith('#') or line.startswith('//'):
|
| 1142 |
+
pass
|
| 1143 |
+
# Checks for a nested "--flagfile=<bar>" flag in the current file.
|
| 1144 |
+
# If we find one, recursively parse down into that file.
|
| 1145 |
+
elif self._is_flag_file_directive(line):
|
| 1146 |
+
sub_filename = self._extract_filename(line)
|
| 1147 |
+
included_flags = self._get_flag_file_lines(
|
| 1148 |
+
sub_filename, parsed_file_stack=parsed_file_stack)
|
| 1149 |
+
flag_line_list.extend(included_flags)
|
| 1150 |
+
else:
|
| 1151 |
+
# Any line that's not a comment or a nested flagfile should get
|
| 1152 |
+
# copied into 2nd position. This leaves earlier arguments
|
| 1153 |
+
# further back in the list, thus giving them higher priority.
|
| 1154 |
+
flag_line_list.append(line.strip())
|
| 1155 |
+
|
| 1156 |
+
parsed_file_stack.pop()
|
| 1157 |
+
return flag_line_list
|
| 1158 |
+
|
| 1159 |
+
def read_flags_from_files(
|
| 1160 |
+
self, argv: Sequence[str], force_gnu: bool = True
|
| 1161 |
+
) -> List[str]:
|
| 1162 |
+
"""Processes command line args, but also allow args to be read from file.
|
| 1163 |
+
|
| 1164 |
+
Args:
|
| 1165 |
+
argv: [str], a list of strings, usually sys.argv[1:], which may contain
|
| 1166 |
+
one or more flagfile directives of the form --flagfile="./filename".
|
| 1167 |
+
Note that the name of the program (sys.argv[0]) should be omitted.
|
| 1168 |
+
force_gnu: bool, if False, --flagfile parsing obeys the
|
| 1169 |
+
FLAGS.is_gnu_getopt() value. If True, ignore the value and always follow
|
| 1170 |
+
gnu_getopt semantics.
|
| 1171 |
+
|
| 1172 |
+
Returns:
|
| 1173 |
+
A new list which has the original list combined with what we read
|
| 1174 |
+
from any flagfile(s).
|
| 1175 |
+
|
| 1176 |
+
Raises:
|
| 1177 |
+
IllegalFlagValueError: Raised when --flagfile is provided with no
|
| 1178 |
+
argument.
|
| 1179 |
+
|
| 1180 |
+
This function is called by FLAGS(argv).
|
| 1181 |
+
It scans the input list for a flag that looks like:
|
| 1182 |
+
--flagfile=<somefile>. Then it opens <somefile>, reads all valid key
|
| 1183 |
+
and value pairs and inserts them into the input list in exactly the
|
| 1184 |
+
place where the --flagfile arg is found.
|
| 1185 |
+
|
| 1186 |
+
Note that your application's flags are still defined the usual way
|
| 1187 |
+
using absl.flags DEFINE_flag() type functions.
|
| 1188 |
+
|
| 1189 |
+
Notes (assuming we're getting a commandline of some sort as our input):
|
| 1190 |
+
|
| 1191 |
+
* For duplicate flags, the last one we hit should "win".
|
| 1192 |
+
* Since flags that appear later win, a flagfile's settings can be "weak"
|
| 1193 |
+
if the --flagfile comes at the beginning of the argument sequence,
|
| 1194 |
+
and it can be "strong" if the --flagfile comes at the end.
|
| 1195 |
+
* A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
|
| 1196 |
+
It will be expanded in exactly the spot where it is found.
|
| 1197 |
+
* In a flagfile, a line beginning with # or // is a comment.
|
| 1198 |
+
* Entirely blank lines _should_ be ignored.
|
| 1199 |
+
"""
|
| 1200 |
+
rest_of_args = argv
|
| 1201 |
+
new_argv = []
|
| 1202 |
+
while rest_of_args:
|
| 1203 |
+
current_arg = rest_of_args[0]
|
| 1204 |
+
rest_of_args = rest_of_args[1:]
|
| 1205 |
+
if self._is_flag_file_directive(current_arg):
|
| 1206 |
+
# This handles the case of -(-)flagfile foo. In this case the
|
| 1207 |
+
# next arg really is part of this one.
|
| 1208 |
+
if current_arg == '--flagfile' or current_arg == '-flagfile':
|
| 1209 |
+
if not rest_of_args:
|
| 1210 |
+
raise _exceptions.IllegalFlagValueError(
|
| 1211 |
+
'--flagfile with no argument')
|
| 1212 |
+
flag_filename = os.path.expanduser(rest_of_args[0])
|
| 1213 |
+
rest_of_args = rest_of_args[1:]
|
| 1214 |
+
else:
|
| 1215 |
+
# This handles the case of (-)-flagfile=foo.
|
| 1216 |
+
flag_filename = self._extract_filename(current_arg)
|
| 1217 |
+
new_argv.extend(self._get_flag_file_lines(flag_filename))
|
| 1218 |
+
else:
|
| 1219 |
+
new_argv.append(current_arg)
|
| 1220 |
+
# Stop parsing after '--', like getopt and gnu_getopt.
|
| 1221 |
+
if current_arg == '--':
|
| 1222 |
+
break
|
| 1223 |
+
# Stop parsing after a non-flag, like getopt.
|
| 1224 |
+
if not current_arg.startswith('-'):
|
| 1225 |
+
if not force_gnu and not self.__dict__['__use_gnu_getopt']:
|
| 1226 |
+
break
|
| 1227 |
+
else:
|
| 1228 |
+
if ('=' not in current_arg and rest_of_args and
|
| 1229 |
+
not rest_of_args[0].startswith('-')):
|
| 1230 |
+
# If this is an occurrence of a legitimate --x y, skip the value
|
| 1231 |
+
# so that it won't be mistaken for a standalone arg.
|
| 1232 |
+
fl = self._flags()
|
| 1233 |
+
name = current_arg.lstrip('-')
|
| 1234 |
+
if name in fl and not fl[name].boolean:
|
| 1235 |
+
current_arg = rest_of_args[0]
|
| 1236 |
+
rest_of_args = rest_of_args[1:]
|
| 1237 |
+
new_argv.append(current_arg)
|
| 1238 |
+
|
| 1239 |
+
if rest_of_args:
|
| 1240 |
+
new_argv.extend(rest_of_args)
|
| 1241 |
+
|
| 1242 |
+
return new_argv
|
| 1243 |
+
|
| 1244 |
+
def flags_into_string(self) -> str:
|
| 1245 |
+
"""Returns a string with the flags assignments from this FlagValues object.
|
| 1246 |
+
|
| 1247 |
+
This function ignores flags whose value is None. Each flag
|
| 1248 |
+
assignment is separated by a newline.
|
| 1249 |
+
|
| 1250 |
+
NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString
|
| 1251 |
+
from https://github.com/gflags/gflags.
|
| 1252 |
+
|
| 1253 |
+
Returns:
|
| 1254 |
+
str, the string with the flags assignments from this FlagValues object.
|
| 1255 |
+
The flags are ordered by (module_name, flag_name).
|
| 1256 |
+
"""
|
| 1257 |
+
module_flags = sorted(self.flags_by_module_dict().items())
|
| 1258 |
+
s = ''
|
| 1259 |
+
for unused_module_name, flags in module_flags:
|
| 1260 |
+
flags = sorted(flags, key=lambda f: f.name)
|
| 1261 |
+
for flag in flags:
|
| 1262 |
+
if flag.value is not None:
|
| 1263 |
+
s += flag.serialize() + '\n'
|
| 1264 |
+
return s
|
| 1265 |
+
|
| 1266 |
+
def append_flags_into_file(self, filename: str) -> None:
|
| 1267 |
+
"""Appends all flags assignments from this FlagInfo object to a file.
|
| 1268 |
+
|
| 1269 |
+
Output will be in the format of a flagfile.
|
| 1270 |
+
|
| 1271 |
+
NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile
|
| 1272 |
+
from https://github.com/gflags/gflags.
|
| 1273 |
+
|
| 1274 |
+
Args:
|
| 1275 |
+
filename: str, name of the file.
|
| 1276 |
+
"""
|
| 1277 |
+
with open(filename, 'a') as out_file:
|
| 1278 |
+
out_file.write(self.flags_into_string())
|
| 1279 |
+
|
| 1280 |
+
def write_help_in_xml_format(self, outfile: Optional[TextIO] = None) -> None:
|
| 1281 |
+
"""Outputs flag documentation in XML format.
|
| 1282 |
+
|
| 1283 |
+
NOTE: We use element names that are consistent with those used by
|
| 1284 |
+
the C++ command-line flag library, from
|
| 1285 |
+
https://github.com/gflags/gflags.
|
| 1286 |
+
We also use a few new elements (e.g., <key>), but we do not
|
| 1287 |
+
interfere / overlap with existing XML elements used by the C++
|
| 1288 |
+
library. Please maintain this consistency.
|
| 1289 |
+
|
| 1290 |
+
Args:
|
| 1291 |
+
outfile: File object we write to. Default None means sys.stdout.
|
| 1292 |
+
"""
|
| 1293 |
+
doc = minidom.Document()
|
| 1294 |
+
all_flag = doc.createElement('AllFlags')
|
| 1295 |
+
doc.appendChild(all_flag)
|
| 1296 |
+
|
| 1297 |
+
all_flag.appendChild(
|
| 1298 |
+
_helpers.create_xml_dom_element(doc, 'program',
|
| 1299 |
+
os.path.basename(sys.argv[0])))
|
| 1300 |
+
|
| 1301 |
+
usage_doc = sys.modules['__main__'].__doc__
|
| 1302 |
+
if not usage_doc:
|
| 1303 |
+
usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
|
| 1304 |
+
else:
|
| 1305 |
+
usage_doc = usage_doc.replace('%s', sys.argv[0])
|
| 1306 |
+
all_flag.appendChild(
|
| 1307 |
+
_helpers.create_xml_dom_element(doc, 'usage', usage_doc))
|
| 1308 |
+
|
| 1309 |
+
# Get list of key flags for the main module.
|
| 1310 |
+
key_flags = self.get_key_flags_for_module(sys.argv[0])
|
| 1311 |
+
|
| 1312 |
+
flags_by_module = self.flags_by_module_dict()
|
| 1313 |
+
# Sort flags by declaring module name and next by flag name.
|
| 1314 |
+
for module_name in sorted(flags_by_module.keys()):
|
| 1315 |
+
flag_list = [(f.name, f) for f in flags_by_module[module_name]]
|
| 1316 |
+
flag_list.sort()
|
| 1317 |
+
for unused_flag_name, flag in flag_list:
|
| 1318 |
+
is_key = flag in key_flags
|
| 1319 |
+
all_flag.appendChild(
|
| 1320 |
+
flag._create_xml_dom_element( # pylint: disable=protected-access
|
| 1321 |
+
doc,
|
| 1322 |
+
module_name,
|
| 1323 |
+
is_key=is_key))
|
| 1324 |
+
|
| 1325 |
+
outfile = outfile or sys.stdout
|
| 1326 |
+
outfile.write(
|
| 1327 |
+
doc.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8'))
|
| 1328 |
+
outfile.flush()
|
| 1329 |
+
|
| 1330 |
+
def _check_method_name_conflicts(self, name: str, flag: Flag):
|
| 1331 |
+
if flag.allow_using_method_names:
|
| 1332 |
+
return
|
| 1333 |
+
short_name = flag.short_name
|
| 1334 |
+
flag_names = {name} if short_name is None else {name, short_name}
|
| 1335 |
+
for flag_name in flag_names:
|
| 1336 |
+
if flag_name in self.__dict__['__banned_flag_names']:
|
| 1337 |
+
raise _exceptions.FlagNameConflictsWithMethodError(
|
| 1338 |
+
'Cannot define a flag named "{name}". It conflicts with a method '
|
| 1339 |
+
'on class "{class_name}". To allow defining it, use '
|
| 1340 |
+
'allow_using_method_names and access the flag value with '
|
| 1341 |
+
"FLAGS['{name}'].value. FLAGS.{name} returns the method, "
|
| 1342 |
+
'not the flag value.'.format(
|
| 1343 |
+
name=flag_name, class_name=type(self).__name__))
|
| 1344 |
+
|
| 1345 |
+
|
| 1346 |
+
FLAGS = FlagValues()
|
| 1347 |
+
|
| 1348 |
+
|
| 1349 |
+
class FlagHolder(Generic[_T_co]):
|
| 1350 |
+
"""Holds a defined flag.
|
| 1351 |
+
|
| 1352 |
+
This facilitates a cleaner api around global state. Instead of::
|
| 1353 |
+
|
| 1354 |
+
flags.DEFINE_integer('foo', ...)
|
| 1355 |
+
flags.DEFINE_integer('bar', ...)
|
| 1356 |
+
|
| 1357 |
+
def method():
|
| 1358 |
+
# prints parsed value of 'bar' flag
|
| 1359 |
+
print(flags.FLAGS.foo)
|
| 1360 |
+
# runtime error due to typo or possibly bad coding style.
|
| 1361 |
+
print(flags.FLAGS.baz)
|
| 1362 |
+
|
| 1363 |
+
it encourages code like::
|
| 1364 |
+
|
| 1365 |
+
_FOO_FLAG = flags.DEFINE_integer('foo', ...)
|
| 1366 |
+
_BAR_FLAG = flags.DEFINE_integer('bar', ...)
|
| 1367 |
+
|
| 1368 |
+
def method():
|
| 1369 |
+
print(_FOO_FLAG.value)
|
| 1370 |
+
print(_BAR_FLAG.value)
|
| 1371 |
+
|
| 1372 |
+
since the name of the flag appears only once in the source code.
|
| 1373 |
+
"""
|
| 1374 |
+
|
| 1375 |
+
value: _T_co
|
| 1376 |
+
|
| 1377 |
+
def __init__(
|
| 1378 |
+
self,
|
| 1379 |
+
flag_values: FlagValues,
|
| 1380 |
+
flag: Flag[_T_co],
|
| 1381 |
+
ensure_non_none_value: bool = False,
|
| 1382 |
+
):
|
| 1383 |
+
"""Constructs a FlagHolder instance providing typesafe access to flag.
|
| 1384 |
+
|
| 1385 |
+
Args:
|
| 1386 |
+
flag_values: The container the flag is registered to.
|
| 1387 |
+
flag: The flag object for this flag.
|
| 1388 |
+
ensure_non_none_value: Is the value of the flag allowed to be None.
|
| 1389 |
+
"""
|
| 1390 |
+
self._flagvalues = flag_values
|
| 1391 |
+
# We take the entire flag object, but only keep the name. Why?
|
| 1392 |
+
# - We want FlagHolder[T] to be generic container
|
| 1393 |
+
# - flag_values contains all flags, so has no reference to T.
|
| 1394 |
+
# - typecheckers don't like to see a generic class where none of the ctor
|
| 1395 |
+
# arguments refer to the generic type.
|
| 1396 |
+
self._name = flag.name
|
| 1397 |
+
# We intentionally do NOT check if the default value is None.
|
| 1398 |
+
# This allows future use of this for "required flags with None default"
|
| 1399 |
+
self._ensure_non_none_value = ensure_non_none_value
|
| 1400 |
+
|
| 1401 |
+
def __eq__(self, other):
|
| 1402 |
+
raise TypeError(
|
| 1403 |
+
"unsupported operand type(s) for ==: '{0}' and '{1}' "
|
| 1404 |
+
"(did you mean to use '{0}.value' instead?)".format(
|
| 1405 |
+
type(self).__name__, type(other).__name__))
|
| 1406 |
+
|
| 1407 |
+
def __bool__(self):
|
| 1408 |
+
raise TypeError(
|
| 1409 |
+
"bool() not supported for instances of type '{0}' "
|
| 1410 |
+
"(did you mean to use '{0}.value' instead?)".format(
|
| 1411 |
+
type(self).__name__))
|
| 1412 |
+
|
| 1413 |
+
__nonzero__ = __bool__
|
| 1414 |
+
|
| 1415 |
+
@property
|
| 1416 |
+
def name(self) -> str:
|
| 1417 |
+
return self._name
|
| 1418 |
+
|
| 1419 |
+
@property # type: ignore[no-redef]
|
| 1420 |
+
def value(self) -> _T_co:
|
| 1421 |
+
"""Returns the value of the flag.
|
| 1422 |
+
|
| 1423 |
+
If ``_ensure_non_none_value`` is ``True``, then return value is not
|
| 1424 |
+
``None``.
|
| 1425 |
+
|
| 1426 |
+
Raises:
|
| 1427 |
+
UnparsedFlagAccessError: if flag parsing has not finished.
|
| 1428 |
+
IllegalFlagValueError: if value is None unexpectedly.
|
| 1429 |
+
"""
|
| 1430 |
+
val = getattr(self._flagvalues, self._name)
|
| 1431 |
+
if self._ensure_non_none_value and val is None:
|
| 1432 |
+
raise _exceptions.IllegalFlagValueError(
|
| 1433 |
+
'Unexpected None value for flag %s' % self._name)
|
| 1434 |
+
return val
|
| 1435 |
+
|
| 1436 |
+
@property
|
| 1437 |
+
def default(self) -> _T_co:
|
| 1438 |
+
"""Returns the default value of the flag."""
|
| 1439 |
+
return self._flagvalues[self._name].default # type: ignore[return-value]
|
| 1440 |
+
|
| 1441 |
+
@property
|
| 1442 |
+
def present(self) -> bool:
|
| 1443 |
+
"""Returns True if the flag was parsed from command-line flags."""
|
| 1444 |
+
return bool(self._flagvalues[self._name].present)
|
| 1445 |
+
|
| 1446 |
+
def serialize(self) -> str:
|
| 1447 |
+
"""Returns a serialized representation of the flag."""
|
| 1448 |
+
return self._flagvalues[self._name].serialize()
|
| 1449 |
+
|
| 1450 |
+
|
| 1451 |
+
def resolve_flag_ref(
|
| 1452 |
+
flag_ref: Union[str, FlagHolder], flag_values: FlagValues
|
| 1453 |
+
) -> Tuple[str, FlagValues]:
|
| 1454 |
+
"""Helper to validate and resolve a flag reference argument."""
|
| 1455 |
+
if isinstance(flag_ref, FlagHolder):
|
| 1456 |
+
new_flag_values = flag_ref._flagvalues # pylint: disable=protected-access
|
| 1457 |
+
if flag_values != FLAGS and flag_values != new_flag_values:
|
| 1458 |
+
raise ValueError(
|
| 1459 |
+
'flag_values must not be customized when operating on a FlagHolder')
|
| 1460 |
+
return flag_ref.name, new_flag_values
|
| 1461 |
+
return flag_ref, flag_values
|
| 1462 |
+
|
| 1463 |
+
|
| 1464 |
+
def resolve_flag_refs(
|
| 1465 |
+
flag_refs: Sequence[Union[str, FlagHolder]], flag_values: FlagValues
|
| 1466 |
+
) -> Tuple[List[str], FlagValues]:
|
| 1467 |
+
"""Helper to validate and resolve flag reference list arguments."""
|
| 1468 |
+
fv = None
|
| 1469 |
+
names = []
|
| 1470 |
+
for ref in flag_refs:
|
| 1471 |
+
if isinstance(ref, FlagHolder):
|
| 1472 |
+
newfv = ref._flagvalues # pylint: disable=protected-access
|
| 1473 |
+
name = ref.name
|
| 1474 |
+
else:
|
| 1475 |
+
newfv = flag_values
|
| 1476 |
+
name = ref
|
| 1477 |
+
if fv and fv != newfv:
|
| 1478 |
+
raise ValueError(
|
| 1479 |
+
'multiple FlagValues instances used in invocation. '
|
| 1480 |
+
'FlagHolders must be registered to the same FlagValues instance as '
|
| 1481 |
+
'do flag names, if provided.')
|
| 1482 |
+
fv = newfv
|
| 1483 |
+
names.append(name)
|
| 1484 |
+
if fv is None:
|
| 1485 |
+
raise ValueError('flag_refs argument must not be empty')
|
| 1486 |
+
return names, fv
|
lib/python3.10/site-packages/absl/flags/_helpers.py
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Internal helper functions for Abseil Python flags library."""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
import re
|
| 19 |
+
import struct
|
| 20 |
+
import sys
|
| 21 |
+
import textwrap
|
| 22 |
+
import types
|
| 23 |
+
from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Sequence, Set
|
| 24 |
+
from xml.dom import minidom
|
| 25 |
+
# pylint: disable=g-import-not-at-top
|
| 26 |
+
fcntl: Optional[types.ModuleType]
|
| 27 |
+
try:
|
| 28 |
+
import fcntl
|
| 29 |
+
except ImportError:
|
| 30 |
+
fcntl = None
|
| 31 |
+
termios: Optional[types.ModuleType]
|
| 32 |
+
try:
|
| 33 |
+
# Importing termios will fail on non-unix platforms.
|
| 34 |
+
import termios
|
| 35 |
+
except ImportError:
|
| 36 |
+
termios = None
|
| 37 |
+
# pylint: enable=g-import-not-at-top
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
_DEFAULT_HELP_WIDTH = 80 # Default width of help output.
|
| 41 |
+
# Minimal "sane" width of help output. We assume that any value below 40 is
|
| 42 |
+
# unreasonable.
|
| 43 |
+
_MIN_HELP_WIDTH = 40
|
| 44 |
+
|
| 45 |
+
# Define the allowed error rate in an input string to get suggestions.
|
| 46 |
+
#
|
| 47 |
+
# We lean towards a high threshold because we tend to be matching a phrase,
|
| 48 |
+
# and the simple algorithm used here is geared towards correcting word
|
| 49 |
+
# spellings.
|
| 50 |
+
#
|
| 51 |
+
# For manual testing, consider "<command> --list" which produced a large number
|
| 52 |
+
# of spurious suggestions when we used "least_errors > 0.5" instead of
|
| 53 |
+
# "least_erros >= 0.5".
|
| 54 |
+
_SUGGESTION_ERROR_RATE_THRESHOLD = 0.50
|
| 55 |
+
|
| 56 |
+
# Characters that cannot appear or are highly discouraged in an XML 1.0
|
| 57 |
+
# document. (See http://www.w3.org/TR/REC-xml/#charsets or
|
| 58 |
+
# https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0)
|
| 59 |
+
_ILLEGAL_XML_CHARS_REGEX = re.compile(
|
| 60 |
+
'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]'
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# This is a set of module ids for the modules that disclaim key flags.
|
| 64 |
+
# This module is explicitly added to this set so that we never consider it to
|
| 65 |
+
# define key flag.
|
| 66 |
+
disclaim_module_ids: Set[int] = {id(sys.modules[__name__])}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
# Define special flags here so that help may be generated for them.
|
| 70 |
+
# NOTE: Please do NOT use SPECIAL_FLAGS from outside flags module.
|
| 71 |
+
# Initialized inside flagvalues.py.
|
| 72 |
+
# NOTE: This cannot be annotated as its actual FlagValues type since this would
|
| 73 |
+
# create a circular dependency.
|
| 74 |
+
SPECIAL_FLAGS: Any = None
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# This points to the flags module, initialized in flags/__init__.py.
|
| 78 |
+
# This should only be used in adopt_module_key_flags to take SPECIAL_FLAGS into
|
| 79 |
+
# account.
|
| 80 |
+
FLAGS_MODULE: Optional[types.ModuleType] = None
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class _ModuleObjectAndName(NamedTuple):
|
| 84 |
+
"""Module object and name.
|
| 85 |
+
|
| 86 |
+
Fields:
|
| 87 |
+
- module: object, module object.
|
| 88 |
+
- module_name: str, module name.
|
| 89 |
+
"""
|
| 90 |
+
module: types.ModuleType
|
| 91 |
+
module_name: str
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def get_module_object_and_name(
|
| 95 |
+
globals_dict: Dict[str, Any],
|
| 96 |
+
) -> Optional[_ModuleObjectAndName]:
|
| 97 |
+
"""Returns the module that defines a global environment, and its name.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
globals_dict: A dictionary that should correspond to an environment
|
| 101 |
+
providing the values of the globals.
|
| 102 |
+
|
| 103 |
+
Returns:
|
| 104 |
+
_ModuleObjectAndName - pair of module object & module name.
|
| 105 |
+
Returns None if the module could not be identified.
|
| 106 |
+
"""
|
| 107 |
+
try:
|
| 108 |
+
name = globals_dict['__name__']
|
| 109 |
+
module = sys.modules[name]
|
| 110 |
+
except KeyError:
|
| 111 |
+
return None
|
| 112 |
+
# Pick a more informative name for the main module.
|
| 113 |
+
return _ModuleObjectAndName(module,
|
| 114 |
+
(sys.argv[0] if name == '__main__' else name))
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def get_calling_module_object_and_name() -> _ModuleObjectAndName:
|
| 118 |
+
"""Returns the module that's calling into this module.
|
| 119 |
+
|
| 120 |
+
We generally use this function to get the name of the module calling a
|
| 121 |
+
DEFINE_foo... function.
|
| 122 |
+
|
| 123 |
+
Returns:
|
| 124 |
+
The module object that called into this one.
|
| 125 |
+
|
| 126 |
+
Raises:
|
| 127 |
+
AssertionError: Raised when no calling module could be identified.
|
| 128 |
+
"""
|
| 129 |
+
for depth in range(1, sys.getrecursionlimit()):
|
| 130 |
+
# sys._getframe is the right thing to use here, as it's the best
|
| 131 |
+
# way to walk up the call stack.
|
| 132 |
+
globals_for_frame = sys._getframe(depth).f_globals # pylint: disable=protected-access
|
| 133 |
+
module = get_module_object_and_name(globals_for_frame)
|
| 134 |
+
if module is not None and id(module.module) not in disclaim_module_ids:
|
| 135 |
+
return module
|
| 136 |
+
raise AssertionError('No module was found')
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def get_calling_module() -> str:
|
| 140 |
+
"""Returns the name of the module that's calling into this module."""
|
| 141 |
+
return get_calling_module_object_and_name().module_name
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def create_xml_dom_element(
|
| 145 |
+
doc: minidom.Document, name: str, value: Any
|
| 146 |
+
) -> minidom.Element:
|
| 147 |
+
"""Returns an XML DOM element with name and text value.
|
| 148 |
+
|
| 149 |
+
Args:
|
| 150 |
+
doc: minidom.Document, the DOM document it should create nodes from.
|
| 151 |
+
name: str, the tag of XML element.
|
| 152 |
+
value: object, whose string representation will be used
|
| 153 |
+
as the value of the XML element. Illegal or highly discouraged xml 1.0
|
| 154 |
+
characters are stripped.
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
An instance of minidom.Element.
|
| 158 |
+
"""
|
| 159 |
+
s = str(value)
|
| 160 |
+
if isinstance(value, bool):
|
| 161 |
+
# Display boolean values as the C++ flag library does: no caps.
|
| 162 |
+
s = s.lower()
|
| 163 |
+
# Remove illegal xml characters.
|
| 164 |
+
s = _ILLEGAL_XML_CHARS_REGEX.sub('', s)
|
| 165 |
+
|
| 166 |
+
e = doc.createElement(name)
|
| 167 |
+
e.appendChild(doc.createTextNode(s))
|
| 168 |
+
return e
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def get_help_width() -> int:
|
| 172 |
+
"""Returns the integer width of help lines that is used in TextWrap."""
|
| 173 |
+
if not sys.stdout.isatty() or termios is None or fcntl is None:
|
| 174 |
+
return _DEFAULT_HELP_WIDTH
|
| 175 |
+
try:
|
| 176 |
+
data = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, b'1234')
|
| 177 |
+
columns = struct.unpack('hh', data)[1]
|
| 178 |
+
# Emacs mode returns 0.
|
| 179 |
+
# Here we assume that any value below 40 is unreasonable.
|
| 180 |
+
if columns >= _MIN_HELP_WIDTH:
|
| 181 |
+
return columns
|
| 182 |
+
# Returning an int as default is fine, int(int) just return the int.
|
| 183 |
+
return int(os.getenv('COLUMNS', _DEFAULT_HELP_WIDTH))
|
| 184 |
+
|
| 185 |
+
except (TypeError, OSError, struct.error):
|
| 186 |
+
return _DEFAULT_HELP_WIDTH
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def get_flag_suggestions(
|
| 190 |
+
attempt: str, longopt_list: Sequence[str]
|
| 191 |
+
) -> List[str]:
|
| 192 |
+
"""Returns helpful similar matches for an invalid flag."""
|
| 193 |
+
# Don't suggest on very short strings, or if no longopts are specified.
|
| 194 |
+
if len(attempt) <= 2 or not longopt_list:
|
| 195 |
+
return []
|
| 196 |
+
|
| 197 |
+
option_names = [v.split('=')[0] for v in longopt_list]
|
| 198 |
+
|
| 199 |
+
# Find close approximations in flag prefixes.
|
| 200 |
+
# This also handles the case where the flag is spelled right but ambiguous.
|
| 201 |
+
distances = [(_damerau_levenshtein(attempt, option[0:len(attempt)]), option)
|
| 202 |
+
for option in option_names]
|
| 203 |
+
# t[0] is distance, and sorting by t[1] allows us to have stable output.
|
| 204 |
+
distances.sort()
|
| 205 |
+
|
| 206 |
+
least_errors, _ = distances[0]
|
| 207 |
+
# Don't suggest excessively bad matches.
|
| 208 |
+
if least_errors >= _SUGGESTION_ERROR_RATE_THRESHOLD * len(attempt):
|
| 209 |
+
return []
|
| 210 |
+
|
| 211 |
+
suggestions = []
|
| 212 |
+
for errors, name in distances:
|
| 213 |
+
if errors == least_errors:
|
| 214 |
+
suggestions.append(name)
|
| 215 |
+
else:
|
| 216 |
+
break
|
| 217 |
+
return suggestions
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def _damerau_levenshtein(a, b):
|
| 221 |
+
"""Returns Damerau-Levenshtein edit distance from a to b."""
|
| 222 |
+
memo = {}
|
| 223 |
+
|
| 224 |
+
def distance(x, y):
|
| 225 |
+
"""Recursively defined string distance with memoization."""
|
| 226 |
+
if (x, y) in memo:
|
| 227 |
+
return memo[x, y]
|
| 228 |
+
if not x:
|
| 229 |
+
d = len(y)
|
| 230 |
+
elif not y:
|
| 231 |
+
d = len(x)
|
| 232 |
+
else:
|
| 233 |
+
d = min(
|
| 234 |
+
distance(x[1:], y) + 1, # correct an insertion error
|
| 235 |
+
distance(x, y[1:]) + 1, # correct a deletion error
|
| 236 |
+
distance(x[1:], y[1:]) + (x[0] != y[0])) # correct a wrong character
|
| 237 |
+
if len(x) >= 2 and len(y) >= 2 and x[0] == y[1] and x[1] == y[0]:
|
| 238 |
+
# Correct a transposition.
|
| 239 |
+
t = distance(x[2:], y[2:]) + 1
|
| 240 |
+
if d > t:
|
| 241 |
+
d = t
|
| 242 |
+
|
| 243 |
+
memo[x, y] = d
|
| 244 |
+
return d
|
| 245 |
+
return distance(a, b)
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def text_wrap(
|
| 249 |
+
text: str,
|
| 250 |
+
length: Optional[int] = None,
|
| 251 |
+
indent: str = '',
|
| 252 |
+
firstline_indent: Optional[str] = None,
|
| 253 |
+
) -> str:
|
| 254 |
+
"""Wraps a given text to a maximum line length and returns it.
|
| 255 |
+
|
| 256 |
+
It turns lines that only contain whitespace into empty lines, keeps new lines,
|
| 257 |
+
and expands tabs using 4 spaces.
|
| 258 |
+
|
| 259 |
+
Args:
|
| 260 |
+
text: str, text to wrap.
|
| 261 |
+
length: int, maximum length of a line, includes indentation.
|
| 262 |
+
If this is None then use get_help_width()
|
| 263 |
+
indent: str, indent for all but first line.
|
| 264 |
+
firstline_indent: str, indent for first line; if None, fall back to indent.
|
| 265 |
+
|
| 266 |
+
Returns:
|
| 267 |
+
str, the wrapped text.
|
| 268 |
+
|
| 269 |
+
Raises:
|
| 270 |
+
ValueError: Raised if indent or firstline_indent not shorter than length.
|
| 271 |
+
"""
|
| 272 |
+
# Get defaults where callee used None
|
| 273 |
+
if length is None:
|
| 274 |
+
length = get_help_width()
|
| 275 |
+
if indent is None:
|
| 276 |
+
indent = ''
|
| 277 |
+
if firstline_indent is None:
|
| 278 |
+
firstline_indent = indent
|
| 279 |
+
|
| 280 |
+
if len(indent) >= length:
|
| 281 |
+
raise ValueError('Length of indent exceeds length')
|
| 282 |
+
if len(firstline_indent) >= length:
|
| 283 |
+
raise ValueError('Length of first line indent exceeds length')
|
| 284 |
+
|
| 285 |
+
text = text.expandtabs(4)
|
| 286 |
+
|
| 287 |
+
result = []
|
| 288 |
+
# Create one wrapper for the first paragraph and one for subsequent
|
| 289 |
+
# paragraphs that does not have the initial wrapping.
|
| 290 |
+
wrapper = textwrap.TextWrapper(
|
| 291 |
+
width=length, initial_indent=firstline_indent, subsequent_indent=indent)
|
| 292 |
+
subsequent_wrapper = textwrap.TextWrapper(
|
| 293 |
+
width=length, initial_indent=indent, subsequent_indent=indent)
|
| 294 |
+
|
| 295 |
+
# textwrap does not have any special treatment for newlines. From the docs:
|
| 296 |
+
# "...newlines may appear in the middle of a line and cause strange output.
|
| 297 |
+
# For this reason, text should be split into paragraphs (using
|
| 298 |
+
# str.splitlines() or similar) which are wrapped separately."
|
| 299 |
+
for paragraph in (p.strip() for p in text.splitlines()):
|
| 300 |
+
if paragraph:
|
| 301 |
+
result.extend(wrapper.wrap(paragraph))
|
| 302 |
+
else:
|
| 303 |
+
result.append('') # Keep empty lines.
|
| 304 |
+
# Replace initial wrapper with wrapper for subsequent paragraphs.
|
| 305 |
+
wrapper = subsequent_wrapper
|
| 306 |
+
|
| 307 |
+
return '\n'.join(result)
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def flag_dict_to_args(
|
| 311 |
+
flag_map: Dict[str, Any], multi_flags: Optional[Set[str]] = None
|
| 312 |
+
) -> Iterable[str]:
|
| 313 |
+
"""Convert a dict of values into process call parameters.
|
| 314 |
+
|
| 315 |
+
This method is used to convert a dictionary into a sequence of parameters
|
| 316 |
+
for a binary that parses arguments using this module.
|
| 317 |
+
|
| 318 |
+
Args:
|
| 319 |
+
flag_map: dict, a mapping where the keys are flag names (strings).
|
| 320 |
+
values are treated according to their type:
|
| 321 |
+
|
| 322 |
+
* If value is ``None``, then only the name is emitted.
|
| 323 |
+
* If value is ``True``, then only the name is emitted.
|
| 324 |
+
* If value is ``False``, then only the name prepended with 'no' is
|
| 325 |
+
emitted.
|
| 326 |
+
* If value is a string then ``--name=value`` is emitted.
|
| 327 |
+
* If value is a collection, this will emit
|
| 328 |
+
``--name=value1,value2,value3``, unless the flag name is in
|
| 329 |
+
``multi_flags``, in which case this will emit
|
| 330 |
+
``--name=value1 --name=value2 --name=value3``.
|
| 331 |
+
* Everything else is converted to string an passed as such.
|
| 332 |
+
|
| 333 |
+
multi_flags: set, names (strings) of flags that should be treated as
|
| 334 |
+
multi-flags.
|
| 335 |
+
Yields:
|
| 336 |
+
sequence of string suitable for a subprocess execution.
|
| 337 |
+
"""
|
| 338 |
+
for key, value in flag_map.items():
|
| 339 |
+
if value is None:
|
| 340 |
+
yield '--%s' % key
|
| 341 |
+
elif isinstance(value, bool):
|
| 342 |
+
if value:
|
| 343 |
+
yield '--%s' % key
|
| 344 |
+
else:
|
| 345 |
+
yield '--no%s' % key
|
| 346 |
+
elif isinstance(value, (bytes, str)):
|
| 347 |
+
# We don't want strings to be handled like python collections.
|
| 348 |
+
yield '--%s=%s' % (key, value) # type: ignore[str-bytes-safe]
|
| 349 |
+
else:
|
| 350 |
+
# Now we attempt to deal with collections.
|
| 351 |
+
try:
|
| 352 |
+
if multi_flags and key in multi_flags:
|
| 353 |
+
for item in value:
|
| 354 |
+
yield '--%s=%s' % (key, str(item))
|
| 355 |
+
else:
|
| 356 |
+
yield '--%s=%s' % (key, ','.join(str(item) for item in value))
|
| 357 |
+
except TypeError:
|
| 358 |
+
# Default case.
|
| 359 |
+
yield '--%s=%s' % (key, value)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def trim_docstring(docstring: str) -> str:
|
| 363 |
+
"""Removes indentation from triple-quoted strings.
|
| 364 |
+
|
| 365 |
+
This is the function specified in PEP 257 to handle docstrings:
|
| 366 |
+
https://www.python.org/dev/peps/pep-0257/.
|
| 367 |
+
|
| 368 |
+
Args:
|
| 369 |
+
docstring: str, a python docstring.
|
| 370 |
+
|
| 371 |
+
Returns:
|
| 372 |
+
str, docstring with indentation removed.
|
| 373 |
+
"""
|
| 374 |
+
if not docstring:
|
| 375 |
+
return ''
|
| 376 |
+
|
| 377 |
+
# If you've got a line longer than this you have other problems...
|
| 378 |
+
max_indent = 1 << 29
|
| 379 |
+
|
| 380 |
+
# Convert tabs to spaces (following the normal Python rules)
|
| 381 |
+
# and split into a list of lines:
|
| 382 |
+
lines = docstring.expandtabs().splitlines()
|
| 383 |
+
|
| 384 |
+
# Determine minimum indentation (first line doesn't count):
|
| 385 |
+
indent = max_indent
|
| 386 |
+
for line in lines[1:]:
|
| 387 |
+
stripped = line.lstrip()
|
| 388 |
+
if stripped:
|
| 389 |
+
indent = min(indent, len(line) - len(stripped))
|
| 390 |
+
# Remove indentation (first line is special):
|
| 391 |
+
trimmed = [lines[0].strip()]
|
| 392 |
+
if indent < max_indent:
|
| 393 |
+
for line in lines[1:]:
|
| 394 |
+
trimmed.append(line[indent:].rstrip())
|
| 395 |
+
# Strip off trailing and leading blank lines:
|
| 396 |
+
while trimmed and not trimmed[-1]:
|
| 397 |
+
trimmed.pop()
|
| 398 |
+
while trimmed and not trimmed[0]:
|
| 399 |
+
trimmed.pop(0)
|
| 400 |
+
# Return a single string:
|
| 401 |
+
return '\n'.join(trimmed)
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def doc_to_help(doc: str) -> str:
|
| 405 |
+
"""Takes a __doc__ string and reformats it as help."""
|
| 406 |
+
|
| 407 |
+
# Get rid of starting and ending white space. Using lstrip() or even
|
| 408 |
+
# strip() could drop more than maximum of first line and right space
|
| 409 |
+
# of last line.
|
| 410 |
+
doc = doc.strip()
|
| 411 |
+
|
| 412 |
+
# Get rid of all empty lines.
|
| 413 |
+
whitespace_only_line = re.compile('^[ \t]+$', re.M)
|
| 414 |
+
doc = whitespace_only_line.sub('', doc)
|
| 415 |
+
|
| 416 |
+
# Cut out common space at line beginnings.
|
| 417 |
+
doc = trim_docstring(doc)
|
| 418 |
+
|
| 419 |
+
# Just like this module's comment, comments tend to be aligned somehow.
|
| 420 |
+
# In other words they all start with the same amount of white space.
|
| 421 |
+
# 1) keep double new lines;
|
| 422 |
+
# 2) keep ws after new lines if not empty line;
|
| 423 |
+
# 3) all other new lines shall be changed to a space;
|
| 424 |
+
# Solution: Match new lines between non white space and replace with space.
|
| 425 |
+
doc = re.sub(r'(?<=\S)\n(?=\S)', ' ', doc, flags=re.M)
|
| 426 |
+
|
| 427 |
+
return doc
|
lib/python3.10/site-packages/absl/flags/_validators.py
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Module to enforce different constraints on flags.
|
| 16 |
+
|
| 17 |
+
Flags validators can be registered using following functions / decorators::
|
| 18 |
+
|
| 19 |
+
flags.register_validator
|
| 20 |
+
@flags.validator
|
| 21 |
+
flags.register_multi_flags_validator
|
| 22 |
+
@flags.multi_flags_validator
|
| 23 |
+
|
| 24 |
+
Three convenience functions are also provided for common flag constraints::
|
| 25 |
+
|
| 26 |
+
flags.mark_flag_as_required
|
| 27 |
+
flags.mark_flags_as_required
|
| 28 |
+
flags.mark_flags_as_mutual_exclusive
|
| 29 |
+
flags.mark_bool_flags_as_mutual_exclusive
|
| 30 |
+
|
| 31 |
+
See their docstring in this module for a usage manual.
|
| 32 |
+
|
| 33 |
+
Do NOT import this module directly. Import the flags package and use the
|
| 34 |
+
aliases defined at the package level instead.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
import warnings
|
| 38 |
+
|
| 39 |
+
from absl.flags import _exceptions
|
| 40 |
+
from absl.flags import _flagvalues
|
| 41 |
+
from absl.flags import _validators_classes
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def register_validator(flag_name,
|
| 45 |
+
checker,
|
| 46 |
+
message='Flag validation failed',
|
| 47 |
+
flag_values=_flagvalues.FLAGS):
|
| 48 |
+
"""Adds a constraint, which will be enforced during program execution.
|
| 49 |
+
|
| 50 |
+
The constraint is validated when flags are initially parsed, and after each
|
| 51 |
+
change of the corresponding flag's value.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
flag_name: str | FlagHolder, name or holder of the flag to be checked.
|
| 55 |
+
Positional-only parameter.
|
| 56 |
+
checker: callable, a function to validate the flag.
|
| 57 |
+
|
| 58 |
+
* input - A single positional argument: The value of the corresponding
|
| 59 |
+
flag (string, boolean, etc. This value will be passed to checker
|
| 60 |
+
by the library).
|
| 61 |
+
* output - bool, True if validator constraint is satisfied.
|
| 62 |
+
If constraint is not satisfied, it should either ``return False`` or
|
| 63 |
+
``raise flags.ValidationError(desired_error_message)``.
|
| 64 |
+
|
| 65 |
+
message: str, error text to be shown to the user if checker returns False.
|
| 66 |
+
If checker raises flags.ValidationError, message from the raised
|
| 67 |
+
error will be shown.
|
| 68 |
+
flag_values: flags.FlagValues, optional FlagValues instance to validate
|
| 69 |
+
against.
|
| 70 |
+
|
| 71 |
+
Raises:
|
| 72 |
+
AttributeError: Raised when flag_name is not registered as a valid flag
|
| 73 |
+
name.
|
| 74 |
+
ValueError: Raised when flag_values is non-default and does not match the
|
| 75 |
+
FlagValues of the provided FlagHolder instance.
|
| 76 |
+
"""
|
| 77 |
+
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
|
| 78 |
+
v = _validators_classes.SingleFlagValidator(flag_name, checker, message)
|
| 79 |
+
_add_validator(flag_values, v)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def validator(flag_name, message='Flag validation failed',
|
| 83 |
+
flag_values=_flagvalues.FLAGS):
|
| 84 |
+
"""A function decorator for defining a flag validator.
|
| 85 |
+
|
| 86 |
+
Registers the decorated function as a validator for flag_name, e.g.::
|
| 87 |
+
|
| 88 |
+
@flags.validator('foo')
|
| 89 |
+
def _CheckFoo(foo):
|
| 90 |
+
...
|
| 91 |
+
|
| 92 |
+
See :func:`register_validator` for the specification of checker function.
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
flag_name: str | FlagHolder, name or holder of the flag to be checked.
|
| 96 |
+
Positional-only parameter.
|
| 97 |
+
message: str, error text to be shown to the user if checker returns False.
|
| 98 |
+
If checker raises flags.ValidationError, message from the raised
|
| 99 |
+
error will be shown.
|
| 100 |
+
flag_values: flags.FlagValues, optional FlagValues instance to validate
|
| 101 |
+
against.
|
| 102 |
+
Returns:
|
| 103 |
+
A function decorator that registers its function argument as a validator.
|
| 104 |
+
Raises:
|
| 105 |
+
AttributeError: Raised when flag_name is not registered as a valid flag
|
| 106 |
+
name.
|
| 107 |
+
"""
|
| 108 |
+
|
| 109 |
+
def decorate(function):
|
| 110 |
+
register_validator(flag_name, function,
|
| 111 |
+
message=message,
|
| 112 |
+
flag_values=flag_values)
|
| 113 |
+
return function
|
| 114 |
+
return decorate
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def register_multi_flags_validator(flag_names,
|
| 118 |
+
multi_flags_checker,
|
| 119 |
+
message='Flags validation failed',
|
| 120 |
+
flag_values=_flagvalues.FLAGS):
|
| 121 |
+
"""Adds a constraint to multiple flags.
|
| 122 |
+
|
| 123 |
+
The constraint is validated when flags are initially parsed, and after each
|
| 124 |
+
change of the corresponding flag's value.
|
| 125 |
+
|
| 126 |
+
Args:
|
| 127 |
+
flag_names: [str | FlagHolder], a list of the flag names or holders to be
|
| 128 |
+
checked. Positional-only parameter.
|
| 129 |
+
multi_flags_checker: callable, a function to validate the flag.
|
| 130 |
+
|
| 131 |
+
* input - dict, with keys() being flag_names, and value for each key
|
| 132 |
+
being the value of the corresponding flag (string, boolean, etc).
|
| 133 |
+
* output - bool, True if validator constraint is satisfied.
|
| 134 |
+
If constraint is not satisfied, it should either return False or
|
| 135 |
+
raise flags.ValidationError.
|
| 136 |
+
|
| 137 |
+
message: str, error text to be shown to the user if checker returns False.
|
| 138 |
+
If checker raises flags.ValidationError, message from the raised
|
| 139 |
+
error will be shown.
|
| 140 |
+
flag_values: flags.FlagValues, optional FlagValues instance to validate
|
| 141 |
+
against.
|
| 142 |
+
|
| 143 |
+
Raises:
|
| 144 |
+
AttributeError: Raised when a flag is not registered as a valid flag name.
|
| 145 |
+
ValueError: Raised when multiple FlagValues are used in the same
|
| 146 |
+
invocation. This can occur when FlagHolders have different `_flagvalues`
|
| 147 |
+
or when str-type flag_names entries are present and the `flag_values`
|
| 148 |
+
argument does not match that of provided FlagHolder(s).
|
| 149 |
+
"""
|
| 150 |
+
flag_names, flag_values = _flagvalues.resolve_flag_refs(
|
| 151 |
+
flag_names, flag_values)
|
| 152 |
+
v = _validators_classes.MultiFlagsValidator(
|
| 153 |
+
flag_names, multi_flags_checker, message)
|
| 154 |
+
_add_validator(flag_values, v)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def multi_flags_validator(flag_names,
|
| 158 |
+
message='Flag validation failed',
|
| 159 |
+
flag_values=_flagvalues.FLAGS):
|
| 160 |
+
"""A function decorator for defining a multi-flag validator.
|
| 161 |
+
|
| 162 |
+
Registers the decorated function as a validator for flag_names, e.g.::
|
| 163 |
+
|
| 164 |
+
@flags.multi_flags_validator(['foo', 'bar'])
|
| 165 |
+
def _CheckFooBar(flags_dict):
|
| 166 |
+
...
|
| 167 |
+
|
| 168 |
+
See :func:`register_multi_flags_validator` for the specification of checker
|
| 169 |
+
function.
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
flag_names: [str | FlagHolder], a list of the flag names or holders to be
|
| 173 |
+
checked. Positional-only parameter.
|
| 174 |
+
message: str, error text to be shown to the user if checker returns False.
|
| 175 |
+
If checker raises flags.ValidationError, message from the raised
|
| 176 |
+
error will be shown.
|
| 177 |
+
flag_values: flags.FlagValues, optional FlagValues instance to validate
|
| 178 |
+
against.
|
| 179 |
+
|
| 180 |
+
Returns:
|
| 181 |
+
A function decorator that registers its function argument as a validator.
|
| 182 |
+
|
| 183 |
+
Raises:
|
| 184 |
+
AttributeError: Raised when a flag is not registered as a valid flag name.
|
| 185 |
+
"""
|
| 186 |
+
|
| 187 |
+
def decorate(function):
|
| 188 |
+
register_multi_flags_validator(flag_names,
|
| 189 |
+
function,
|
| 190 |
+
message=message,
|
| 191 |
+
flag_values=flag_values)
|
| 192 |
+
return function
|
| 193 |
+
|
| 194 |
+
return decorate
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def mark_flag_as_required(flag_name, flag_values=_flagvalues.FLAGS):
|
| 198 |
+
"""Ensures that flag is not None during program execution.
|
| 199 |
+
|
| 200 |
+
Registers a flag validator, which will follow usual validator rules.
|
| 201 |
+
Important note: validator will pass for any non-``None`` value, such as
|
| 202 |
+
``False``, ``0`` (zero), ``''`` (empty string) and so on.
|
| 203 |
+
|
| 204 |
+
If your module might be imported by others, and you only wish to make the flag
|
| 205 |
+
required when the module is directly executed, call this method like this::
|
| 206 |
+
|
| 207 |
+
if __name__ == '__main__':
|
| 208 |
+
flags.mark_flag_as_required('your_flag_name')
|
| 209 |
+
app.run()
|
| 210 |
+
|
| 211 |
+
Args:
|
| 212 |
+
flag_name: str | FlagHolder, name or holder of the flag.
|
| 213 |
+
Positional-only parameter.
|
| 214 |
+
flag_values: flags.FlagValues, optional :class:`~absl.flags.FlagValues`
|
| 215 |
+
instance where the flag is defined.
|
| 216 |
+
Raises:
|
| 217 |
+
AttributeError: Raised when flag_name is not registered as a valid flag
|
| 218 |
+
name.
|
| 219 |
+
ValueError: Raised when flag_values is non-default and does not match the
|
| 220 |
+
FlagValues of the provided FlagHolder instance.
|
| 221 |
+
"""
|
| 222 |
+
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
|
| 223 |
+
if flag_values[flag_name].default is not None:
|
| 224 |
+
warnings.warn(
|
| 225 |
+
'Flag --%s has a non-None default value; therefore, '
|
| 226 |
+
'mark_flag_as_required will pass even if flag is not specified in the '
|
| 227 |
+
'command line!' % flag_name,
|
| 228 |
+
stacklevel=2)
|
| 229 |
+
register_validator(
|
| 230 |
+
flag_name,
|
| 231 |
+
lambda value: value is not None,
|
| 232 |
+
message=f'Flag --{flag_name} must have a value other than None.',
|
| 233 |
+
flag_values=flag_values,
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def mark_flags_as_required(flag_names, flag_values=_flagvalues.FLAGS):
|
| 238 |
+
"""Ensures that flags are not None during program execution.
|
| 239 |
+
|
| 240 |
+
If your module might be imported by others, and you only wish to make the flag
|
| 241 |
+
required when the module is directly executed, call this method like this::
|
| 242 |
+
|
| 243 |
+
if __name__ == '__main__':
|
| 244 |
+
flags.mark_flags_as_required(['flag1', 'flag2', 'flag3'])
|
| 245 |
+
app.run()
|
| 246 |
+
|
| 247 |
+
Args:
|
| 248 |
+
flag_names: Sequence[str | FlagHolder], names or holders of the flags.
|
| 249 |
+
flag_values: flags.FlagValues, optional FlagValues instance where the flags
|
| 250 |
+
are defined.
|
| 251 |
+
Raises:
|
| 252 |
+
AttributeError: If any of flag name has not already been defined as a flag.
|
| 253 |
+
"""
|
| 254 |
+
for flag_name in flag_names:
|
| 255 |
+
mark_flag_as_required(flag_name, flag_values)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def mark_flags_as_mutual_exclusive(flag_names, required=False,
|
| 259 |
+
flag_values=_flagvalues.FLAGS):
|
| 260 |
+
"""Ensures that only one flag among flag_names is not None.
|
| 261 |
+
|
| 262 |
+
Important note: This validator checks if flag values are ``None``, and it does
|
| 263 |
+
not distinguish between default and explicit values. Therefore, this validator
|
| 264 |
+
does not make sense when applied to flags with default values other than None,
|
| 265 |
+
including other false values (e.g. ``False``, ``0``, ``''``, ``[]``). That
|
| 266 |
+
includes multi flags with a default value of ``[]`` instead of None.
|
| 267 |
+
|
| 268 |
+
Args:
|
| 269 |
+
flag_names: [str | FlagHolder], names or holders of flags.
|
| 270 |
+
Positional-only parameter.
|
| 271 |
+
required: bool. If true, exactly one of the flags must have a value other
|
| 272 |
+
than None. Otherwise, at most one of the flags can have a value other
|
| 273 |
+
than None, and it is valid for all of the flags to be None.
|
| 274 |
+
flag_values: flags.FlagValues, optional FlagValues instance where the flags
|
| 275 |
+
are defined.
|
| 276 |
+
|
| 277 |
+
Raises:
|
| 278 |
+
ValueError: Raised when multiple FlagValues are used in the same
|
| 279 |
+
invocation. This can occur when FlagHolders have different `_flagvalues`
|
| 280 |
+
or when str-type flag_names entries are present and the `flag_values`
|
| 281 |
+
argument does not match that of provided FlagHolder(s).
|
| 282 |
+
"""
|
| 283 |
+
flag_names, flag_values = _flagvalues.resolve_flag_refs(
|
| 284 |
+
flag_names, flag_values)
|
| 285 |
+
for flag_name in flag_names:
|
| 286 |
+
if flag_values[flag_name].default is not None:
|
| 287 |
+
warnings.warn(
|
| 288 |
+
'Flag --{} has a non-None default value. That does not make sense '
|
| 289 |
+
'with mark_flags_as_mutual_exclusive, which checks whether the '
|
| 290 |
+
'listed flags have a value other than None.'.format(flag_name),
|
| 291 |
+
stacklevel=2)
|
| 292 |
+
|
| 293 |
+
def validate_mutual_exclusion(flags_dict):
|
| 294 |
+
flag_count = sum(1 for val in flags_dict.values() if val is not None)
|
| 295 |
+
if flag_count == 1 or (not required and flag_count == 0):
|
| 296 |
+
return True
|
| 297 |
+
raise _exceptions.ValidationError(
|
| 298 |
+
'{} one of ({}) must have a value other than None.'.format(
|
| 299 |
+
'Exactly' if required else 'At most', ', '.join(flag_names)))
|
| 300 |
+
|
| 301 |
+
register_multi_flags_validator(
|
| 302 |
+
flag_names, validate_mutual_exclusion, flag_values=flag_values)
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def mark_bool_flags_as_mutual_exclusive(flag_names, required=False,
|
| 306 |
+
flag_values=_flagvalues.FLAGS):
|
| 307 |
+
"""Ensures that only one flag among flag_names is True.
|
| 308 |
+
|
| 309 |
+
Args:
|
| 310 |
+
flag_names: [str | FlagHolder], names or holders of flags.
|
| 311 |
+
Positional-only parameter.
|
| 312 |
+
required: bool. If true, exactly one flag must be True. Otherwise, at most
|
| 313 |
+
one flag can be True, and it is valid for all flags to be False.
|
| 314 |
+
flag_values: flags.FlagValues, optional FlagValues instance where the flags
|
| 315 |
+
are defined.
|
| 316 |
+
|
| 317 |
+
Raises:
|
| 318 |
+
ValueError: Raised when multiple FlagValues are used in the same
|
| 319 |
+
invocation. This can occur when FlagHolders have different `_flagvalues`
|
| 320 |
+
or when str-type flag_names entries are present and the `flag_values`
|
| 321 |
+
argument does not match that of provided FlagHolder(s).
|
| 322 |
+
"""
|
| 323 |
+
flag_names, flag_values = _flagvalues.resolve_flag_refs(
|
| 324 |
+
flag_names, flag_values)
|
| 325 |
+
for flag_name in flag_names:
|
| 326 |
+
if not flag_values[flag_name].boolean:
|
| 327 |
+
raise _exceptions.ValidationError(
|
| 328 |
+
'Flag --{} is not Boolean, which is required for flags used in '
|
| 329 |
+
'mark_bool_flags_as_mutual_exclusive.'.format(flag_name))
|
| 330 |
+
|
| 331 |
+
def validate_boolean_mutual_exclusion(flags_dict):
|
| 332 |
+
flag_count = sum(bool(val) for val in flags_dict.values())
|
| 333 |
+
if flag_count == 1 or (not required and flag_count == 0):
|
| 334 |
+
return True
|
| 335 |
+
raise _exceptions.ValidationError(
|
| 336 |
+
'{} one of ({}) must be True.'.format(
|
| 337 |
+
'Exactly' if required else 'At most', ', '.join(flag_names)))
|
| 338 |
+
|
| 339 |
+
register_multi_flags_validator(
|
| 340 |
+
flag_names, validate_boolean_mutual_exclusion, flag_values=flag_values)
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def _add_validator(fv, validator_instance):
|
| 344 |
+
"""Register new flags validator to be checked.
|
| 345 |
+
|
| 346 |
+
Args:
|
| 347 |
+
fv: flags.FlagValues, the FlagValues instance to add the validator.
|
| 348 |
+
validator_instance: validators.Validator, the validator to add.
|
| 349 |
+
Raises:
|
| 350 |
+
KeyError: Raised when validators work with a non-existing flag.
|
| 351 |
+
"""
|
| 352 |
+
for flag_name in validator_instance.get_flags_names():
|
| 353 |
+
fv[flag_name].validators.append(validator_instance)
|
lib/python3.10/site-packages/absl/flags/_validators_classes.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Defines *private* classes used for flag validators.
|
| 16 |
+
|
| 17 |
+
Do NOT import this module. DO NOT use anything from this module. They are
|
| 18 |
+
private APIs.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
from absl.flags import _exceptions
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class Validator:
|
| 25 |
+
"""Base class for flags validators.
|
| 26 |
+
|
| 27 |
+
Users should NOT overload these classes, and use flags.Register...
|
| 28 |
+
methods instead.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
# Used to assign each validator an unique insertion_index
|
| 32 |
+
validators_count = 0
|
| 33 |
+
|
| 34 |
+
def __init__(self, checker, message):
|
| 35 |
+
"""Constructor to create all validators.
|
| 36 |
+
|
| 37 |
+
Args:
|
| 38 |
+
checker: function to verify the constraint.
|
| 39 |
+
Input of this method varies, see SingleFlagValidator and
|
| 40 |
+
multi_flags_validator for a detailed description.
|
| 41 |
+
message: str, error message to be shown to the user.
|
| 42 |
+
"""
|
| 43 |
+
self.checker = checker
|
| 44 |
+
self.message = message
|
| 45 |
+
Validator.validators_count += 1
|
| 46 |
+
# Used to assert validators in the order they were registered.
|
| 47 |
+
self.insertion_index = Validator.validators_count
|
| 48 |
+
|
| 49 |
+
def verify(self, flag_values):
|
| 50 |
+
"""Verifies that constraint is satisfied.
|
| 51 |
+
|
| 52 |
+
flags library calls this method to verify Validator's constraint.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
flag_values: flags.FlagValues, the FlagValues instance to get flags from.
|
| 56 |
+
Raises:
|
| 57 |
+
Error: Raised if constraint is not satisfied.
|
| 58 |
+
"""
|
| 59 |
+
param = self._get_input_to_checker_function(flag_values)
|
| 60 |
+
if not self.checker(param):
|
| 61 |
+
raise _exceptions.ValidationError(self.message)
|
| 62 |
+
|
| 63 |
+
def get_flags_names(self):
|
| 64 |
+
"""Returns the names of the flags checked by this validator.
|
| 65 |
+
|
| 66 |
+
Returns:
|
| 67 |
+
[string], names of the flags.
|
| 68 |
+
"""
|
| 69 |
+
raise NotImplementedError('This method should be overloaded')
|
| 70 |
+
|
| 71 |
+
def print_flags_with_values(self, flag_values):
|
| 72 |
+
raise NotImplementedError('This method should be overloaded')
|
| 73 |
+
|
| 74 |
+
def _get_input_to_checker_function(self, flag_values):
|
| 75 |
+
"""Given flag values, returns the input to be given to checker.
|
| 76 |
+
|
| 77 |
+
Args:
|
| 78 |
+
flag_values: flags.FlagValues, containing all flags.
|
| 79 |
+
Returns:
|
| 80 |
+
The input to be given to checker. The return type depends on the specific
|
| 81 |
+
validator.
|
| 82 |
+
"""
|
| 83 |
+
raise NotImplementedError('This method should be overloaded')
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class SingleFlagValidator(Validator):
|
| 87 |
+
"""Validator behind register_validator() method.
|
| 88 |
+
|
| 89 |
+
Validates that a single flag passes its checker function. The checker function
|
| 90 |
+
takes the flag value and returns True (if value looks fine) or, if flag value
|
| 91 |
+
is not valid, either returns False or raises an Exception.
|
| 92 |
+
"""
|
| 93 |
+
|
| 94 |
+
def __init__(self, flag_name, checker, message):
|
| 95 |
+
"""Constructor.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
flag_name: string, name of the flag.
|
| 99 |
+
checker: function to verify the validator.
|
| 100 |
+
input - value of the corresponding flag (string, boolean, etc).
|
| 101 |
+
output - bool, True if validator constraint is satisfied.
|
| 102 |
+
If constraint is not satisfied, it should either return False or
|
| 103 |
+
raise flags.ValidationError(desired_error_message).
|
| 104 |
+
message: str, error message to be shown to the user if validator's
|
| 105 |
+
condition is not satisfied.
|
| 106 |
+
"""
|
| 107 |
+
super().__init__(checker, message)
|
| 108 |
+
self.flag_name = flag_name
|
| 109 |
+
|
| 110 |
+
def get_flags_names(self):
|
| 111 |
+
return [self.flag_name]
|
| 112 |
+
|
| 113 |
+
def print_flags_with_values(self, flag_values):
|
| 114 |
+
return 'flag --%s=%s' % (self.flag_name, flag_values[self.flag_name].value)
|
| 115 |
+
|
| 116 |
+
def _get_input_to_checker_function(self, flag_values):
|
| 117 |
+
"""Given flag values, returns the input to be given to checker.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
flag_values: flags.FlagValues, the FlagValues instance to get flags from.
|
| 121 |
+
Returns:
|
| 122 |
+
object, the input to be given to checker.
|
| 123 |
+
"""
|
| 124 |
+
return flag_values[self.flag_name].value
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class MultiFlagsValidator(Validator):
|
| 128 |
+
"""Validator behind register_multi_flags_validator method.
|
| 129 |
+
|
| 130 |
+
Validates that flag values pass their common checker function. The checker
|
| 131 |
+
function takes flag values and returns True (if values look fine) or,
|
| 132 |
+
if values are not valid, either returns False or raises an Exception.
|
| 133 |
+
"""
|
| 134 |
+
|
| 135 |
+
def __init__(self, flag_names, checker, message):
|
| 136 |
+
"""Constructor.
|
| 137 |
+
|
| 138 |
+
Args:
|
| 139 |
+
flag_names: [str], containing names of the flags used by checker.
|
| 140 |
+
checker: function to verify the validator.
|
| 141 |
+
input - dict, with keys() being flag_names, and value for each
|
| 142 |
+
key being the value of the corresponding flag (string, boolean,
|
| 143 |
+
etc).
|
| 144 |
+
output - bool, True if validator constraint is satisfied.
|
| 145 |
+
If constraint is not satisfied, it should either return False or
|
| 146 |
+
raise flags.ValidationError(desired_error_message).
|
| 147 |
+
message: str, error message to be shown to the user if validator's
|
| 148 |
+
condition is not satisfied
|
| 149 |
+
"""
|
| 150 |
+
super().__init__(checker, message)
|
| 151 |
+
self.flag_names = flag_names
|
| 152 |
+
|
| 153 |
+
def _get_input_to_checker_function(self, flag_values):
|
| 154 |
+
"""Given flag values, returns the input to be given to checker.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
flag_values: flags.FlagValues, the FlagValues instance to get flags from.
|
| 158 |
+
Returns:
|
| 159 |
+
dict, with keys() being self.flag_names, and value for each key
|
| 160 |
+
being the value of the corresponding flag (string, boolean, etc).
|
| 161 |
+
"""
|
| 162 |
+
return {key: flag_values[key].value for key in self.flag_names}
|
| 163 |
+
|
| 164 |
+
def print_flags_with_values(self, flag_values):
|
| 165 |
+
prefix = 'flags '
|
| 166 |
+
flags_with_values = []
|
| 167 |
+
for key in self.flag_names:
|
| 168 |
+
flags_with_values.append('%s=%s' % (key, flag_values[key].value))
|
| 169 |
+
return prefix + ', '.join(flags_with_values)
|
| 170 |
+
|
| 171 |
+
def get_flags_names(self):
|
| 172 |
+
return self.flag_names
|
lib/python3.10/site-packages/absl/flags/argparse_flags.py
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2018 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""This module provides argparse integration with absl.flags.
|
| 16 |
+
|
| 17 |
+
``argparse_flags.ArgumentParser`` is a drop-in replacement for
|
| 18 |
+
:class:`argparse.ArgumentParser`. It takes care of collecting and defining absl
|
| 19 |
+
flags in :mod:`argparse`.
|
| 20 |
+
|
| 21 |
+
Here is a simple example::
|
| 22 |
+
|
| 23 |
+
# Assume the following absl.flags is defined in another module:
|
| 24 |
+
#
|
| 25 |
+
# from absl import flags
|
| 26 |
+
# flags.DEFINE_string('echo', None, 'The echo message.')
|
| 27 |
+
#
|
| 28 |
+
parser = argparse_flags.ArgumentParser(
|
| 29 |
+
description='A demo of absl.flags and argparse integration.')
|
| 30 |
+
parser.add_argument('--header', help='Header message to print.')
|
| 31 |
+
|
| 32 |
+
# The parser will also accept the absl flag `--echo`.
|
| 33 |
+
# The `header` value is available as `args.header` just like a regular
|
| 34 |
+
# argparse flag. The absl flag `--echo` continues to be available via
|
| 35 |
+
# `absl.flags.FLAGS` if you want to access it.
|
| 36 |
+
args = parser.parse_args()
|
| 37 |
+
|
| 38 |
+
# Example usages:
|
| 39 |
+
# ./program --echo='A message.' --header='A header'
|
| 40 |
+
# ./program --header 'A header' --echo 'A message.'
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
Here is another example demonstrates subparsers::
|
| 44 |
+
|
| 45 |
+
parser = argparse_flags.ArgumentParser(description='A subcommands demo.')
|
| 46 |
+
parser.add_argument('--header', help='The header message to print.')
|
| 47 |
+
|
| 48 |
+
subparsers = parser.add_subparsers(help='The command to execute.')
|
| 49 |
+
|
| 50 |
+
roll_dice_parser = subparsers.add_parser(
|
| 51 |
+
'roll_dice', help='Roll a dice.',
|
| 52 |
+
# By default, absl flags can also be specified after the sub-command.
|
| 53 |
+
# To only allow them before sub-command, pass
|
| 54 |
+
# `inherited_absl_flags=None`.
|
| 55 |
+
inherited_absl_flags=None)
|
| 56 |
+
roll_dice_parser.add_argument('--num_faces', type=int, default=6)
|
| 57 |
+
roll_dice_parser.set_defaults(command=roll_dice)
|
| 58 |
+
|
| 59 |
+
shuffle_parser = subparsers.add_parser('shuffle', help='Shuffle inputs.')
|
| 60 |
+
shuffle_parser.add_argument(
|
| 61 |
+
'inputs', metavar='I', nargs='+', help='Inputs to shuffle.')
|
| 62 |
+
shuffle_parser.set_defaults(command=shuffle)
|
| 63 |
+
|
| 64 |
+
args = parser.parse_args(argv[1:])
|
| 65 |
+
args.command(args)
|
| 66 |
+
|
| 67 |
+
# Example usages:
|
| 68 |
+
# ./program --echo='A message.' roll_dice --num_faces=6
|
| 69 |
+
# ./program shuffle --echo='A message.' 1 2 3 4
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
There are several differences between :mod:`absl.flags` and
|
| 73 |
+
:mod:`~absl.flags.argparse_flags`:
|
| 74 |
+
|
| 75 |
+
1. Flags defined with absl.flags are parsed differently when using the
|
| 76 |
+
argparse parser. Notably:
|
| 77 |
+
|
| 78 |
+
1) absl.flags allows both single-dash and double-dash for any flag, and
|
| 79 |
+
doesn't distinguish them; argparse_flags only allows double-dash for
|
| 80 |
+
flag's regular name, and single-dash for flag's ``short_name``.
|
| 81 |
+
2) Boolean flags in absl.flags can be specified with ``--bool``,
|
| 82 |
+
``--nobool``, as well as ``--bool=true/false`` (though not recommended);
|
| 83 |
+
in argparse_flags, it only allows ``--bool``, ``--nobool``.
|
| 84 |
+
|
| 85 |
+
2. Help related flag differences:
|
| 86 |
+
|
| 87 |
+
1) absl.flags does not define help flags, absl.app does that; argparse_flags
|
| 88 |
+
defines help flags unless passed with ``add_help=False``.
|
| 89 |
+
2) absl.app supports ``--helpxml``; argparse_flags does not.
|
| 90 |
+
3) argparse_flags supports ``-h``; absl.app does not.
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
import argparse
|
| 94 |
+
import sys
|
| 95 |
+
|
| 96 |
+
from absl import flags
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
_BUILT_IN_FLAGS = frozenset({
|
| 100 |
+
'help',
|
| 101 |
+
'helpshort',
|
| 102 |
+
'helpfull',
|
| 103 |
+
'helpxml',
|
| 104 |
+
'flagfile',
|
| 105 |
+
'undefok',
|
| 106 |
+
})
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class ArgumentParser(argparse.ArgumentParser):
|
| 110 |
+
"""Custom ArgumentParser class to support special absl flags."""
|
| 111 |
+
|
| 112 |
+
def __init__(self, **kwargs):
|
| 113 |
+
"""Initializes ArgumentParser.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
**kwargs: same as argparse.ArgumentParser, except:
|
| 117 |
+
1. It also accepts `inherited_absl_flags`: the absl flags to inherit.
|
| 118 |
+
The default is the global absl.flags.FLAGS instance. Pass None to
|
| 119 |
+
ignore absl flags.
|
| 120 |
+
2. The `prefix_chars` argument must be the default value '-'.
|
| 121 |
+
|
| 122 |
+
Raises:
|
| 123 |
+
ValueError: Raised when prefix_chars is not '-'.
|
| 124 |
+
"""
|
| 125 |
+
prefix_chars = kwargs.get('prefix_chars', '-')
|
| 126 |
+
if prefix_chars != '-':
|
| 127 |
+
raise ValueError(
|
| 128 |
+
'argparse_flags.ArgumentParser only supports "-" as the prefix '
|
| 129 |
+
'character, found "{}".'.format(prefix_chars))
|
| 130 |
+
|
| 131 |
+
# Remove inherited_absl_flags before calling super.
|
| 132 |
+
self._inherited_absl_flags = kwargs.pop('inherited_absl_flags', flags.FLAGS)
|
| 133 |
+
# Now call super to initialize argparse.ArgumentParser before calling
|
| 134 |
+
# add_argument in _define_absl_flags.
|
| 135 |
+
super().__init__(**kwargs)
|
| 136 |
+
|
| 137 |
+
if self.add_help:
|
| 138 |
+
# -h and --help are defined in super.
|
| 139 |
+
# Also add the --helpshort and --helpfull flags.
|
| 140 |
+
self.add_argument(
|
| 141 |
+
# Action 'help' defines a similar flag to -h/--help.
|
| 142 |
+
'--helpshort', action='help',
|
| 143 |
+
default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
| 144 |
+
self.add_argument(
|
| 145 |
+
'--helpfull', action=_HelpFullAction,
|
| 146 |
+
default=argparse.SUPPRESS, help='show full help message and exit')
|
| 147 |
+
|
| 148 |
+
if self._inherited_absl_flags is not None:
|
| 149 |
+
self.add_argument(
|
| 150 |
+
'--undefok', default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
| 151 |
+
self._define_absl_flags(self._inherited_absl_flags)
|
| 152 |
+
|
| 153 |
+
def parse_known_args(self, args=None, namespace=None):
|
| 154 |
+
if args is None:
|
| 155 |
+
args = sys.argv[1:]
|
| 156 |
+
if self._inherited_absl_flags is not None:
|
| 157 |
+
# Handle --flagfile.
|
| 158 |
+
# Explicitly specify force_gnu=True, since argparse behaves like
|
| 159 |
+
# gnu_getopt: flags can be specified after positional arguments.
|
| 160 |
+
args = self._inherited_absl_flags.read_flags_from_files(
|
| 161 |
+
args, force_gnu=True)
|
| 162 |
+
|
| 163 |
+
undefok_missing = object()
|
| 164 |
+
undefok = getattr(namespace, 'undefok', undefok_missing)
|
| 165 |
+
|
| 166 |
+
namespace, args = super().parse_known_args(args, namespace)
|
| 167 |
+
|
| 168 |
+
# For Python <= 2.7.8: https://bugs.python.org/issue9351, a bug where
|
| 169 |
+
# sub-parsers don't preserve existing namespace attributes.
|
| 170 |
+
# Restore the undefok attribute if a sub-parser dropped it.
|
| 171 |
+
if undefok is not undefok_missing:
|
| 172 |
+
namespace.undefok = undefok
|
| 173 |
+
|
| 174 |
+
if self._inherited_absl_flags is not None:
|
| 175 |
+
# Handle --undefok. At this point, `args` only contains unknown flags,
|
| 176 |
+
# so it won't strip defined flags that are also specified with --undefok.
|
| 177 |
+
# For Python <= 2.7.8: https://bugs.python.org/issue9351, a bug where
|
| 178 |
+
# sub-parsers don't preserve existing namespace attributes. The undefok
|
| 179 |
+
# attribute might not exist because a subparser dropped it.
|
| 180 |
+
if hasattr(namespace, 'undefok'):
|
| 181 |
+
args = _strip_undefok_args(namespace.undefok, args)
|
| 182 |
+
# absl flags are not exposed in the Namespace object. See Namespace:
|
| 183 |
+
# https://docs.python.org/3/library/argparse.html#argparse.Namespace.
|
| 184 |
+
del namespace.undefok
|
| 185 |
+
self._inherited_absl_flags.mark_as_parsed()
|
| 186 |
+
try:
|
| 187 |
+
self._inherited_absl_flags.validate_all_flags()
|
| 188 |
+
except flags.IllegalFlagValueError as e:
|
| 189 |
+
self.error(str(e))
|
| 190 |
+
|
| 191 |
+
return namespace, args
|
| 192 |
+
|
| 193 |
+
def _define_absl_flags(self, absl_flags):
|
| 194 |
+
"""Defines flags from absl_flags."""
|
| 195 |
+
key_flags = set(absl_flags.get_key_flags_for_module(sys.argv[0]))
|
| 196 |
+
for name in absl_flags:
|
| 197 |
+
if name in _BUILT_IN_FLAGS:
|
| 198 |
+
# Do not inherit built-in flags.
|
| 199 |
+
continue
|
| 200 |
+
flag_instance = absl_flags[name]
|
| 201 |
+
# Each flags with short_name appears in FLAGS twice, so only define
|
| 202 |
+
# when the dictionary key is equal to the regular name.
|
| 203 |
+
if name == flag_instance.name:
|
| 204 |
+
# Suppress the flag in the help short message if it's not a main
|
| 205 |
+
# module's key flag.
|
| 206 |
+
suppress = flag_instance not in key_flags
|
| 207 |
+
self._define_absl_flag(flag_instance, suppress)
|
| 208 |
+
|
| 209 |
+
def _define_absl_flag(self, flag_instance, suppress):
|
| 210 |
+
"""Defines a flag from the flag_instance."""
|
| 211 |
+
flag_name = flag_instance.name
|
| 212 |
+
short_name = flag_instance.short_name
|
| 213 |
+
argument_names = ['--' + flag_name]
|
| 214 |
+
if short_name:
|
| 215 |
+
argument_names.insert(0, '-' + short_name)
|
| 216 |
+
if suppress:
|
| 217 |
+
helptext = argparse.SUPPRESS
|
| 218 |
+
else:
|
| 219 |
+
# argparse help string uses %-formatting. Escape the literal %'s.
|
| 220 |
+
helptext = flag_instance.help.replace('%', '%%')
|
| 221 |
+
if flag_instance.boolean:
|
| 222 |
+
# Only add the `no` form to the long name.
|
| 223 |
+
argument_names.append('--no' + flag_name)
|
| 224 |
+
self.add_argument(
|
| 225 |
+
*argument_names, action=_BooleanFlagAction, help=helptext,
|
| 226 |
+
metavar=flag_instance.name.upper(),
|
| 227 |
+
flag_instance=flag_instance)
|
| 228 |
+
else:
|
| 229 |
+
self.add_argument(
|
| 230 |
+
*argument_names, action=_FlagAction, help=helptext,
|
| 231 |
+
metavar=flag_instance.name.upper(),
|
| 232 |
+
flag_instance=flag_instance)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class _FlagAction(argparse.Action):
|
| 236 |
+
"""Action class for Abseil non-boolean flags."""
|
| 237 |
+
|
| 238 |
+
def __init__(
|
| 239 |
+
self,
|
| 240 |
+
option_strings,
|
| 241 |
+
dest,
|
| 242 |
+
help, # pylint: disable=redefined-builtin
|
| 243 |
+
metavar,
|
| 244 |
+
flag_instance,
|
| 245 |
+
default=argparse.SUPPRESS):
|
| 246 |
+
"""Initializes _FlagAction.
|
| 247 |
+
|
| 248 |
+
Args:
|
| 249 |
+
option_strings: See argparse.Action.
|
| 250 |
+
dest: Ignored. The flag is always defined with dest=argparse.SUPPRESS.
|
| 251 |
+
help: See argparse.Action.
|
| 252 |
+
metavar: See argparse.Action.
|
| 253 |
+
flag_instance: absl.flags.Flag, the absl flag instance.
|
| 254 |
+
default: Ignored. The flag always uses dest=argparse.SUPPRESS so it
|
| 255 |
+
doesn't affect the parsing result.
|
| 256 |
+
"""
|
| 257 |
+
del dest
|
| 258 |
+
self._flag_instance = flag_instance
|
| 259 |
+
super().__init__(
|
| 260 |
+
option_strings=option_strings,
|
| 261 |
+
dest=argparse.SUPPRESS,
|
| 262 |
+
help=help,
|
| 263 |
+
metavar=metavar,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
def __call__(self, parser, namespace, values, option_string=None):
|
| 267 |
+
"""See https://docs.python.org/3/library/argparse.html#action-classes."""
|
| 268 |
+
self._flag_instance.parse(values)
|
| 269 |
+
self._flag_instance.using_default_value = False
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
class _BooleanFlagAction(argparse.Action):
|
| 273 |
+
"""Action class for Abseil boolean flags."""
|
| 274 |
+
|
| 275 |
+
def __init__(
|
| 276 |
+
self,
|
| 277 |
+
option_strings,
|
| 278 |
+
dest,
|
| 279 |
+
help, # pylint: disable=redefined-builtin
|
| 280 |
+
metavar,
|
| 281 |
+
flag_instance,
|
| 282 |
+
default=argparse.SUPPRESS):
|
| 283 |
+
"""Initializes _BooleanFlagAction.
|
| 284 |
+
|
| 285 |
+
Args:
|
| 286 |
+
option_strings: See argparse.Action.
|
| 287 |
+
dest: Ignored. The flag is always defined with dest=argparse.SUPPRESS.
|
| 288 |
+
help: See argparse.Action.
|
| 289 |
+
metavar: See argparse.Action.
|
| 290 |
+
flag_instance: absl.flags.Flag, the absl flag instance.
|
| 291 |
+
default: Ignored. The flag always uses dest=argparse.SUPPRESS so it
|
| 292 |
+
doesn't affect the parsing result.
|
| 293 |
+
"""
|
| 294 |
+
del dest, default
|
| 295 |
+
self._flag_instance = flag_instance
|
| 296 |
+
flag_names = [self._flag_instance.name]
|
| 297 |
+
if self._flag_instance.short_name:
|
| 298 |
+
flag_names.append(self._flag_instance.short_name)
|
| 299 |
+
self._flag_names = frozenset(flag_names)
|
| 300 |
+
super().__init__(
|
| 301 |
+
option_strings=option_strings,
|
| 302 |
+
dest=argparse.SUPPRESS,
|
| 303 |
+
nargs=0, # Does not accept values, only `--bool` or `--nobool`.
|
| 304 |
+
help=help,
|
| 305 |
+
metavar=metavar,
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
def __call__(self, parser, namespace, values, option_string=None):
|
| 309 |
+
"""See https://docs.python.org/3/library/argparse.html#action-classes."""
|
| 310 |
+
if not isinstance(values, list) or values:
|
| 311 |
+
raise ValueError('values must be an empty list.')
|
| 312 |
+
if option_string.startswith('--'):
|
| 313 |
+
option = option_string[2:]
|
| 314 |
+
else:
|
| 315 |
+
option = option_string[1:]
|
| 316 |
+
if option in self._flag_names:
|
| 317 |
+
self._flag_instance.parse('true')
|
| 318 |
+
else:
|
| 319 |
+
if not option.startswith('no') or option[2:] not in self._flag_names:
|
| 320 |
+
raise ValueError('invalid option_string: ' + option_string)
|
| 321 |
+
self._flag_instance.parse('false')
|
| 322 |
+
self._flag_instance.using_default_value = False
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class _HelpFullAction(argparse.Action):
|
| 326 |
+
"""Action class for --helpfull flag."""
|
| 327 |
+
|
| 328 |
+
def __init__(self, option_strings, dest, default, help): # pylint: disable=redefined-builtin
|
| 329 |
+
"""Initializes _HelpFullAction.
|
| 330 |
+
|
| 331 |
+
Args:
|
| 332 |
+
option_strings: See argparse.Action.
|
| 333 |
+
dest: Ignored. The flag is always defined with dest=argparse.SUPPRESS.
|
| 334 |
+
default: Ignored.
|
| 335 |
+
help: See argparse.Action.
|
| 336 |
+
"""
|
| 337 |
+
del dest, default
|
| 338 |
+
super().__init__(
|
| 339 |
+
option_strings=option_strings,
|
| 340 |
+
dest=argparse.SUPPRESS,
|
| 341 |
+
default=argparse.SUPPRESS,
|
| 342 |
+
nargs=0,
|
| 343 |
+
help=help,
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
def __call__(self, parser, namespace, values, option_string=None):
|
| 347 |
+
"""See https://docs.python.org/3/library/argparse.html#action-classes."""
|
| 348 |
+
# This only prints flags when help is not argparse.SUPPRESS.
|
| 349 |
+
# It includes user defined argparse flags, as well as main module's
|
| 350 |
+
# key absl flags. Other absl flags use argparse.SUPPRESS, so they aren't
|
| 351 |
+
# printed here.
|
| 352 |
+
parser.print_help()
|
| 353 |
+
|
| 354 |
+
absl_flags = parser._inherited_absl_flags # pylint: disable=protected-access
|
| 355 |
+
if absl_flags is not None:
|
| 356 |
+
modules = sorted(absl_flags.flags_by_module_dict())
|
| 357 |
+
main_module = sys.argv[0]
|
| 358 |
+
if main_module in modules:
|
| 359 |
+
# The main module flags are already printed in parser.print_help().
|
| 360 |
+
modules.remove(main_module)
|
| 361 |
+
print(absl_flags._get_help_for_modules( # pylint: disable=protected-access
|
| 362 |
+
modules, prefix='', include_special_flags=True))
|
| 363 |
+
parser.exit()
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
def _strip_undefok_args(undefok, args):
|
| 367 |
+
"""Returns a new list of args after removing flags in --undefok."""
|
| 368 |
+
if undefok:
|
| 369 |
+
undefok_names = {name.strip() for name in undefok.split(',')}
|
| 370 |
+
undefok_names |= {'no' + name for name in undefok_names}
|
| 371 |
+
# Remove undefok flags.
|
| 372 |
+
args = [arg for arg in args if not _is_undefok(arg, undefok_names)]
|
| 373 |
+
return args
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
def _is_undefok(arg, undefok_names):
|
| 377 |
+
"""Returns whether we can ignore arg based on a set of undefok flag names."""
|
| 378 |
+
if not arg.startswith('-'):
|
| 379 |
+
return False
|
| 380 |
+
if arg.startswith('--'):
|
| 381 |
+
arg_without_dash = arg[2:]
|
| 382 |
+
else:
|
| 383 |
+
arg_without_dash = arg[1:]
|
| 384 |
+
if '=' in arg_without_dash:
|
| 385 |
+
name, _ = arg_without_dash.split('=', 1)
|
| 386 |
+
else:
|
| 387 |
+
name = arg_without_dash
|
| 388 |
+
if name in undefok_names:
|
| 389 |
+
return True
|
| 390 |
+
return False
|
lib/python3.10/site-packages/absl/logging/__init__.py
ADDED
|
@@ -0,0 +1,1331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Abseil Python logging module implemented on top of standard logging.
|
| 16 |
+
|
| 17 |
+
Simple usage::
|
| 18 |
+
|
| 19 |
+
from absl import logging
|
| 20 |
+
|
| 21 |
+
logging.info('Interesting Stuff')
|
| 22 |
+
logging.info('Interesting Stuff with Arguments: %d', 42)
|
| 23 |
+
|
| 24 |
+
logging.set_verbosity(logging.INFO)
|
| 25 |
+
logging.log(logging.DEBUG, 'This will *not* be printed')
|
| 26 |
+
logging.set_verbosity(logging.DEBUG)
|
| 27 |
+
logging.log(logging.DEBUG, 'This will be printed')
|
| 28 |
+
|
| 29 |
+
logging.warning('Worrying Stuff')
|
| 30 |
+
logging.error('Alarming Stuff')
|
| 31 |
+
logging.fatal('AAAAHHHHH!!!!') # Process exits.
|
| 32 |
+
|
| 33 |
+
Usage note: Do not pre-format the strings in your program code.
|
| 34 |
+
Instead, let the logging module perform argument interpolation.
|
| 35 |
+
This saves cycles because strings that don't need to be printed
|
| 36 |
+
are never formatted. Note that this module does not attempt to
|
| 37 |
+
interpolate arguments when no arguments are given. In other words::
|
| 38 |
+
|
| 39 |
+
logging.info('Interesting Stuff: %s')
|
| 40 |
+
|
| 41 |
+
does not raise an exception because logging.info() has only one
|
| 42 |
+
argument, the message string.
|
| 43 |
+
|
| 44 |
+
"Lazy" evaluation for debugging
|
| 45 |
+
-------------------------------
|
| 46 |
+
|
| 47 |
+
If you do something like this::
|
| 48 |
+
|
| 49 |
+
logging.debug('Thing: %s', thing.ExpensiveOp())
|
| 50 |
+
|
| 51 |
+
then the ExpensiveOp will be evaluated even if nothing
|
| 52 |
+
is printed to the log. To avoid this, use the level_debug() function::
|
| 53 |
+
|
| 54 |
+
if logging.level_debug():
|
| 55 |
+
logging.debug('Thing: %s', thing.ExpensiveOp())
|
| 56 |
+
|
| 57 |
+
Per file level logging is supported by logging.vlog() and
|
| 58 |
+
logging.vlog_is_on(). For example::
|
| 59 |
+
|
| 60 |
+
if logging.vlog_is_on(2):
|
| 61 |
+
logging.vlog(2, very_expensive_debug_message())
|
| 62 |
+
|
| 63 |
+
Notes on Unicode
|
| 64 |
+
----------------
|
| 65 |
+
|
| 66 |
+
The log output is encoded as UTF-8. Don't pass data in other encodings in
|
| 67 |
+
bytes() instances -- instead pass unicode string instances when you need to
|
| 68 |
+
(for both the format string and arguments).
|
| 69 |
+
|
| 70 |
+
Note on critical and fatal:
|
| 71 |
+
Standard logging module defines fatal as an alias to critical, but it's not
|
| 72 |
+
documented, and it does NOT actually terminate the program.
|
| 73 |
+
This module only defines fatal but not critical, and it DOES terminate the
|
| 74 |
+
program.
|
| 75 |
+
|
| 76 |
+
The differences in behavior are historical and unfortunate.
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
import collections
|
| 80 |
+
from collections import abc
|
| 81 |
+
import getpass
|
| 82 |
+
import io
|
| 83 |
+
import inspect
|
| 84 |
+
import itertools
|
| 85 |
+
import logging
|
| 86 |
+
import os
|
| 87 |
+
import socket
|
| 88 |
+
import struct
|
| 89 |
+
import sys
|
| 90 |
+
import tempfile
|
| 91 |
+
import threading
|
| 92 |
+
import time
|
| 93 |
+
import timeit
|
| 94 |
+
import traceback
|
| 95 |
+
import types
|
| 96 |
+
import warnings
|
| 97 |
+
|
| 98 |
+
from absl import flags
|
| 99 |
+
from absl.logging import converter
|
| 100 |
+
|
| 101 |
+
# pylint: disable=g-import-not-at-top
|
| 102 |
+
try:
|
| 103 |
+
from typing import NoReturn
|
| 104 |
+
except ImportError:
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
+
# pylint: enable=g-import-not-at-top
|
| 108 |
+
|
| 109 |
+
FLAGS = flags.FLAGS
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
# Logging levels.
|
| 113 |
+
FATAL = converter.ABSL_FATAL
|
| 114 |
+
ERROR = converter.ABSL_ERROR
|
| 115 |
+
WARNING = converter.ABSL_WARNING
|
| 116 |
+
WARN = converter.ABSL_WARNING # Deprecated name.
|
| 117 |
+
INFO = converter.ABSL_INFO
|
| 118 |
+
DEBUG = converter.ABSL_DEBUG
|
| 119 |
+
|
| 120 |
+
# Regex to match/parse log line prefixes.
|
| 121 |
+
ABSL_LOGGING_PREFIX_REGEX = (
|
| 122 |
+
r'^(?P<severity>[IWEF])'
|
| 123 |
+
r'(?P<month>\d\d)(?P<day>\d\d) '
|
| 124 |
+
r'(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)'
|
| 125 |
+
r'\.(?P<microsecond>\d\d\d\d\d\d) +'
|
| 126 |
+
r'(?P<thread_id>-?\d+) '
|
| 127 |
+
r'(?P<filename>[a-zA-Z<][\w._<>-]+):(?P<line>\d+)')
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
# Mask to convert integer thread ids to unsigned quantities for logging purposes
|
| 131 |
+
_THREAD_ID_MASK = 2 ** (struct.calcsize('L') * 8) - 1
|
| 132 |
+
|
| 133 |
+
# Extra property set on the LogRecord created by ABSLLogger when its level is
|
| 134 |
+
# CRITICAL/FATAL.
|
| 135 |
+
_ABSL_LOG_FATAL = '_absl_log_fatal'
|
| 136 |
+
# Extra prefix added to the log message when a non-absl logger logs a
|
| 137 |
+
# CRITICAL/FATAL message.
|
| 138 |
+
_CRITICAL_PREFIX = 'CRITICAL - '
|
| 139 |
+
|
| 140 |
+
# Used by findCaller to skip callers from */logging/__init__.py.
|
| 141 |
+
_LOGGING_FILE_PREFIX = os.path.join('logging', '__init__.')
|
| 142 |
+
|
| 143 |
+
# The ABSL logger instance, initialized in _initialize().
|
| 144 |
+
_absl_logger = None
|
| 145 |
+
# The ABSL handler instance, initialized in _initialize().
|
| 146 |
+
_absl_handler = None
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
_CPP_NAME_TO_LEVELS = {
|
| 150 |
+
'debug': '0', # Abseil C++ has no DEBUG level, mapping it to INFO here.
|
| 151 |
+
'info': '0',
|
| 152 |
+
'warning': '1',
|
| 153 |
+
'warn': '1',
|
| 154 |
+
'error': '2',
|
| 155 |
+
'fatal': '3'
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
_CPP_LEVEL_TO_NAMES = {
|
| 159 |
+
'0': 'info',
|
| 160 |
+
'1': 'warning',
|
| 161 |
+
'2': 'error',
|
| 162 |
+
'3': 'fatal',
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class _VerbosityFlag(flags.Flag):
|
| 167 |
+
"""Flag class for -v/--verbosity."""
|
| 168 |
+
|
| 169 |
+
def __init__(self, *args, **kwargs):
|
| 170 |
+
super().__init__(
|
| 171 |
+
flags.IntegerParser(), flags.ArgumentSerializer(), *args, **kwargs
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
@property
|
| 175 |
+
def value(self):
|
| 176 |
+
return self._value
|
| 177 |
+
|
| 178 |
+
@value.setter
|
| 179 |
+
def value(self, v):
|
| 180 |
+
self._value = v
|
| 181 |
+
self._update_logging_levels()
|
| 182 |
+
|
| 183 |
+
def _update_logging_levels(self):
|
| 184 |
+
"""Updates absl logging levels to the current verbosity.
|
| 185 |
+
|
| 186 |
+
Visibility: module-private
|
| 187 |
+
"""
|
| 188 |
+
if not _absl_logger:
|
| 189 |
+
return
|
| 190 |
+
|
| 191 |
+
if self._value <= converter.ABSL_DEBUG:
|
| 192 |
+
standard_verbosity = converter.absl_to_standard(self._value)
|
| 193 |
+
else:
|
| 194 |
+
# --verbosity is set to higher than 1 for vlog.
|
| 195 |
+
standard_verbosity = logging.DEBUG - (self._value - 1)
|
| 196 |
+
|
| 197 |
+
# Also update root level when absl_handler is used.
|
| 198 |
+
if _absl_handler in logging.root.handlers:
|
| 199 |
+
# Make absl logger inherit from the root logger. absl logger might have
|
| 200 |
+
# a non-NOTSET value if logging.set_verbosity() is called at import time.
|
| 201 |
+
_absl_logger.setLevel(logging.NOTSET)
|
| 202 |
+
logging.root.setLevel(standard_verbosity)
|
| 203 |
+
else:
|
| 204 |
+
_absl_logger.setLevel(standard_verbosity)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class _LoggerLevelsFlag(flags.Flag):
|
| 208 |
+
"""Flag class for --logger_levels."""
|
| 209 |
+
|
| 210 |
+
def __init__(self, *args, **kwargs):
|
| 211 |
+
super().__init__(
|
| 212 |
+
_LoggerLevelsParser(), _LoggerLevelsSerializer(), *args, **kwargs
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
@property
|
| 216 |
+
def value(self):
|
| 217 |
+
# For lack of an immutable type, be defensive and return a copy.
|
| 218 |
+
# Modifications to the dict aren't supported and won't have any affect.
|
| 219 |
+
# While Py3 could use MappingProxyType, that isn't deepcopy friendly, so
|
| 220 |
+
# just return a copy.
|
| 221 |
+
return self._value.copy()
|
| 222 |
+
|
| 223 |
+
@value.setter
|
| 224 |
+
def value(self, v):
|
| 225 |
+
self._value = {} if v is None else v
|
| 226 |
+
self._update_logger_levels()
|
| 227 |
+
|
| 228 |
+
def _update_logger_levels(self):
|
| 229 |
+
# Visibility: module-private.
|
| 230 |
+
# This is called by absl.app.run() during initialization.
|
| 231 |
+
for name, level in self._value.items():
|
| 232 |
+
logging.getLogger(name).setLevel(level)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class _LoggerLevelsParser(flags.ArgumentParser):
|
| 236 |
+
"""Parser for --logger_levels flag."""
|
| 237 |
+
|
| 238 |
+
def parse(self, value):
|
| 239 |
+
if isinstance(value, abc.Mapping):
|
| 240 |
+
return value
|
| 241 |
+
|
| 242 |
+
pairs = [pair.strip() for pair in value.split(',') if pair.strip()]
|
| 243 |
+
|
| 244 |
+
# Preserve the order so that serialization is deterministic.
|
| 245 |
+
levels = collections.OrderedDict()
|
| 246 |
+
for name_level in pairs:
|
| 247 |
+
name, level = name_level.split(':', 1)
|
| 248 |
+
name = name.strip()
|
| 249 |
+
level = level.strip()
|
| 250 |
+
levels[name] = level
|
| 251 |
+
return levels
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
class _LoggerLevelsSerializer:
|
| 255 |
+
"""Serializer for --logger_levels flag."""
|
| 256 |
+
|
| 257 |
+
def serialize(self, value):
|
| 258 |
+
if isinstance(value, str):
|
| 259 |
+
return value
|
| 260 |
+
return ','.join(f'{name}:{level}' for name, level in value.items())
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
class _StderrthresholdFlag(flags.Flag):
|
| 264 |
+
"""Flag class for --stderrthreshold."""
|
| 265 |
+
|
| 266 |
+
def __init__(self, *args, **kwargs):
|
| 267 |
+
super().__init__(
|
| 268 |
+
flags.ArgumentParser(), flags.ArgumentSerializer(), *args, **kwargs
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
@property
|
| 272 |
+
def value(self):
|
| 273 |
+
return self._value
|
| 274 |
+
|
| 275 |
+
@value.setter
|
| 276 |
+
def value(self, v):
|
| 277 |
+
if v in _CPP_LEVEL_TO_NAMES:
|
| 278 |
+
# --stderrthreshold also accepts numeric strings whose values are
|
| 279 |
+
# Abseil C++ log levels.
|
| 280 |
+
cpp_value = int(v)
|
| 281 |
+
v = _CPP_LEVEL_TO_NAMES[v] # Normalize to strings.
|
| 282 |
+
elif v.lower() in _CPP_NAME_TO_LEVELS:
|
| 283 |
+
v = v.lower()
|
| 284 |
+
if v == 'warn':
|
| 285 |
+
v = 'warning' # Use 'warning' as the canonical name.
|
| 286 |
+
cpp_value = int(_CPP_NAME_TO_LEVELS[v])
|
| 287 |
+
else:
|
| 288 |
+
raise ValueError(
|
| 289 |
+
'--stderrthreshold must be one of (case-insensitive) '
|
| 290 |
+
"'debug', 'info', 'warning', 'error', 'fatal', "
|
| 291 |
+
"or '0', '1', '2', '3', not '%s'" % v)
|
| 292 |
+
|
| 293 |
+
self._value = v
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
LOGTOSTDERR = flags.DEFINE_boolean(
|
| 297 |
+
'logtostderr',
|
| 298 |
+
False,
|
| 299 |
+
'Should only log to stderr?',
|
| 300 |
+
allow_override_cpp=True,
|
| 301 |
+
)
|
| 302 |
+
ALSOLOGTOSTDERR = flags.DEFINE_boolean(
|
| 303 |
+
'alsologtostderr',
|
| 304 |
+
False,
|
| 305 |
+
'also log to stderr?',
|
| 306 |
+
allow_override_cpp=True,
|
| 307 |
+
)
|
| 308 |
+
LOG_DIR = flags.DEFINE_string(
|
| 309 |
+
'log_dir',
|
| 310 |
+
os.getenv('TEST_TMPDIR', ''),
|
| 311 |
+
'directory to write logfiles into',
|
| 312 |
+
allow_override_cpp=True,
|
| 313 |
+
)
|
| 314 |
+
VERBOSITY = flags.DEFINE_flag(
|
| 315 |
+
_VerbosityFlag(
|
| 316 |
+
'verbosity',
|
| 317 |
+
-1,
|
| 318 |
+
(
|
| 319 |
+
'Logging verbosity level. Messages logged at this level or lower'
|
| 320 |
+
' will be included. Set to 1 for debug logging. If the flag was not'
|
| 321 |
+
' set or supplied, the value will be changed from the default of -1'
|
| 322 |
+
' (warning) to 0 (info) after flags are parsed.'
|
| 323 |
+
),
|
| 324 |
+
short_name='v',
|
| 325 |
+
allow_hide_cpp=True,
|
| 326 |
+
)
|
| 327 |
+
)
|
| 328 |
+
LOGGER_LEVELS = flags.DEFINE_flag(
|
| 329 |
+
_LoggerLevelsFlag(
|
| 330 |
+
'logger_levels',
|
| 331 |
+
{},
|
| 332 |
+
(
|
| 333 |
+
'Specify log level of loggers. The format is a CSV list of '
|
| 334 |
+
'`name:level`. Where `name` is the logger name used with '
|
| 335 |
+
'`logging.getLogger()`, and `level` is a level name (INFO, DEBUG, '
|
| 336 |
+
'etc). e.g. `myapp.foo:INFO,other.logger:DEBUG`'
|
| 337 |
+
),
|
| 338 |
+
)
|
| 339 |
+
)
|
| 340 |
+
STDERRTHRESHOLD = flags.DEFINE_flag(
|
| 341 |
+
_StderrthresholdFlag(
|
| 342 |
+
'stderrthreshold',
|
| 343 |
+
'fatal',
|
| 344 |
+
(
|
| 345 |
+
'log messages at this level, or more severe, to stderr in '
|
| 346 |
+
'addition to the logfile. Possible values are '
|
| 347 |
+
"'debug', 'info', 'warning', 'error', and 'fatal'. "
|
| 348 |
+
'Obsoletes --alsologtostderr. Using --alsologtostderr '
|
| 349 |
+
'cancels the effect of this flag. Please also note that '
|
| 350 |
+
'this flag is subject to --verbosity and requires logfile '
|
| 351 |
+
'not be stderr.'
|
| 352 |
+
),
|
| 353 |
+
allow_hide_cpp=True,
|
| 354 |
+
)
|
| 355 |
+
)
|
| 356 |
+
SHOWPREFIXFORINFO = flags.DEFINE_boolean(
|
| 357 |
+
'showprefixforinfo',
|
| 358 |
+
True,
|
| 359 |
+
(
|
| 360 |
+
'If False, do not prepend prefix to info messages '
|
| 361 |
+
"when it's logged to stderr, "
|
| 362 |
+
'--verbosity is set to INFO level, '
|
| 363 |
+
'and python logging is used.'
|
| 364 |
+
),
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
def get_verbosity():
|
| 369 |
+
"""Returns the logging verbosity."""
|
| 370 |
+
return FLAGS['verbosity'].value
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
def set_verbosity(v):
|
| 374 |
+
"""Sets the logging verbosity.
|
| 375 |
+
|
| 376 |
+
Causes all messages of level <= v to be logged,
|
| 377 |
+
and all messages of level > v to be silently discarded.
|
| 378 |
+
|
| 379 |
+
Args:
|
| 380 |
+
v: int|str, the verbosity level as an integer or string. Legal string values
|
| 381 |
+
are those that can be coerced to an integer as well as case-insensitive
|
| 382 |
+
'debug', 'info', 'warning', 'error', and 'fatal'.
|
| 383 |
+
"""
|
| 384 |
+
try:
|
| 385 |
+
new_level = int(v)
|
| 386 |
+
except ValueError:
|
| 387 |
+
new_level = converter.ABSL_NAMES[v.upper()]
|
| 388 |
+
FLAGS.verbosity = new_level
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def set_stderrthreshold(s):
|
| 392 |
+
"""Sets the stderr threshold to the value passed in.
|
| 393 |
+
|
| 394 |
+
Args:
|
| 395 |
+
s: str|int, valid strings values are case-insensitive 'debug',
|
| 396 |
+
'info', 'warning', 'error', and 'fatal'; valid integer values are
|
| 397 |
+
logging.DEBUG|INFO|WARNING|ERROR|FATAL.
|
| 398 |
+
|
| 399 |
+
Raises:
|
| 400 |
+
ValueError: Raised when s is an invalid value.
|
| 401 |
+
"""
|
| 402 |
+
if s in converter.ABSL_LEVELS:
|
| 403 |
+
FLAGS.stderrthreshold = converter.ABSL_LEVELS[s]
|
| 404 |
+
elif isinstance(s, str) and s.upper() in converter.ABSL_NAMES:
|
| 405 |
+
FLAGS.stderrthreshold = s
|
| 406 |
+
else:
|
| 407 |
+
raise ValueError(
|
| 408 |
+
'set_stderrthreshold only accepts integer absl logging level '
|
| 409 |
+
'from -3 to 1, or case-insensitive string values '
|
| 410 |
+
"'debug', 'info', 'warning', 'error', and 'fatal'. "
|
| 411 |
+
'But found "{}" ({}).'.format(s, type(s)))
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
def fatal(msg, *args, **kwargs):
|
| 415 |
+
# type: (Any, Any, Any) -> NoReturn
|
| 416 |
+
"""Logs a fatal message."""
|
| 417 |
+
log(FATAL, msg, *args, **kwargs)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def error(msg, *args, **kwargs):
|
| 421 |
+
"""Logs an error message."""
|
| 422 |
+
log(ERROR, msg, *args, **kwargs)
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
def warning(msg, *args, **kwargs):
|
| 426 |
+
"""Logs a warning message."""
|
| 427 |
+
log(WARNING, msg, *args, **kwargs)
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def warn(msg, *args, **kwargs):
|
| 431 |
+
"""Deprecated, use 'warning' instead."""
|
| 432 |
+
warnings.warn("The 'warn' function is deprecated, use 'warning' instead",
|
| 433 |
+
DeprecationWarning, 2)
|
| 434 |
+
log(WARNING, msg, *args, **kwargs)
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
def info(msg, *args, **kwargs):
|
| 438 |
+
"""Logs an info message."""
|
| 439 |
+
log(INFO, msg, *args, **kwargs)
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def debug(msg, *args, **kwargs):
|
| 443 |
+
"""Logs a debug message."""
|
| 444 |
+
log(DEBUG, msg, *args, **kwargs)
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
def exception(msg, *args, exc_info=True, **kwargs):
|
| 448 |
+
"""Logs an exception, with traceback and message."""
|
| 449 |
+
error(msg, *args, exc_info=exc_info, **kwargs)
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def _fast_stack_trace():
|
| 453 |
+
"""A fast stack trace that gets us the minimal information we need.
|
| 454 |
+
|
| 455 |
+
Compared to using `get_absl_logger().findCaller(stack_info=True)`, this
|
| 456 |
+
function is ~100x faster.
|
| 457 |
+
|
| 458 |
+
Returns:
|
| 459 |
+
A tuple of tuples of (filename, line_number, last_instruction_offset).
|
| 460 |
+
"""
|
| 461 |
+
cur_stack = inspect.currentframe()
|
| 462 |
+
if cur_stack is None or cur_stack.f_back is None:
|
| 463 |
+
return tuple()
|
| 464 |
+
# We drop the first frame, which is this function itself.
|
| 465 |
+
cur_stack = cur_stack.f_back
|
| 466 |
+
call_stack = []
|
| 467 |
+
while cur_stack.f_back:
|
| 468 |
+
cur_stack = cur_stack.f_back
|
| 469 |
+
call_stack.append(
|
| 470 |
+
(cur_stack.f_code.co_filename, cur_stack.f_lineno, cur_stack.f_lasti)
|
| 471 |
+
)
|
| 472 |
+
return tuple(call_stack)
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
# Counter to keep track of number of log entries per token.
|
| 476 |
+
_log_counter_per_token = {}
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def _get_next_log_count_per_token(token):
|
| 480 |
+
"""Wrapper for _log_counter_per_token. Thread-safe.
|
| 481 |
+
|
| 482 |
+
Args:
|
| 483 |
+
token: The token for which to look up the count.
|
| 484 |
+
|
| 485 |
+
Returns:
|
| 486 |
+
The number of times this function has been called with
|
| 487 |
+
*token* as an argument (starting at 0).
|
| 488 |
+
"""
|
| 489 |
+
# Can't use a defaultdict because defaultdict isn't atomic, whereas
|
| 490 |
+
# setdefault is.
|
| 491 |
+
return next(_log_counter_per_token.setdefault(token, itertools.count()))
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def log_every_n(level, msg, n, *args, use_call_stack=False):
|
| 495 |
+
"""Logs ``msg % args`` at level 'level' once per 'n' times.
|
| 496 |
+
|
| 497 |
+
Logs the 1st call, (N+1)st call, (2N+1)st call, etc.
|
| 498 |
+
Not threadsafe.
|
| 499 |
+
|
| 500 |
+
Args:
|
| 501 |
+
level: int, the absl logging level at which to log.
|
| 502 |
+
msg: str, the message to be logged.
|
| 503 |
+
n: int, the number of times this should be called before it is logged.
|
| 504 |
+
*args: The args to be substituted into the msg.
|
| 505 |
+
use_call_stack: bool, whether to include the call stack when counting the
|
| 506 |
+
number of times the message is logged.
|
| 507 |
+
"""
|
| 508 |
+
caller_info = get_absl_logger().findCaller()
|
| 509 |
+
if use_call_stack:
|
| 510 |
+
# To reduce storage costs, we hash the call stack.
|
| 511 |
+
caller_info = (*caller_info[0:3], hash(_fast_stack_trace()))
|
| 512 |
+
count = _get_next_log_count_per_token(caller_info)
|
| 513 |
+
log_if(level, msg, not (count % n), *args)
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
# Keeps track of the last log time of the given token.
|
| 517 |
+
# Note: must be a dict since set/get is atomic in CPython.
|
| 518 |
+
# Note: entries are never released as their number is expected to be low.
|
| 519 |
+
_log_timer_per_token = {}
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def _seconds_have_elapsed(token, num_seconds):
|
| 523 |
+
"""Tests if 'num_seconds' have passed since 'token' was requested.
|
| 524 |
+
|
| 525 |
+
Not strictly thread-safe - may log with the wrong frequency if called
|
| 526 |
+
concurrently from multiple threads. Accuracy depends on resolution of
|
| 527 |
+
'timeit.default_timer()'.
|
| 528 |
+
|
| 529 |
+
Always returns True on the first call for a given 'token'.
|
| 530 |
+
|
| 531 |
+
Args:
|
| 532 |
+
token: The token for which to look up the count.
|
| 533 |
+
num_seconds: The number of seconds to test for.
|
| 534 |
+
|
| 535 |
+
Returns:
|
| 536 |
+
Whether it has been >= 'num_seconds' since 'token' was last requested.
|
| 537 |
+
"""
|
| 538 |
+
now = timeit.default_timer()
|
| 539 |
+
then = _log_timer_per_token.get(token, None)
|
| 540 |
+
if then is None or (now - then) >= num_seconds:
|
| 541 |
+
_log_timer_per_token[token] = now
|
| 542 |
+
return True
|
| 543 |
+
else:
|
| 544 |
+
return False
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def log_every_n_seconds(level, msg, n_seconds, *args, use_call_stack=False):
|
| 548 |
+
"""Logs ``msg % args`` at level ``level`` iff ``n_seconds`` elapsed since last call.
|
| 549 |
+
|
| 550 |
+
Logs the first call, logs subsequent calls if 'n' seconds have elapsed since
|
| 551 |
+
the last logging call from the same call site (file + line). Not thread-safe.
|
| 552 |
+
|
| 553 |
+
Args:
|
| 554 |
+
level: int, the absl logging level at which to log.
|
| 555 |
+
msg: str, the message to be logged.
|
| 556 |
+
n_seconds: float or int, seconds which should elapse before logging again.
|
| 557 |
+
*args: The args to be substituted into the msg.
|
| 558 |
+
use_call_stack: bool, whether to include the call stack when counting the
|
| 559 |
+
number of times the message is logged.
|
| 560 |
+
"""
|
| 561 |
+
caller_info = get_absl_logger().findCaller()
|
| 562 |
+
if use_call_stack:
|
| 563 |
+
# To reduce storage costs, we hash the call stack.
|
| 564 |
+
caller_info = (*caller_info[0:3], hash(_fast_stack_trace()))
|
| 565 |
+
should_log = _seconds_have_elapsed(caller_info, n_seconds)
|
| 566 |
+
log_if(level, msg, should_log, *args)
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
def log_first_n(level, msg, n, *args, use_call_stack=False):
|
| 570 |
+
"""Logs ``msg % args`` at level ``level`` only first ``n`` times.
|
| 571 |
+
|
| 572 |
+
Not threadsafe.
|
| 573 |
+
|
| 574 |
+
Args:
|
| 575 |
+
level: int, the absl logging level at which to log.
|
| 576 |
+
msg: str, the message to be logged.
|
| 577 |
+
n: int, the maximal number of times the message is logged.
|
| 578 |
+
*args: The args to be substituted into the msg.
|
| 579 |
+
use_call_stack: bool, whether to include the call stack when counting the
|
| 580 |
+
number of times the message is logged.
|
| 581 |
+
"""
|
| 582 |
+
caller_info = get_absl_logger().findCaller()
|
| 583 |
+
if use_call_stack:
|
| 584 |
+
# To reduce storage costs, we hash the call stack.
|
| 585 |
+
caller_info = (*caller_info[0:3], hash(_fast_stack_trace()))
|
| 586 |
+
count = _get_next_log_count_per_token(caller_info)
|
| 587 |
+
log_if(level, msg, count < n, *args)
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
def log_if(level, msg, condition, *args):
|
| 591 |
+
"""Logs ``msg % args`` at level ``level`` only if condition is fulfilled."""
|
| 592 |
+
if condition:
|
| 593 |
+
log(level, msg, *args)
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def log(level, msg, *args, **kwargs):
|
| 597 |
+
"""Logs ``msg % args`` at absl logging level ``level``.
|
| 598 |
+
|
| 599 |
+
If no args are given just print msg, ignoring any interpolation specifiers.
|
| 600 |
+
|
| 601 |
+
Args:
|
| 602 |
+
level: int, the absl logging level at which to log the message
|
| 603 |
+
(logging.DEBUG|INFO|WARNING|ERROR|FATAL). While some C++ verbose logging
|
| 604 |
+
level constants are also supported, callers should prefer explicit
|
| 605 |
+
logging.vlog() calls for such purpose.
|
| 606 |
+
|
| 607 |
+
msg: str, the message to be logged.
|
| 608 |
+
*args: The args to be substituted into the msg.
|
| 609 |
+
**kwargs: May contain exc_info to add exception traceback to message.
|
| 610 |
+
"""
|
| 611 |
+
if level > converter.ABSL_DEBUG:
|
| 612 |
+
# Even though this function supports level that is greater than 1, users
|
| 613 |
+
# should use logging.vlog instead for such cases.
|
| 614 |
+
# Treat this as vlog, 1 is equivalent to DEBUG.
|
| 615 |
+
standard_level = converter.STANDARD_DEBUG - (level - 1)
|
| 616 |
+
else:
|
| 617 |
+
if level < converter.ABSL_FATAL:
|
| 618 |
+
level = converter.ABSL_FATAL
|
| 619 |
+
standard_level = converter.absl_to_standard(level)
|
| 620 |
+
|
| 621 |
+
# Match standard logging's behavior. Before use_absl_handler() and
|
| 622 |
+
# logging is configured, there is no handler attached on _absl_logger nor
|
| 623 |
+
# logging.root. So logs go no where.
|
| 624 |
+
if not logging.root.handlers:
|
| 625 |
+
logging.basicConfig()
|
| 626 |
+
|
| 627 |
+
_absl_logger.log(standard_level, msg, *args, **kwargs)
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
def vlog(level, msg, *args, **kwargs):
|
| 631 |
+
"""Log ``msg % args`` at C++ vlog level ``level``.
|
| 632 |
+
|
| 633 |
+
Args:
|
| 634 |
+
level: int, the C++ verbose logging level at which to log the message,
|
| 635 |
+
e.g. 1, 2, 3, 4... While absl level constants are also supported,
|
| 636 |
+
callers should prefer logging.log|debug|info|... calls for such purpose.
|
| 637 |
+
msg: str, the message to be logged.
|
| 638 |
+
*args: The args to be substituted into the msg.
|
| 639 |
+
**kwargs: May contain exc_info to add exception traceback to message.
|
| 640 |
+
"""
|
| 641 |
+
log(level, msg, *args, **kwargs)
|
| 642 |
+
|
| 643 |
+
|
| 644 |
+
def vlog_is_on(level):
|
| 645 |
+
"""Checks if vlog is enabled for the given level in caller's source file.
|
| 646 |
+
|
| 647 |
+
Args:
|
| 648 |
+
level: int, the C++ verbose logging level at which to log the message,
|
| 649 |
+
e.g. 1, 2, 3, 4... While absl level constants are also supported,
|
| 650 |
+
callers should prefer level_debug|level_info|... calls for
|
| 651 |
+
checking those.
|
| 652 |
+
|
| 653 |
+
Returns:
|
| 654 |
+
True if logging is turned on for that level.
|
| 655 |
+
"""
|
| 656 |
+
|
| 657 |
+
if level > converter.ABSL_DEBUG:
|
| 658 |
+
# Even though this function supports level that is greater than 1, users
|
| 659 |
+
# should use logging.vlog instead for such cases.
|
| 660 |
+
# Treat this as vlog, 1 is equivalent to DEBUG.
|
| 661 |
+
standard_level = converter.STANDARD_DEBUG - (level - 1)
|
| 662 |
+
else:
|
| 663 |
+
if level < converter.ABSL_FATAL:
|
| 664 |
+
level = converter.ABSL_FATAL
|
| 665 |
+
standard_level = converter.absl_to_standard(level)
|
| 666 |
+
return _absl_logger.isEnabledFor(standard_level)
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
def flush():
|
| 670 |
+
"""Flushes all log files."""
|
| 671 |
+
get_absl_handler().flush()
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
def level_debug():
|
| 675 |
+
"""Returns True if debug logging is turned on."""
|
| 676 |
+
return get_verbosity() >= DEBUG
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
def level_info():
|
| 680 |
+
"""Returns True if info logging is turned on."""
|
| 681 |
+
return get_verbosity() >= INFO
|
| 682 |
+
|
| 683 |
+
|
| 684 |
+
def level_warning():
|
| 685 |
+
"""Returns True if warning logging is turned on."""
|
| 686 |
+
return get_verbosity() >= WARNING
|
| 687 |
+
|
| 688 |
+
|
| 689 |
+
level_warn = level_warning # Deprecated function.
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
def level_error():
|
| 693 |
+
"""Returns True if error logging is turned on."""
|
| 694 |
+
return get_verbosity() >= ERROR
|
| 695 |
+
|
| 696 |
+
|
| 697 |
+
def get_log_file_name(level=INFO):
|
| 698 |
+
"""Returns the name of the log file.
|
| 699 |
+
|
| 700 |
+
For Python logging, only one file is used and level is ignored. And it returns
|
| 701 |
+
empty string if it logs to stderr/stdout or the log stream has no `name`
|
| 702 |
+
attribute.
|
| 703 |
+
|
| 704 |
+
Args:
|
| 705 |
+
level: int, the absl.logging level.
|
| 706 |
+
|
| 707 |
+
Raises:
|
| 708 |
+
ValueError: Raised when `level` has an invalid value.
|
| 709 |
+
"""
|
| 710 |
+
if level not in converter.ABSL_LEVELS:
|
| 711 |
+
raise ValueError(f'Invalid absl.logging level {level}')
|
| 712 |
+
stream = get_absl_handler().python_handler.stream
|
| 713 |
+
if (stream == sys.stderr or stream == sys.stdout or
|
| 714 |
+
not hasattr(stream, 'name')):
|
| 715 |
+
return ''
|
| 716 |
+
else:
|
| 717 |
+
return stream.name
|
| 718 |
+
|
| 719 |
+
|
| 720 |
+
def find_log_dir_and_names(program_name=None, log_dir=None):
|
| 721 |
+
"""Computes the directory and filename prefix for log file.
|
| 722 |
+
|
| 723 |
+
Args:
|
| 724 |
+
program_name: str|None, the filename part of the path to the program that is
|
| 725 |
+
running without its extension. e.g: if your program is called
|
| 726 |
+
``usr/bin/foobar.py`` this method should probably be called with
|
| 727 |
+
``program_name='foobar`` However, this is just a convention, you can pass
|
| 728 |
+
in any string you want, and it will be used as part of the log filename.
|
| 729 |
+
If you don't pass in anything, the default behavior is as described in the
|
| 730 |
+
example. In python standard logging mode, the program_name will be
|
| 731 |
+
prepended with ``py_`` if it is the ``program_name`` argument is omitted.
|
| 732 |
+
log_dir: str|None, the desired log directory.
|
| 733 |
+
|
| 734 |
+
Returns:
|
| 735 |
+
(log_dir, file_prefix, symlink_prefix)
|
| 736 |
+
|
| 737 |
+
Raises:
|
| 738 |
+
FileNotFoundError: raised when it cannot find a log directory.
|
| 739 |
+
"""
|
| 740 |
+
if not program_name:
|
| 741 |
+
# Strip the extension (foobar.par becomes foobar, and
|
| 742 |
+
# fubar.py becomes fubar). We do this so that the log
|
| 743 |
+
# file names are similar to C++ log file names.
|
| 744 |
+
program_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
|
| 745 |
+
|
| 746 |
+
# Prepend py_ to files so that python code gets a unique file, and
|
| 747 |
+
# so that C++ libraries do not try to write to the same log files as us.
|
| 748 |
+
program_name = 'py_%s' % program_name
|
| 749 |
+
|
| 750 |
+
actual_log_dir = find_log_dir(log_dir=log_dir)
|
| 751 |
+
|
| 752 |
+
try:
|
| 753 |
+
username = getpass.getuser()
|
| 754 |
+
except KeyError:
|
| 755 |
+
# This can happen, e.g. when running under docker w/o passwd file.
|
| 756 |
+
if hasattr(os, 'getuid'):
|
| 757 |
+
# Windows doesn't have os.getuid
|
| 758 |
+
username = str(os.getuid())
|
| 759 |
+
else:
|
| 760 |
+
username = 'unknown'
|
| 761 |
+
hostname = socket.gethostname()
|
| 762 |
+
file_prefix = '%s.%s.%s.log' % (program_name, hostname, username)
|
| 763 |
+
|
| 764 |
+
return actual_log_dir, file_prefix, program_name
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
def find_log_dir(log_dir=None):
|
| 768 |
+
"""Returns the most suitable directory to put log files into.
|
| 769 |
+
|
| 770 |
+
Args:
|
| 771 |
+
log_dir: str|None, if specified, the logfile(s) will be created in that
|
| 772 |
+
directory. Otherwise if the --log_dir command-line flag is provided, the
|
| 773 |
+
logfile will be created in that directory. Otherwise the logfile will be
|
| 774 |
+
created in a standard location.
|
| 775 |
+
|
| 776 |
+
Raises:
|
| 777 |
+
FileNotFoundError: raised when it cannot find a log directory.
|
| 778 |
+
"""
|
| 779 |
+
# Get a list of possible log dirs (will try to use them in order).
|
| 780 |
+
# NOTE: Google's internal implementation has a special handling for Google
|
| 781 |
+
# machines, which uses a list of directories. Hence the following uses `dirs`
|
| 782 |
+
# instead of a single directory.
|
| 783 |
+
if log_dir:
|
| 784 |
+
# log_dir was explicitly specified as an arg, so use it and it alone.
|
| 785 |
+
dirs = [log_dir]
|
| 786 |
+
elif FLAGS['log_dir'].value:
|
| 787 |
+
# log_dir flag was provided, so use it and it alone (this mimics the
|
| 788 |
+
# behavior of the same flag in logging.cc).
|
| 789 |
+
dirs = [FLAGS['log_dir'].value]
|
| 790 |
+
else:
|
| 791 |
+
dirs = [tempfile.gettempdir()]
|
| 792 |
+
|
| 793 |
+
# Find the first usable log dir.
|
| 794 |
+
for d in dirs:
|
| 795 |
+
if os.path.isdir(d) and os.access(d, os.W_OK):
|
| 796 |
+
return d
|
| 797 |
+
raise FileNotFoundError(
|
| 798 |
+
"Can't find a writable directory for logs, tried %s" % dirs)
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
def get_absl_log_prefix(record):
|
| 802 |
+
"""Returns the absl log prefix for the log record.
|
| 803 |
+
|
| 804 |
+
Args:
|
| 805 |
+
record: logging.LogRecord, the record to get prefix for.
|
| 806 |
+
"""
|
| 807 |
+
created_tuple = time.localtime(record.created)
|
| 808 |
+
created_microsecond = int(record.created % 1.0 * 1e6)
|
| 809 |
+
|
| 810 |
+
critical_prefix = ''
|
| 811 |
+
level = record.levelno
|
| 812 |
+
if _is_non_absl_fatal_record(record):
|
| 813 |
+
# When the level is FATAL, but not logged from absl, lower the level so
|
| 814 |
+
# it's treated as ERROR.
|
| 815 |
+
level = logging.ERROR
|
| 816 |
+
critical_prefix = _CRITICAL_PREFIX
|
| 817 |
+
severity = converter.get_initial_for_level(level)
|
| 818 |
+
|
| 819 |
+
return '%c%02d%02d %02d:%02d:%02d.%06d %5d %s:%d] %s' % (
|
| 820 |
+
severity,
|
| 821 |
+
created_tuple.tm_mon,
|
| 822 |
+
created_tuple.tm_mday,
|
| 823 |
+
created_tuple.tm_hour,
|
| 824 |
+
created_tuple.tm_min,
|
| 825 |
+
created_tuple.tm_sec,
|
| 826 |
+
created_microsecond,
|
| 827 |
+
_get_thread_id(),
|
| 828 |
+
record.filename,
|
| 829 |
+
record.lineno,
|
| 830 |
+
critical_prefix)
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
def skip_log_prefix(func):
|
| 834 |
+
"""Skips reporting the prefix of a given function or name by :class:`~absl.logging.ABSLLogger`.
|
| 835 |
+
|
| 836 |
+
This is a convenience wrapper function / decorator for
|
| 837 |
+
:meth:`~absl.logging.ABSLLogger.register_frame_to_skip`.
|
| 838 |
+
|
| 839 |
+
If a callable function is provided, only that function will be skipped.
|
| 840 |
+
If a function name is provided, all functions with the same name in the
|
| 841 |
+
file that this is called in will be skipped.
|
| 842 |
+
|
| 843 |
+
This can be used as a decorator of the intended function to be skipped.
|
| 844 |
+
|
| 845 |
+
Args:
|
| 846 |
+
func: Callable function or its name as a string.
|
| 847 |
+
|
| 848 |
+
Returns:
|
| 849 |
+
func (the input, unchanged).
|
| 850 |
+
|
| 851 |
+
Raises:
|
| 852 |
+
ValueError: The input is callable but does not have a function code object.
|
| 853 |
+
TypeError: The input is neither callable nor a string.
|
| 854 |
+
"""
|
| 855 |
+
if callable(func):
|
| 856 |
+
func_code = getattr(func, '__code__', None)
|
| 857 |
+
if func_code is None:
|
| 858 |
+
raise ValueError('Input callable does not have a function code object.')
|
| 859 |
+
file_name = func_code.co_filename
|
| 860 |
+
func_name = func_code.co_name
|
| 861 |
+
func_lineno = func_code.co_firstlineno
|
| 862 |
+
elif isinstance(func, str):
|
| 863 |
+
file_name = get_absl_logger().findCaller()[0]
|
| 864 |
+
func_name = func
|
| 865 |
+
func_lineno = None
|
| 866 |
+
else:
|
| 867 |
+
raise TypeError('Input is neither callable nor a string.')
|
| 868 |
+
ABSLLogger.register_frame_to_skip(file_name, func_name, func_lineno)
|
| 869 |
+
return func
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
def _is_non_absl_fatal_record(log_record):
|
| 873 |
+
return (log_record.levelno >= logging.FATAL and
|
| 874 |
+
not log_record.__dict__.get(_ABSL_LOG_FATAL, False))
|
| 875 |
+
|
| 876 |
+
|
| 877 |
+
def _is_absl_fatal_record(log_record):
|
| 878 |
+
return (log_record.levelno >= logging.FATAL and
|
| 879 |
+
log_record.__dict__.get(_ABSL_LOG_FATAL, False))
|
| 880 |
+
|
| 881 |
+
|
| 882 |
+
# Indicates if we still need to warn about pre-init logs going to stderr.
|
| 883 |
+
_warn_preinit_stderr = True
|
| 884 |
+
|
| 885 |
+
|
| 886 |
+
class PythonHandler(logging.StreamHandler):
|
| 887 |
+
"""The handler class used by Abseil Python logging implementation."""
|
| 888 |
+
|
| 889 |
+
def __init__(self, stream=None, formatter=None):
|
| 890 |
+
super().__init__(stream)
|
| 891 |
+
self.setFormatter(formatter or PythonFormatter())
|
| 892 |
+
|
| 893 |
+
def start_logging_to_file(self, program_name=None, log_dir=None):
|
| 894 |
+
"""Starts logging messages to files instead of standard error."""
|
| 895 |
+
FLAGS.logtostderr = False
|
| 896 |
+
|
| 897 |
+
actual_log_dir, file_prefix, symlink_prefix = find_log_dir_and_names(
|
| 898 |
+
program_name=program_name, log_dir=log_dir)
|
| 899 |
+
|
| 900 |
+
basename = '%s.INFO.%s.%d' % (
|
| 901 |
+
file_prefix,
|
| 902 |
+
time.strftime('%Y%m%d-%H%M%S', time.localtime(time.time())),
|
| 903 |
+
os.getpid())
|
| 904 |
+
filename = os.path.join(actual_log_dir, basename)
|
| 905 |
+
|
| 906 |
+
self.stream = open(filename, 'a', encoding='utf-8')
|
| 907 |
+
|
| 908 |
+
# os.symlink is not available on Windows Python 2.
|
| 909 |
+
if getattr(os, 'symlink', None):
|
| 910 |
+
# Create a symlink to the log file with a canonical name.
|
| 911 |
+
symlink = os.path.join(actual_log_dir, symlink_prefix + '.INFO')
|
| 912 |
+
try:
|
| 913 |
+
if os.path.islink(symlink):
|
| 914 |
+
os.unlink(symlink)
|
| 915 |
+
os.symlink(os.path.basename(filename), symlink)
|
| 916 |
+
except OSError:
|
| 917 |
+
# If it fails, we're sad but it's no error. Commonly, this
|
| 918 |
+
# fails because the symlink was created by another user and so
|
| 919 |
+
# we can't modify it
|
| 920 |
+
pass
|
| 921 |
+
|
| 922 |
+
def use_absl_log_file(self, program_name=None, log_dir=None):
|
| 923 |
+
"""Conditionally logs to files, based on --logtostderr."""
|
| 924 |
+
if FLAGS['logtostderr'].value:
|
| 925 |
+
self.stream = sys.stderr
|
| 926 |
+
else:
|
| 927 |
+
self.start_logging_to_file(program_name=program_name, log_dir=log_dir)
|
| 928 |
+
|
| 929 |
+
def flush(self):
|
| 930 |
+
"""Flushes all log files."""
|
| 931 |
+
self.acquire()
|
| 932 |
+
try:
|
| 933 |
+
if self.stream and hasattr(self.stream, 'flush'):
|
| 934 |
+
self.stream.flush()
|
| 935 |
+
except (OSError, ValueError):
|
| 936 |
+
# A ValueError is thrown if we try to flush a closed file.
|
| 937 |
+
pass
|
| 938 |
+
finally:
|
| 939 |
+
self.release()
|
| 940 |
+
|
| 941 |
+
def _log_to_stderr(self, record):
|
| 942 |
+
"""Emits the record to stderr.
|
| 943 |
+
|
| 944 |
+
This temporarily sets the handler stream to stderr, calls
|
| 945 |
+
StreamHandler.emit, then reverts the stream back.
|
| 946 |
+
|
| 947 |
+
Args:
|
| 948 |
+
record: logging.LogRecord, the record to log.
|
| 949 |
+
"""
|
| 950 |
+
# emit() is protected by a lock in logging.Handler, so we don't need to
|
| 951 |
+
# protect here again.
|
| 952 |
+
old_stream = self.stream
|
| 953 |
+
self.stream = sys.stderr
|
| 954 |
+
try:
|
| 955 |
+
super().emit(record)
|
| 956 |
+
finally:
|
| 957 |
+
self.stream = old_stream
|
| 958 |
+
|
| 959 |
+
def emit(self, record):
|
| 960 |
+
"""Prints a record out to some streams.
|
| 961 |
+
|
| 962 |
+
1. If ``FLAGS.logtostderr`` is set, it will print to ``sys.stderr`` ONLY.
|
| 963 |
+
2. If ``FLAGS.alsologtostderr`` is set, it will print to ``sys.stderr``.
|
| 964 |
+
3. If ``FLAGS.logtostderr`` is not set, it will log to the stream
|
| 965 |
+
associated with the current thread.
|
| 966 |
+
|
| 967 |
+
Args:
|
| 968 |
+
record: :class:`logging.LogRecord`, the record to emit.
|
| 969 |
+
"""
|
| 970 |
+
# People occasionally call logging functions at import time before
|
| 971 |
+
# our flags may have even been defined yet, let alone even parsed, as we
|
| 972 |
+
# rely on the C++ side to define some flags for us and app init to
|
| 973 |
+
# deal with parsing. Match the C++ library behavior of notify and emit
|
| 974 |
+
# such messages to stderr. It encourages people to clean-up and does
|
| 975 |
+
# not hide the message.
|
| 976 |
+
level = record.levelno
|
| 977 |
+
if not FLAGS.is_parsed(): # Also implies "before flag has been defined".
|
| 978 |
+
global _warn_preinit_stderr
|
| 979 |
+
if _warn_preinit_stderr:
|
| 980 |
+
sys.stderr.write(
|
| 981 |
+
'WARNING: Logging before flag parsing goes to stderr.\n')
|
| 982 |
+
_warn_preinit_stderr = False
|
| 983 |
+
self._log_to_stderr(record)
|
| 984 |
+
elif FLAGS['logtostderr'].value:
|
| 985 |
+
self._log_to_stderr(record)
|
| 986 |
+
else:
|
| 987 |
+
super().emit(record)
|
| 988 |
+
stderr_threshold = converter.string_to_standard(
|
| 989 |
+
FLAGS['stderrthreshold'].value)
|
| 990 |
+
if ((FLAGS['alsologtostderr'].value or level >= stderr_threshold) and
|
| 991 |
+
self.stream != sys.stderr):
|
| 992 |
+
self._log_to_stderr(record)
|
| 993 |
+
# Die when the record is created from ABSLLogger and level is FATAL.
|
| 994 |
+
if _is_absl_fatal_record(record):
|
| 995 |
+
self.flush() # Flush the log before dying.
|
| 996 |
+
|
| 997 |
+
# In threaded python, sys.exit() from a non-main thread only
|
| 998 |
+
# exits the thread in question.
|
| 999 |
+
os.abort()
|
| 1000 |
+
|
| 1001 |
+
def close(self):
|
| 1002 |
+
"""Closes the stream to which we are writing."""
|
| 1003 |
+
self.acquire()
|
| 1004 |
+
try:
|
| 1005 |
+
self.flush()
|
| 1006 |
+
try:
|
| 1007 |
+
# Do not close the stream if it's sys.stderr|stdout. They may be
|
| 1008 |
+
# redirected or overridden to files, which should be managed by users
|
| 1009 |
+
# explicitly.
|
| 1010 |
+
user_managed = sys.stderr, sys.stdout, sys.__stderr__, sys.__stdout__
|
| 1011 |
+
if self.stream not in user_managed and (
|
| 1012 |
+
not hasattr(self.stream, 'isatty') or not self.stream.isatty()):
|
| 1013 |
+
self.stream.close()
|
| 1014 |
+
except ValueError:
|
| 1015 |
+
# A ValueError is thrown if we try to run isatty() on a closed file.
|
| 1016 |
+
pass
|
| 1017 |
+
super().close()
|
| 1018 |
+
finally:
|
| 1019 |
+
self.release()
|
| 1020 |
+
|
| 1021 |
+
|
| 1022 |
+
class ABSLHandler(logging.Handler):
|
| 1023 |
+
"""Abseil Python logging module's log handler."""
|
| 1024 |
+
|
| 1025 |
+
def __init__(self, python_logging_formatter):
|
| 1026 |
+
super().__init__()
|
| 1027 |
+
|
| 1028 |
+
self._python_handler = PythonHandler(formatter=python_logging_formatter)
|
| 1029 |
+
self.activate_python_handler()
|
| 1030 |
+
|
| 1031 |
+
def format(self, record):
|
| 1032 |
+
return self._current_handler.format(record)
|
| 1033 |
+
|
| 1034 |
+
def setFormatter(self, fmt):
|
| 1035 |
+
self._current_handler.setFormatter(fmt)
|
| 1036 |
+
|
| 1037 |
+
def emit(self, record):
|
| 1038 |
+
self._current_handler.emit(record)
|
| 1039 |
+
|
| 1040 |
+
def flush(self):
|
| 1041 |
+
self._current_handler.flush()
|
| 1042 |
+
|
| 1043 |
+
def close(self):
|
| 1044 |
+
super().close()
|
| 1045 |
+
self._current_handler.close()
|
| 1046 |
+
|
| 1047 |
+
def handle(self, record):
|
| 1048 |
+
rv = self.filter(record)
|
| 1049 |
+
if rv:
|
| 1050 |
+
return self._current_handler.handle(record)
|
| 1051 |
+
return rv
|
| 1052 |
+
|
| 1053 |
+
@property
|
| 1054 |
+
def python_handler(self):
|
| 1055 |
+
return self._python_handler
|
| 1056 |
+
|
| 1057 |
+
def activate_python_handler(self):
|
| 1058 |
+
"""Uses the Python logging handler as the current logging handler."""
|
| 1059 |
+
self._current_handler = self._python_handler
|
| 1060 |
+
|
| 1061 |
+
def use_absl_log_file(self, program_name=None, log_dir=None):
|
| 1062 |
+
self._current_handler.use_absl_log_file(program_name, log_dir)
|
| 1063 |
+
|
| 1064 |
+
def start_logging_to_file(self, program_name=None, log_dir=None):
|
| 1065 |
+
self._current_handler.start_logging_to_file(program_name, log_dir)
|
| 1066 |
+
|
| 1067 |
+
|
| 1068 |
+
class PythonFormatter(logging.Formatter):
|
| 1069 |
+
"""Formatter class used by :class:`~absl.logging.PythonHandler`."""
|
| 1070 |
+
|
| 1071 |
+
def format(self, record):
|
| 1072 |
+
"""Appends the message from the record to the results of the prefix.
|
| 1073 |
+
|
| 1074 |
+
Args:
|
| 1075 |
+
record: logging.LogRecord, the record to be formatted.
|
| 1076 |
+
|
| 1077 |
+
Returns:
|
| 1078 |
+
The formatted string representing the record.
|
| 1079 |
+
"""
|
| 1080 |
+
if (not FLAGS['showprefixforinfo'].value and
|
| 1081 |
+
FLAGS['verbosity'].value == converter.ABSL_INFO and
|
| 1082 |
+
record.levelno == logging.INFO and
|
| 1083 |
+
_absl_handler.python_handler.stream == sys.stderr):
|
| 1084 |
+
prefix = ''
|
| 1085 |
+
else:
|
| 1086 |
+
prefix = get_absl_log_prefix(record)
|
| 1087 |
+
return prefix + super().format(record)
|
| 1088 |
+
|
| 1089 |
+
|
| 1090 |
+
class ABSLLogger(logging.getLoggerClass()):
|
| 1091 |
+
"""A logger that will create LogRecords while skipping some stack frames.
|
| 1092 |
+
|
| 1093 |
+
This class maintains an internal list of filenames and method names
|
| 1094 |
+
for use when determining who called the currently executing stack
|
| 1095 |
+
frame. Any method names from specific source files are skipped when
|
| 1096 |
+
walking backwards through the stack.
|
| 1097 |
+
|
| 1098 |
+
Client code should use the register_frame_to_skip method to let the
|
| 1099 |
+
ABSLLogger know which method from which file should be
|
| 1100 |
+
excluded from the walk backwards through the stack.
|
| 1101 |
+
"""
|
| 1102 |
+
_frames_to_skip = set()
|
| 1103 |
+
|
| 1104 |
+
def findCaller(self, stack_info=False, stacklevel=1):
|
| 1105 |
+
"""Finds the frame of the calling method on the stack.
|
| 1106 |
+
|
| 1107 |
+
This method skips any frames registered with the
|
| 1108 |
+
ABSLLogger and any methods from this file, and whatever
|
| 1109 |
+
method is currently being used to generate the prefix for the log
|
| 1110 |
+
line. Then it returns the file name, line number, and method name
|
| 1111 |
+
of the calling method. An optional fourth item may be returned,
|
| 1112 |
+
callers who only need things from the first three are advised to
|
| 1113 |
+
always slice or index the result rather than using direct unpacking
|
| 1114 |
+
assignment.
|
| 1115 |
+
|
| 1116 |
+
Args:
|
| 1117 |
+
stack_info: bool, when True, include the stack trace as a fourth item
|
| 1118 |
+
returned. On Python 3 there are always four items returned - the fourth
|
| 1119 |
+
will be None when this is False. On Python 2 the stdlib base class API
|
| 1120 |
+
only returns three items. We do the same when this new parameter is
|
| 1121 |
+
unspecified or False for compatibility.
|
| 1122 |
+
stacklevel: int, if greater than 1, that number of frames will be skipped.
|
| 1123 |
+
|
| 1124 |
+
Returns:
|
| 1125 |
+
(filename, lineno, methodname[, sinfo]) of the calling method.
|
| 1126 |
+
"""
|
| 1127 |
+
f_to_skip = ABSLLogger._frames_to_skip
|
| 1128 |
+
# Use sys._getframe(2) instead of logging.currentframe(), it's slightly
|
| 1129 |
+
# faster because there is one less frame to traverse.
|
| 1130 |
+
frame = sys._getframe(2) # pylint: disable=protected-access
|
| 1131 |
+
frame_to_return = None
|
| 1132 |
+
|
| 1133 |
+
while frame:
|
| 1134 |
+
code = frame.f_code
|
| 1135 |
+
if (_LOGGING_FILE_PREFIX not in code.co_filename and
|
| 1136 |
+
(code.co_filename, code.co_name,
|
| 1137 |
+
code.co_firstlineno) not in f_to_skip and
|
| 1138 |
+
(code.co_filename, code.co_name) not in f_to_skip):
|
| 1139 |
+
frame_to_return = frame
|
| 1140 |
+
stacklevel -= 1
|
| 1141 |
+
if stacklevel <= 0:
|
| 1142 |
+
break
|
| 1143 |
+
frame = frame.f_back
|
| 1144 |
+
|
| 1145 |
+
if frame_to_return is not None:
|
| 1146 |
+
sinfo = None
|
| 1147 |
+
if stack_info:
|
| 1148 |
+
out = io.StringIO()
|
| 1149 |
+
out.write('Stack (most recent call last):\n')
|
| 1150 |
+
traceback.print_stack(frame, file=out)
|
| 1151 |
+
sinfo = out.getvalue().rstrip('\n')
|
| 1152 |
+
return (
|
| 1153 |
+
frame_to_return.f_code.co_filename,
|
| 1154 |
+
frame_to_return.f_lineno,
|
| 1155 |
+
frame_to_return.f_code.co_name,
|
| 1156 |
+
sinfo,
|
| 1157 |
+
)
|
| 1158 |
+
|
| 1159 |
+
return None
|
| 1160 |
+
|
| 1161 |
+
def critical(self, msg, *args, **kwargs):
|
| 1162 |
+
"""Logs ``msg % args`` with severity ``CRITICAL``."""
|
| 1163 |
+
self.log(logging.CRITICAL, msg, *args, **kwargs)
|
| 1164 |
+
|
| 1165 |
+
def fatal(self, msg, *args, **kwargs):
|
| 1166 |
+
"""Logs ``msg % args`` with severity ``FATAL``."""
|
| 1167 |
+
self.log(logging.FATAL, msg, *args, **kwargs)
|
| 1168 |
+
|
| 1169 |
+
def error(self, msg, *args, **kwargs):
|
| 1170 |
+
"""Logs ``msg % args`` with severity ``ERROR``."""
|
| 1171 |
+
self.log(logging.ERROR, msg, *args, **kwargs)
|
| 1172 |
+
|
| 1173 |
+
def warn(self, msg, *args, **kwargs):
|
| 1174 |
+
"""Logs ``msg % args`` with severity ``WARN``."""
|
| 1175 |
+
warnings.warn("The 'warn' method is deprecated, use 'warning' instead",
|
| 1176 |
+
DeprecationWarning, 2)
|
| 1177 |
+
self.log(logging.WARN, msg, *args, **kwargs)
|
| 1178 |
+
|
| 1179 |
+
def warning(self, msg, *args, **kwargs):
|
| 1180 |
+
"""Logs ``msg % args`` with severity ``WARNING``."""
|
| 1181 |
+
self.log(logging.WARNING, msg, *args, **kwargs)
|
| 1182 |
+
|
| 1183 |
+
def info(self, msg, *args, **kwargs):
|
| 1184 |
+
"""Logs ``msg % args`` with severity ``INFO``."""
|
| 1185 |
+
self.log(logging.INFO, msg, *args, **kwargs)
|
| 1186 |
+
|
| 1187 |
+
def debug(self, msg, *args, **kwargs):
|
| 1188 |
+
"""Logs ``msg % args`` with severity ``DEBUG``."""
|
| 1189 |
+
self.log(logging.DEBUG, msg, *args, **kwargs)
|
| 1190 |
+
|
| 1191 |
+
def log(self, level, msg, *args, **kwargs):
|
| 1192 |
+
"""Logs a message at a cetain level substituting in the supplied arguments.
|
| 1193 |
+
|
| 1194 |
+
This method behaves differently in python and c++ modes.
|
| 1195 |
+
|
| 1196 |
+
Args:
|
| 1197 |
+
level: int, the standard logging level at which to log the message.
|
| 1198 |
+
msg: str, the text of the message to log.
|
| 1199 |
+
*args: The arguments to substitute in the message.
|
| 1200 |
+
**kwargs: The keyword arguments to substitute in the message.
|
| 1201 |
+
"""
|
| 1202 |
+
if level >= logging.FATAL:
|
| 1203 |
+
# Add property to the LogRecord created by this logger.
|
| 1204 |
+
# This will be used by the ABSLHandler to determine whether it should
|
| 1205 |
+
# treat CRITICAL/FATAL logs as really FATAL.
|
| 1206 |
+
extra = kwargs.setdefault('extra', {})
|
| 1207 |
+
extra[_ABSL_LOG_FATAL] = True
|
| 1208 |
+
super().log(level, msg, *args, **kwargs)
|
| 1209 |
+
|
| 1210 |
+
def handle(self, record):
|
| 1211 |
+
"""Calls handlers without checking ``Logger.disabled``.
|
| 1212 |
+
|
| 1213 |
+
Non-root loggers are set to disabled after setup with :func:`logging.config`
|
| 1214 |
+
if it's not explicitly specified. Historically, absl logging will not be
|
| 1215 |
+
disabled by that. To maintaining this behavior, this function skips
|
| 1216 |
+
checking the ``Logger.disabled`` bit.
|
| 1217 |
+
|
| 1218 |
+
This logger can still be disabled by adding a filter that filters out
|
| 1219 |
+
everything.
|
| 1220 |
+
|
| 1221 |
+
Args:
|
| 1222 |
+
record: logging.LogRecord, the record to handle.
|
| 1223 |
+
"""
|
| 1224 |
+
if self.filter(record):
|
| 1225 |
+
self.callHandlers(record)
|
| 1226 |
+
|
| 1227 |
+
@classmethod
|
| 1228 |
+
def register_frame_to_skip(cls, file_name, function_name, line_number=None):
|
| 1229 |
+
"""Registers a function name to skip when walking the stack.
|
| 1230 |
+
|
| 1231 |
+
The :class:`~absl.logging.ABSLLogger` sometimes skips method calls on the
|
| 1232 |
+
stack to make the log messages meaningful in their appropriate context.
|
| 1233 |
+
This method registers a function from a particular file as one
|
| 1234 |
+
which should be skipped.
|
| 1235 |
+
|
| 1236 |
+
Args:
|
| 1237 |
+
file_name: str, the name of the file that contains the function.
|
| 1238 |
+
function_name: str, the name of the function to skip.
|
| 1239 |
+
line_number: int, if provided, only the function with this starting line
|
| 1240 |
+
number will be skipped. Otherwise, all functions with the same name
|
| 1241 |
+
in the file will be skipped.
|
| 1242 |
+
"""
|
| 1243 |
+
if line_number is not None:
|
| 1244 |
+
cls._frames_to_skip.add((file_name, function_name, line_number))
|
| 1245 |
+
else:
|
| 1246 |
+
cls._frames_to_skip.add((file_name, function_name))
|
| 1247 |
+
|
| 1248 |
+
|
| 1249 |
+
def _get_thread_id():
|
| 1250 |
+
"""Gets id of current thread, suitable for logging as an unsigned quantity.
|
| 1251 |
+
|
| 1252 |
+
If pywrapbase is linked, returns GetTID() for the thread ID to be
|
| 1253 |
+
consistent with C++ logging. Otherwise, returns the numeric thread id.
|
| 1254 |
+
The quantities are made unsigned by masking with 2*sys.maxint + 1.
|
| 1255 |
+
|
| 1256 |
+
Returns:
|
| 1257 |
+
Thread ID unique to this process (unsigned)
|
| 1258 |
+
"""
|
| 1259 |
+
thread_id = threading.get_ident()
|
| 1260 |
+
return thread_id & _THREAD_ID_MASK
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
def get_absl_logger():
|
| 1264 |
+
"""Returns the absl logger instance."""
|
| 1265 |
+
assert _absl_logger is not None
|
| 1266 |
+
return _absl_logger
|
| 1267 |
+
|
| 1268 |
+
|
| 1269 |
+
def get_absl_handler():
|
| 1270 |
+
"""Returns the absl handler instance."""
|
| 1271 |
+
assert _absl_handler is not None
|
| 1272 |
+
return _absl_handler
|
| 1273 |
+
|
| 1274 |
+
|
| 1275 |
+
def use_python_logging(quiet=False):
|
| 1276 |
+
"""Uses the python implementation of the logging code.
|
| 1277 |
+
|
| 1278 |
+
Args:
|
| 1279 |
+
quiet: No logging message about switching logging type.
|
| 1280 |
+
"""
|
| 1281 |
+
get_absl_handler().activate_python_handler()
|
| 1282 |
+
if not quiet:
|
| 1283 |
+
info('Restoring pure python logging')
|
| 1284 |
+
|
| 1285 |
+
|
| 1286 |
+
_attempted_to_remove_stderr_stream_handlers = False
|
| 1287 |
+
|
| 1288 |
+
|
| 1289 |
+
def use_absl_handler():
|
| 1290 |
+
"""Uses the ABSL logging handler for logging.
|
| 1291 |
+
|
| 1292 |
+
This method is called in :func:`app.run()<absl.app.run>` so the absl handler
|
| 1293 |
+
is used in absl apps.
|
| 1294 |
+
"""
|
| 1295 |
+
global _attempted_to_remove_stderr_stream_handlers
|
| 1296 |
+
if not _attempted_to_remove_stderr_stream_handlers:
|
| 1297 |
+
# The absl handler logs to stderr by default. To prevent double logging to
|
| 1298 |
+
# stderr, the following code tries its best to remove other handlers that
|
| 1299 |
+
# emit to stderr. Those handlers are most commonly added when
|
| 1300 |
+
# logging.info/debug is called before calling use_absl_handler().
|
| 1301 |
+
handlers = [
|
| 1302 |
+
h for h in logging.root.handlers
|
| 1303 |
+
if isinstance(h, logging.StreamHandler) and h.stream == sys.stderr]
|
| 1304 |
+
for h in handlers:
|
| 1305 |
+
logging.root.removeHandler(h)
|
| 1306 |
+
_attempted_to_remove_stderr_stream_handlers = True
|
| 1307 |
+
|
| 1308 |
+
absl_handler = get_absl_handler()
|
| 1309 |
+
if absl_handler not in logging.root.handlers:
|
| 1310 |
+
logging.root.addHandler(absl_handler)
|
| 1311 |
+
FLAGS['verbosity']._update_logging_levels() # pylint: disable=protected-access
|
| 1312 |
+
FLAGS['logger_levels']._update_logger_levels() # pylint: disable=protected-access
|
| 1313 |
+
|
| 1314 |
+
|
| 1315 |
+
def _initialize():
|
| 1316 |
+
"""Initializes loggers and handlers."""
|
| 1317 |
+
global _absl_logger, _absl_handler
|
| 1318 |
+
|
| 1319 |
+
if _absl_logger:
|
| 1320 |
+
return
|
| 1321 |
+
|
| 1322 |
+
original_logger_class = logging.getLoggerClass()
|
| 1323 |
+
logging.setLoggerClass(ABSLLogger)
|
| 1324 |
+
_absl_logger = logging.getLogger('absl')
|
| 1325 |
+
logging.setLoggerClass(original_logger_class)
|
| 1326 |
+
|
| 1327 |
+
python_logging_formatter = PythonFormatter()
|
| 1328 |
+
_absl_handler = ABSLHandler(python_logging_formatter)
|
| 1329 |
+
|
| 1330 |
+
|
| 1331 |
+
_initialize()
|
lib/python3.10/site-packages/absl/logging/__init__.pyi
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import logging
|
| 16 |
+
from typing import Any, Callable, Dict, IO, NoReturn, Optional, Tuple, TypeVar, Union
|
| 17 |
+
|
| 18 |
+
from absl import flags
|
| 19 |
+
|
| 20 |
+
# Logging levels.
|
| 21 |
+
FATAL: int
|
| 22 |
+
ERROR: int
|
| 23 |
+
WARNING: int
|
| 24 |
+
WARN: int # Deprecated name.
|
| 25 |
+
INFO: int
|
| 26 |
+
DEBUG: int
|
| 27 |
+
|
| 28 |
+
ABSL_LOGGING_PREFIX_REGEX: str
|
| 29 |
+
|
| 30 |
+
LOGTOSTDERR: flags.FlagHolder[bool]
|
| 31 |
+
ALSOLOGTOSTDERR: flags.FlagHolder[bool]
|
| 32 |
+
LOG_DIR: flags.FlagHolder[str]
|
| 33 |
+
VERBOSITY: flags.FlagHolder[int]
|
| 34 |
+
LOGGER_LEVELS: flags.FlagHolder[Dict[str, str]]
|
| 35 |
+
STDERRTHRESHOLD: flags.FlagHolder[str]
|
| 36 |
+
SHOWPREFIXFORINFO: flags.FlagHolder[bool]
|
| 37 |
+
|
| 38 |
+
def get_verbosity() -> int:
|
| 39 |
+
...
|
| 40 |
+
|
| 41 |
+
def set_verbosity(v: Union[int, str]) -> None:
|
| 42 |
+
...
|
| 43 |
+
|
| 44 |
+
def set_stderrthreshold(s: Union[int, str]) -> None:
|
| 45 |
+
...
|
| 46 |
+
|
| 47 |
+
# TODO(b/277607978): Provide actual args+kwargs shadowing stdlib's logging functions.
|
| 48 |
+
def fatal(msg: Any, *args: Any, **kwargs: Any) -> NoReturn:
|
| 49 |
+
...
|
| 50 |
+
|
| 51 |
+
def error(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 52 |
+
...
|
| 53 |
+
|
| 54 |
+
def warning(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 55 |
+
...
|
| 56 |
+
|
| 57 |
+
def warn(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 58 |
+
...
|
| 59 |
+
|
| 60 |
+
def info(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 61 |
+
...
|
| 62 |
+
|
| 63 |
+
def debug(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 64 |
+
...
|
| 65 |
+
|
| 66 |
+
def exception(msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 67 |
+
...
|
| 68 |
+
|
| 69 |
+
def log_every_n(
|
| 70 |
+
level: int, msg: Any, n: int, *args: Any, use_call_stack: bool = ...
|
| 71 |
+
) -> None:
|
| 72 |
+
...
|
| 73 |
+
|
| 74 |
+
def log_every_n_seconds(
|
| 75 |
+
level: int,
|
| 76 |
+
msg: Any,
|
| 77 |
+
n_seconds: float,
|
| 78 |
+
*args: Any,
|
| 79 |
+
use_call_stack: bool = ...
|
| 80 |
+
) -> None:
|
| 81 |
+
...
|
| 82 |
+
|
| 83 |
+
def log_first_n(
|
| 84 |
+
level: int, msg: Any, n: int, *args: Any, use_call_stack: bool = ...
|
| 85 |
+
) -> None:
|
| 86 |
+
...
|
| 87 |
+
|
| 88 |
+
def log_if(level: int, msg: Any, condition: Any, *args: Any) -> None:
|
| 89 |
+
...
|
| 90 |
+
|
| 91 |
+
def log(level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 92 |
+
...
|
| 93 |
+
|
| 94 |
+
def vlog(level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 95 |
+
...
|
| 96 |
+
|
| 97 |
+
def vlog_is_on(level: int) -> bool:
|
| 98 |
+
...
|
| 99 |
+
|
| 100 |
+
def flush() -> None:
|
| 101 |
+
...
|
| 102 |
+
|
| 103 |
+
def level_debug() -> bool:
|
| 104 |
+
...
|
| 105 |
+
|
| 106 |
+
def level_info() -> bool:
|
| 107 |
+
...
|
| 108 |
+
|
| 109 |
+
def level_warning() -> bool:
|
| 110 |
+
...
|
| 111 |
+
|
| 112 |
+
level_warn = level_warning # Deprecated function.
|
| 113 |
+
|
| 114 |
+
def level_error() -> bool:
|
| 115 |
+
...
|
| 116 |
+
|
| 117 |
+
def get_log_file_name(level: int = ...) -> str:
|
| 118 |
+
...
|
| 119 |
+
|
| 120 |
+
def find_log_dir_and_names(
|
| 121 |
+
program_name: Optional[str] = ..., log_dir: Optional[str] = ...
|
| 122 |
+
) -> Tuple[str, str, str]:
|
| 123 |
+
...
|
| 124 |
+
|
| 125 |
+
def find_log_dir(log_dir: Optional[str] = ...) -> str:
|
| 126 |
+
...
|
| 127 |
+
|
| 128 |
+
def get_absl_log_prefix(record: logging.LogRecord) -> str:
|
| 129 |
+
...
|
| 130 |
+
|
| 131 |
+
_SkipLogT = TypeVar('_SkipLogT', str, Callable[..., Any])
|
| 132 |
+
|
| 133 |
+
def skip_log_prefix(func: _SkipLogT) -> _SkipLogT:
|
| 134 |
+
...
|
| 135 |
+
|
| 136 |
+
_StreamT = TypeVar('_StreamT')
|
| 137 |
+
|
| 138 |
+
class PythonHandler(logging.StreamHandler[_StreamT]): # type: ignore[type-var]
|
| 139 |
+
|
| 140 |
+
def __init__(
|
| 141 |
+
self,
|
| 142 |
+
stream: Optional[_StreamT] = ...,
|
| 143 |
+
formatter: Optional[logging.Formatter] = ...,
|
| 144 |
+
) -> None:
|
| 145 |
+
...
|
| 146 |
+
|
| 147 |
+
def start_logging_to_file(
|
| 148 |
+
self, program_name: Optional[str] = ..., log_dir: Optional[str] = ...
|
| 149 |
+
) -> None:
|
| 150 |
+
...
|
| 151 |
+
|
| 152 |
+
def use_absl_log_file(
|
| 153 |
+
self, program_name: Optional[str] = ..., log_dir: Optional[str] = ...
|
| 154 |
+
) -> None:
|
| 155 |
+
...
|
| 156 |
+
|
| 157 |
+
def flush(self) -> None:
|
| 158 |
+
...
|
| 159 |
+
|
| 160 |
+
def emit(self, record: logging.LogRecord) -> None:
|
| 161 |
+
...
|
| 162 |
+
|
| 163 |
+
def close(self) -> None:
|
| 164 |
+
...
|
| 165 |
+
|
| 166 |
+
class ABSLHandler(logging.Handler):
|
| 167 |
+
|
| 168 |
+
def __init__(self, python_logging_formatter: PythonFormatter) -> None:
|
| 169 |
+
...
|
| 170 |
+
|
| 171 |
+
def format(self, record: logging.LogRecord) -> str:
|
| 172 |
+
...
|
| 173 |
+
|
| 174 |
+
def setFormatter(self, fmt) -> None:
|
| 175 |
+
...
|
| 176 |
+
|
| 177 |
+
def emit(self, record: logging.LogRecord) -> None:
|
| 178 |
+
...
|
| 179 |
+
|
| 180 |
+
def flush(self) -> None:
|
| 181 |
+
...
|
| 182 |
+
|
| 183 |
+
def close(self) -> None:
|
| 184 |
+
...
|
| 185 |
+
|
| 186 |
+
def handle(self, record: logging.LogRecord) -> bool:
|
| 187 |
+
...
|
| 188 |
+
|
| 189 |
+
@property
|
| 190 |
+
def python_handler(self) -> PythonHandler:
|
| 191 |
+
...
|
| 192 |
+
|
| 193 |
+
def activate_python_handler(self) -> None:
|
| 194 |
+
...
|
| 195 |
+
|
| 196 |
+
def use_absl_log_file(
|
| 197 |
+
self, program_name: Optional[str] = ..., log_dir: Optional[str] = ...
|
| 198 |
+
) -> None:
|
| 199 |
+
...
|
| 200 |
+
|
| 201 |
+
def start_logging_to_file(self, program_name=None, log_dir=None) -> None:
|
| 202 |
+
...
|
| 203 |
+
|
| 204 |
+
class PythonFormatter(logging.Formatter):
|
| 205 |
+
|
| 206 |
+
def format(self, record: logging.LogRecord) -> str:
|
| 207 |
+
...
|
| 208 |
+
|
| 209 |
+
class ABSLLogger(logging.Logger):
|
| 210 |
+
|
| 211 |
+
def findCaller(
|
| 212 |
+
self, stack_info: bool = ..., stacklevel: int = ...
|
| 213 |
+
) -> Tuple[str, int, str, Optional[str]]:
|
| 214 |
+
...
|
| 215 |
+
|
| 216 |
+
def critical(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 217 |
+
...
|
| 218 |
+
|
| 219 |
+
def fatal(self, msg: Any, *args: Any, **kwargs: Any) -> NoReturn: # type: ignore[override]
|
| 220 |
+
...
|
| 221 |
+
|
| 222 |
+
def error(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 223 |
+
...
|
| 224 |
+
|
| 225 |
+
def warn(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 226 |
+
...
|
| 227 |
+
|
| 228 |
+
def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 229 |
+
...
|
| 230 |
+
|
| 231 |
+
def info(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 232 |
+
...
|
| 233 |
+
|
| 234 |
+
def debug(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 235 |
+
...
|
| 236 |
+
|
| 237 |
+
def log(self, level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
|
| 238 |
+
...
|
| 239 |
+
|
| 240 |
+
def handle(self, record: logging.LogRecord) -> None:
|
| 241 |
+
...
|
| 242 |
+
|
| 243 |
+
@classmethod
|
| 244 |
+
def register_frame_to_skip(
|
| 245 |
+
cls, file_name: str, function_name: str, line_number: Optional[int] = ...
|
| 246 |
+
) -> None:
|
| 247 |
+
...
|
| 248 |
+
|
| 249 |
+
# NOTE: Returns None before _initialize called but shouldn't occur after import.
|
| 250 |
+
def get_absl_logger() -> ABSLLogger:
|
| 251 |
+
...
|
| 252 |
+
|
| 253 |
+
# NOTE: Returns None before _initialize called but shouldn't occur after import.
|
| 254 |
+
def get_absl_handler() -> ABSLHandler:
|
| 255 |
+
...
|
| 256 |
+
|
| 257 |
+
def use_python_logging(quiet: bool = ...) -> None:
|
| 258 |
+
...
|
| 259 |
+
|
| 260 |
+
def use_absl_handler() -> None:
|
| 261 |
+
...
|
lib/python3.10/site-packages/absl/logging/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (38.9 kB). View file
|
|
|
lib/python3.10/site-packages/absl/logging/__pycache__/converter.cpython-310.pyc
ADDED
|
Binary file (5.26 kB). View file
|
|
|
lib/python3.10/site-packages/absl/logging/converter.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Module to convert log levels between Abseil Python, C++, and Python standard.
|
| 16 |
+
|
| 17 |
+
This converter has to convert (best effort) between three different
|
| 18 |
+
logging level schemes:
|
| 19 |
+
|
| 20 |
+
* **cpp**: The C++ logging level scheme used in Abseil C++.
|
| 21 |
+
* **absl**: The absl.logging level scheme used in Abseil Python.
|
| 22 |
+
* **standard**: The python standard library logging level scheme.
|
| 23 |
+
|
| 24 |
+
Here is a handy ascii chart for easy mental mapping::
|
| 25 |
+
|
| 26 |
+
LEVEL | cpp | absl | standard |
|
| 27 |
+
---------+-----+--------+----------+
|
| 28 |
+
DEBUG | 0 | 1 | 10 |
|
| 29 |
+
INFO | 0 | 0 | 20 |
|
| 30 |
+
WARNING | 1 | -1 | 30 |
|
| 31 |
+
ERROR | 2 | -2 | 40 |
|
| 32 |
+
CRITICAL | 3 | -3 | 50 |
|
| 33 |
+
FATAL | 3 | -3 | 50 |
|
| 34 |
+
|
| 35 |
+
Note: standard logging ``CRITICAL`` is mapped to absl/cpp ``FATAL``.
|
| 36 |
+
However, only ``CRITICAL`` logs from the absl logger (or absl.logging.fatal)
|
| 37 |
+
will terminate the program. ``CRITICAL`` logs from non-absl loggers are treated
|
| 38 |
+
as error logs with a message prefix ``"CRITICAL - "``.
|
| 39 |
+
|
| 40 |
+
Converting from standard to absl or cpp is a lossy conversion.
|
| 41 |
+
Converting back to standard will lose granularity. For this reason,
|
| 42 |
+
users should always try to convert to standard, the richest
|
| 43 |
+
representation, before manipulating the levels, and then only to cpp
|
| 44 |
+
or absl if those level schemes are absolutely necessary.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
import logging
|
| 48 |
+
|
| 49 |
+
STANDARD_CRITICAL = logging.CRITICAL
|
| 50 |
+
STANDARD_ERROR = logging.ERROR
|
| 51 |
+
STANDARD_WARNING = logging.WARNING
|
| 52 |
+
STANDARD_INFO = logging.INFO
|
| 53 |
+
STANDARD_DEBUG = logging.DEBUG
|
| 54 |
+
|
| 55 |
+
# These levels are also used to define the constants
|
| 56 |
+
# FATAL, ERROR, WARNING, INFO, and DEBUG in the
|
| 57 |
+
# absl.logging module.
|
| 58 |
+
ABSL_FATAL = -3
|
| 59 |
+
ABSL_ERROR = -2
|
| 60 |
+
ABSL_WARNING = -1
|
| 61 |
+
ABSL_WARN = -1 # Deprecated name.
|
| 62 |
+
ABSL_INFO = 0
|
| 63 |
+
ABSL_DEBUG = 1
|
| 64 |
+
|
| 65 |
+
ABSL_LEVELS = {ABSL_FATAL: 'FATAL',
|
| 66 |
+
ABSL_ERROR: 'ERROR',
|
| 67 |
+
ABSL_WARNING: 'WARNING',
|
| 68 |
+
ABSL_INFO: 'INFO',
|
| 69 |
+
ABSL_DEBUG: 'DEBUG'}
|
| 70 |
+
|
| 71 |
+
# Inverts the ABSL_LEVELS dictionary
|
| 72 |
+
ABSL_NAMES = {'FATAL': ABSL_FATAL,
|
| 73 |
+
'ERROR': ABSL_ERROR,
|
| 74 |
+
'WARNING': ABSL_WARNING,
|
| 75 |
+
'WARN': ABSL_WARNING, # Deprecated name.
|
| 76 |
+
'INFO': ABSL_INFO,
|
| 77 |
+
'DEBUG': ABSL_DEBUG}
|
| 78 |
+
|
| 79 |
+
ABSL_TO_STANDARD = {ABSL_FATAL: STANDARD_CRITICAL,
|
| 80 |
+
ABSL_ERROR: STANDARD_ERROR,
|
| 81 |
+
ABSL_WARNING: STANDARD_WARNING,
|
| 82 |
+
ABSL_INFO: STANDARD_INFO,
|
| 83 |
+
ABSL_DEBUG: STANDARD_DEBUG}
|
| 84 |
+
|
| 85 |
+
# Inverts the ABSL_TO_STANDARD
|
| 86 |
+
STANDARD_TO_ABSL = {v: k for (k, v) in ABSL_TO_STANDARD.items()}
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def get_initial_for_level(level):
|
| 90 |
+
"""Gets the initial that should start the log line for the given level.
|
| 91 |
+
|
| 92 |
+
It returns:
|
| 93 |
+
|
| 94 |
+
* ``'I'`` when: ``level < STANDARD_WARNING``.
|
| 95 |
+
* ``'W'`` when: ``STANDARD_WARNING <= level < STANDARD_ERROR``.
|
| 96 |
+
* ``'E'`` when: ``STANDARD_ERROR <= level < STANDARD_CRITICAL``.
|
| 97 |
+
* ``'F'`` when: ``level >= STANDARD_CRITICAL``.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
level: int, a Python standard logging level.
|
| 101 |
+
|
| 102 |
+
Returns:
|
| 103 |
+
The first initial as it would be logged by the C++ logging module.
|
| 104 |
+
"""
|
| 105 |
+
if level < STANDARD_WARNING:
|
| 106 |
+
return 'I'
|
| 107 |
+
elif level < STANDARD_ERROR:
|
| 108 |
+
return 'W'
|
| 109 |
+
elif level < STANDARD_CRITICAL:
|
| 110 |
+
return 'E'
|
| 111 |
+
else:
|
| 112 |
+
return 'F'
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def absl_to_cpp(level):
|
| 116 |
+
"""Converts an absl log level to a cpp log level.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
level: int, an absl.logging level.
|
| 120 |
+
|
| 121 |
+
Raises:
|
| 122 |
+
TypeError: Raised when level is not an integer.
|
| 123 |
+
|
| 124 |
+
Returns:
|
| 125 |
+
The corresponding integer level for use in Abseil C++.
|
| 126 |
+
"""
|
| 127 |
+
if not isinstance(level, int):
|
| 128 |
+
raise TypeError(f'Expect an int level, found {type(level)}')
|
| 129 |
+
if level >= 0:
|
| 130 |
+
# C++ log levels must be >= 0
|
| 131 |
+
return 0
|
| 132 |
+
else:
|
| 133 |
+
return -level
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def absl_to_standard(level):
|
| 137 |
+
"""Converts an integer level from the absl value to the standard value.
|
| 138 |
+
|
| 139 |
+
Args:
|
| 140 |
+
level: int, an absl.logging level.
|
| 141 |
+
|
| 142 |
+
Raises:
|
| 143 |
+
TypeError: Raised when level is not an integer.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
The corresponding integer level for use in standard logging.
|
| 147 |
+
"""
|
| 148 |
+
if not isinstance(level, int):
|
| 149 |
+
raise TypeError(f'Expect an int level, found {type(level)}')
|
| 150 |
+
if level < ABSL_FATAL:
|
| 151 |
+
level = ABSL_FATAL
|
| 152 |
+
if level <= ABSL_DEBUG:
|
| 153 |
+
return ABSL_TO_STANDARD[level]
|
| 154 |
+
# Maps to vlog levels.
|
| 155 |
+
return STANDARD_DEBUG - level + 1
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def string_to_standard(level):
|
| 159 |
+
"""Converts a string level to standard logging level value.
|
| 160 |
+
|
| 161 |
+
Args:
|
| 162 |
+
level: str, case-insensitive ``'debug'``, ``'info'``, ``'warning'``,
|
| 163 |
+
``'error'``, ``'fatal'``.
|
| 164 |
+
|
| 165 |
+
Returns:
|
| 166 |
+
The corresponding integer level for use in standard logging.
|
| 167 |
+
"""
|
| 168 |
+
return absl_to_standard(ABSL_NAMES.get(level.upper()))
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def standard_to_absl(level):
|
| 172 |
+
"""Converts an integer level from the standard value to the absl value.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
level: int, a Python standard logging level.
|
| 176 |
+
|
| 177 |
+
Raises:
|
| 178 |
+
TypeError: Raised when level is not an integer.
|
| 179 |
+
|
| 180 |
+
Returns:
|
| 181 |
+
The corresponding integer level for use in absl logging.
|
| 182 |
+
"""
|
| 183 |
+
if not isinstance(level, int):
|
| 184 |
+
raise TypeError(f'Expect an int level, found {type(level)}')
|
| 185 |
+
if level < 0:
|
| 186 |
+
level = 0
|
| 187 |
+
if level < STANDARD_DEBUG:
|
| 188 |
+
# Maps to vlog levels.
|
| 189 |
+
return STANDARD_DEBUG - level + 1
|
| 190 |
+
elif level < STANDARD_INFO:
|
| 191 |
+
return ABSL_DEBUG
|
| 192 |
+
elif level < STANDARD_WARNING:
|
| 193 |
+
return ABSL_INFO
|
| 194 |
+
elif level < STANDARD_ERROR:
|
| 195 |
+
return ABSL_WARNING
|
| 196 |
+
elif level < STANDARD_CRITICAL:
|
| 197 |
+
return ABSL_ERROR
|
| 198 |
+
else:
|
| 199 |
+
return ABSL_FATAL
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def standard_to_cpp(level):
|
| 203 |
+
"""Converts an integer level from the standard value to the cpp value.
|
| 204 |
+
|
| 205 |
+
Args:
|
| 206 |
+
level: int, a Python standard logging level.
|
| 207 |
+
|
| 208 |
+
Raises:
|
| 209 |
+
TypeError: Raised when level is not an integer.
|
| 210 |
+
|
| 211 |
+
Returns:
|
| 212 |
+
The corresponding integer level for use in cpp logging.
|
| 213 |
+
"""
|
| 214 |
+
return absl_to_cpp(standard_to_absl(level))
|
lib/python3.10/site-packages/absl/py.typed
ADDED
|
File without changes
|
lib/python3.10/site-packages/absl/testing/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
lib/python3.10/site-packages/absl/testing/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (203 Bytes). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-310.pyc
ADDED
|
Binary file (1.66 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-310.pyc
ADDED
|
Binary file (3.14 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/absltest.cpython-310.pyc
ADDED
|
Binary file (87.3 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/flagsaver.cpython-310.pyc
ADDED
|
Binary file (12.7 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/parameterized.cpython-310.pyc
ADDED
|
Binary file (23.6 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/__pycache__/xml_reporter.cpython-310.pyc
ADDED
|
Binary file (18.6 kB). View file
|
|
|
lib/python3.10/site-packages/absl/testing/_bazelize_command.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Internal helper for running tests on Windows Bazel."""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
|
| 19 |
+
from absl import flags
|
| 20 |
+
|
| 21 |
+
FLAGS = flags.FLAGS
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def get_executable_path(py_binary_name):
|
| 25 |
+
"""Returns the executable path of a py_binary.
|
| 26 |
+
|
| 27 |
+
This returns the executable path of a py_binary that is in another Bazel
|
| 28 |
+
target's data dependencies.
|
| 29 |
+
|
| 30 |
+
On Linux/macOS, the path and __file__ has the same root directory.
|
| 31 |
+
On Windows, bazel builds an .exe file and we need to use the MANIFEST file
|
| 32 |
+
the location the actual binary.
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
py_binary_name: string, the name of a py_binary that is in another Bazel
|
| 36 |
+
target's data dependencies.
|
| 37 |
+
|
| 38 |
+
Raises:
|
| 39 |
+
RuntimeError: Raised when it cannot locate the executable path.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
if os.name == 'nt':
|
| 43 |
+
py_binary_name += '.exe'
|
| 44 |
+
manifest_file = os.path.join(FLAGS.test_srcdir, 'MANIFEST')
|
| 45 |
+
workspace_name = os.environ['TEST_WORKSPACE']
|
| 46 |
+
manifest_entry = f'{workspace_name}/{py_binary_name}'
|
| 47 |
+
with open(manifest_file) as manifest_fd:
|
| 48 |
+
for line in manifest_fd:
|
| 49 |
+
tokens = line.strip().split(' ')
|
| 50 |
+
if len(tokens) != 2:
|
| 51 |
+
continue
|
| 52 |
+
if manifest_entry == tokens[0]:
|
| 53 |
+
return tokens[1]
|
| 54 |
+
raise RuntimeError(
|
| 55 |
+
'Cannot locate executable path for {}, MANIFEST file: {}.'.format(
|
| 56 |
+
py_binary_name, manifest_file))
|
| 57 |
+
else:
|
| 58 |
+
# NOTE: __file__ may be .py or .pyc, depending on how the module was
|
| 59 |
+
# loaded and executed.
|
| 60 |
+
path = __file__
|
| 61 |
+
|
| 62 |
+
# Use the package name to find the root directory: every dot is
|
| 63 |
+
# a directory, plus one for ourselves.
|
| 64 |
+
for _ in range(__name__.count('.') + 1):
|
| 65 |
+
path = os.path.dirname(path)
|
| 66 |
+
|
| 67 |
+
root_directory = path
|
| 68 |
+
return os.path.join(root_directory, py_binary_name)
|
lib/python3.10/site-packages/absl/testing/_pretty_print_reporter.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2018 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""TestResult implementing default output for test execution status."""
|
| 16 |
+
|
| 17 |
+
import unittest
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class TextTestResult(unittest.TextTestResult):
|
| 21 |
+
"""TestResult class that provides the default text result formatting."""
|
| 22 |
+
|
| 23 |
+
def __init__(self, stream, descriptions, verbosity):
|
| 24 |
+
# Disable the verbose per-test output from the superclass, since it would
|
| 25 |
+
# conflict with our customized output.
|
| 26 |
+
super().__init__(stream, descriptions, 0)
|
| 27 |
+
self._per_test_output = verbosity > 0
|
| 28 |
+
|
| 29 |
+
def _print_status(self, tag, test, reason=None):
|
| 30 |
+
if self._per_test_output:
|
| 31 |
+
test_id = test.id()
|
| 32 |
+
if test_id.startswith('__main__.'):
|
| 33 |
+
test_id = test_id[len('__main__.'):]
|
| 34 |
+
if reason:
|
| 35 |
+
print('[%s] %s - %s' % (tag, test_id, reason), file=self.stream)
|
| 36 |
+
else:
|
| 37 |
+
print('[%s] %s' % (tag, test_id), file=self.stream)
|
| 38 |
+
self.stream.flush()
|
| 39 |
+
|
| 40 |
+
def startTest(self, test):
|
| 41 |
+
super().startTest(test)
|
| 42 |
+
self._print_status(' RUN ', test)
|
| 43 |
+
|
| 44 |
+
def addSuccess(self, test):
|
| 45 |
+
super().addSuccess(test)
|
| 46 |
+
self._print_status(' OK ', test)
|
| 47 |
+
|
| 48 |
+
def addError(self, test, err):
|
| 49 |
+
super().addError(test, err)
|
| 50 |
+
self._print_status(' FAILED ', test)
|
| 51 |
+
|
| 52 |
+
def addFailure(self, test, err):
|
| 53 |
+
super().addFailure(test, err)
|
| 54 |
+
self._print_status(' FAILED ', test)
|
| 55 |
+
|
| 56 |
+
def addSkip(self, test, reason):
|
| 57 |
+
super().addSkip(test, reason)
|
| 58 |
+
self._print_status(' SKIPPED ', test, reason)
|
| 59 |
+
|
| 60 |
+
def addExpectedFailure(self, test, err):
|
| 61 |
+
super().addExpectedFailure(test, err)
|
| 62 |
+
self._print_status(' OK ', test)
|
| 63 |
+
|
| 64 |
+
def addUnexpectedSuccess(self, test):
|
| 65 |
+
super().addUnexpectedSuccess(test)
|
| 66 |
+
self._print_status(' FAILED ', test)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class TextTestRunner(unittest.TextTestRunner):
|
| 70 |
+
"""A test runner that produces formatted text results."""
|
| 71 |
+
|
| 72 |
+
_TEST_RESULT_CLASS = TextTestResult
|
| 73 |
+
|
| 74 |
+
# Set this to true at the class or instance level to run tests using a
|
| 75 |
+
# debug-friendly method (e.g, one that doesn't catch exceptions and interacts
|
| 76 |
+
# better with debuggers).
|
| 77 |
+
# Usually this is set using --pdb_post_mortem.
|
| 78 |
+
run_for_debugging = False
|
| 79 |
+
|
| 80 |
+
def run(self, test) -> unittest.TextTestResult:
|
| 81 |
+
if self.run_for_debugging:
|
| 82 |
+
return self._run_debug(test)
|
| 83 |
+
else:
|
| 84 |
+
return super().run(test)
|
| 85 |
+
|
| 86 |
+
def _run_debug(self, test) -> unittest.TextTestResult:
|
| 87 |
+
test.debug()
|
| 88 |
+
# Return an empty result to indicate success.
|
| 89 |
+
return self._makeResult()
|
| 90 |
+
|
| 91 |
+
def _makeResult(self):
|
| 92 |
+
return TextTestResult(self.stream, self.descriptions, self.verbosity)
|
lib/python3.10/site-packages/absl/testing/absltest.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lib/python3.10/site-packages/absl/testing/flagsaver.py
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Decorator and context manager for saving and restoring flag values.
|
| 16 |
+
|
| 17 |
+
There are many ways to save and restore. Always use the most convenient method
|
| 18 |
+
for a given use case.
|
| 19 |
+
|
| 20 |
+
Here are examples of each method. They all call ``do_stuff()`` while
|
| 21 |
+
``FLAGS.someflag`` is temporarily set to ``'foo'``::
|
| 22 |
+
|
| 23 |
+
from absl.testing import flagsaver
|
| 24 |
+
|
| 25 |
+
# Use a decorator which can optionally override flags via arguments.
|
| 26 |
+
@flagsaver.flagsaver(someflag='foo')
|
| 27 |
+
def some_func():
|
| 28 |
+
do_stuff()
|
| 29 |
+
|
| 30 |
+
# Use a decorator which can optionally override flags with flagholders.
|
| 31 |
+
@flagsaver.flagsaver((module.FOO_FLAG, 'foo'), (other_mod.BAR_FLAG, 23))
|
| 32 |
+
def some_func():
|
| 33 |
+
do_stuff()
|
| 34 |
+
|
| 35 |
+
# Use a decorator which does not override flags itself.
|
| 36 |
+
@flagsaver.flagsaver
|
| 37 |
+
def some_func():
|
| 38 |
+
FLAGS.someflag = 'foo'
|
| 39 |
+
do_stuff()
|
| 40 |
+
|
| 41 |
+
# Use a context manager which can optionally override flags via arguments.
|
| 42 |
+
with flagsaver.flagsaver(someflag='foo'):
|
| 43 |
+
do_stuff()
|
| 44 |
+
|
| 45 |
+
# Save and restore the flag values yourself.
|
| 46 |
+
saved_flag_values = flagsaver.save_flag_values()
|
| 47 |
+
try:
|
| 48 |
+
FLAGS.someflag = 'foo'
|
| 49 |
+
do_stuff()
|
| 50 |
+
finally:
|
| 51 |
+
flagsaver.restore_flag_values(saved_flag_values)
|
| 52 |
+
|
| 53 |
+
# Use the parsing version to emulate users providing the flags.
|
| 54 |
+
# Note that all flags must be provided as strings (unparsed).
|
| 55 |
+
@flagsaver.as_parsed(some_int_flag='123')
|
| 56 |
+
def some_func():
|
| 57 |
+
# Because the flag was parsed it is considered "present".
|
| 58 |
+
assert FLAGS.some_int_flag.present
|
| 59 |
+
do_stuff()
|
| 60 |
+
|
| 61 |
+
# flagsaver.as_parsed() can also be used as a context manager just like
|
| 62 |
+
# flagsaver.flagsaver()
|
| 63 |
+
with flagsaver.as_parsed(some_int_flag='123'):
|
| 64 |
+
do_stuff()
|
| 65 |
+
|
| 66 |
+
# The flagsaver.as_parsed() interface also supports FlagHolder objects.
|
| 67 |
+
@flagsaver.as_parsed((module.FOO_FLAG, 'foo'), (other_mod.BAR_FLAG, '23'))
|
| 68 |
+
def some_func():
|
| 69 |
+
do_stuff()
|
| 70 |
+
|
| 71 |
+
# Using as_parsed with a multi_X flag requires a sequence of strings.
|
| 72 |
+
@flagsaver.as_parsed(some_multi_int_flag=['123', '456'])
|
| 73 |
+
def some_func():
|
| 74 |
+
assert FLAGS.some_multi_int_flag.present
|
| 75 |
+
do_stuff()
|
| 76 |
+
|
| 77 |
+
# If a flag name includes non-identifier characters it can be specified like
|
| 78 |
+
# so:
|
| 79 |
+
@flagsaver.as_parsed(**{'i-like-dashes': 'true'})
|
| 80 |
+
def some_func():
|
| 81 |
+
do_stuff()
|
| 82 |
+
|
| 83 |
+
We save and restore a shallow copy of each Flag object's ``__dict__`` attribute.
|
| 84 |
+
This preserves all attributes of the flag, such as whether or not it was
|
| 85 |
+
overridden from its default value.
|
| 86 |
+
|
| 87 |
+
WARNING: Currently a flag that is saved and then deleted cannot be restored. An
|
| 88 |
+
exception will be raised. However if you *add* a flag after saving flag values,
|
| 89 |
+
and then restore flag values, the added flag will be deleted with no errors.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
import collections
|
| 93 |
+
import functools
|
| 94 |
+
import inspect
|
| 95 |
+
from typing import Any, Callable, Dict, Mapping, Sequence, Tuple, Type, TypeVar, Union, overload
|
| 96 |
+
|
| 97 |
+
from absl import flags
|
| 98 |
+
|
| 99 |
+
FLAGS = flags.FLAGS
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
# The type of pre/post wrapped functions.
|
| 103 |
+
_CallableT = TypeVar('_CallableT', bound=Callable)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
@overload
|
| 107 |
+
def flagsaver(func: _CallableT) -> _CallableT:
|
| 108 |
+
...
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
@overload
|
| 112 |
+
def flagsaver(
|
| 113 |
+
*args: Tuple[flags.FlagHolder, Any], **kwargs: Any
|
| 114 |
+
) -> '_FlagOverrider':
|
| 115 |
+
...
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def flagsaver(*args, **kwargs):
|
| 119 |
+
"""The main flagsaver interface. See module doc for usage."""
|
| 120 |
+
return _construct_overrider(_FlagOverrider, *args, **kwargs) # type: ignore[bad-return-type]
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
@overload
|
| 124 |
+
def as_parsed(*args: Tuple[flags.FlagHolder, Union[str, Sequence[str]]],
|
| 125 |
+
**kwargs: Union[str, Sequence[str]]) -> '_ParsingFlagOverrider':
|
| 126 |
+
...
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
@overload
|
| 130 |
+
def as_parsed(func: _CallableT) -> _CallableT:
|
| 131 |
+
...
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def as_parsed(*args, **kwargs):
|
| 135 |
+
"""Overrides flags by parsing strings, saves flag state similar to flagsaver.
|
| 136 |
+
|
| 137 |
+
This function can be used as either a decorator or context manager similar to
|
| 138 |
+
flagsaver.flagsaver(). However, where flagsaver.flagsaver() directly sets the
|
| 139 |
+
flags to new values, this function will parse the provided arguments as if
|
| 140 |
+
they were provided on the command line. Among other things, this will cause
|
| 141 |
+
`FLAGS['flag_name'].present == True`.
|
| 142 |
+
|
| 143 |
+
A note on unparsed input: For many flag types, the unparsed version will be
|
| 144 |
+
a single string. However for multi_x (multi_string, multi_integer, multi_enum)
|
| 145 |
+
the unparsed version will be a Sequence of strings.
|
| 146 |
+
|
| 147 |
+
Args:
|
| 148 |
+
*args: Tuples of FlagHolders and their unparsed value.
|
| 149 |
+
**kwargs: The keyword args are flag names, and the values are unparsed
|
| 150 |
+
values.
|
| 151 |
+
|
| 152 |
+
Returns:
|
| 153 |
+
_ParsingFlagOverrider that serves as a context manager or decorator. Will
|
| 154 |
+
save previous flag state and parse new flags, then on cleanup it will
|
| 155 |
+
restore the previous flag state.
|
| 156 |
+
"""
|
| 157 |
+
return _construct_overrider(_ParsingFlagOverrider, *args, **kwargs)
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
# NOTE: the order of these overload declarations matters. The type checker will
|
| 161 |
+
# pick the first match which could be incorrect.
|
| 162 |
+
@overload
|
| 163 |
+
def _construct_overrider(
|
| 164 |
+
flag_overrider_cls: Type['_ParsingFlagOverrider'],
|
| 165 |
+
*args: Tuple[flags.FlagHolder, Union[str, Sequence[str]]],
|
| 166 |
+
**kwargs: Union[str, Sequence[str]]) -> '_ParsingFlagOverrider':
|
| 167 |
+
...
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
@overload
|
| 171 |
+
def _construct_overrider(
|
| 172 |
+
flag_overrider_cls: Type['_FlagOverrider'], func: _CallableT
|
| 173 |
+
) -> _CallableT:
|
| 174 |
+
...
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
@overload
|
| 178 |
+
def _construct_overrider(
|
| 179 |
+
flag_overrider_cls: Type['_FlagOverrider'],
|
| 180 |
+
*args: Tuple[flags.FlagHolder, Any],
|
| 181 |
+
**kwargs: Any,
|
| 182 |
+
) -> '_FlagOverrider':
|
| 183 |
+
...
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def _construct_overrider(flag_overrider_cls, *args, **kwargs):
|
| 187 |
+
"""Handles the args/kwargs returning an instance of flag_overrider_cls.
|
| 188 |
+
|
| 189 |
+
If flag_overrider_cls is _FlagOverrider then values should be native python
|
| 190 |
+
types matching the python types. Otherwise if flag_overrider_cls is
|
| 191 |
+
_ParsingFlagOverrider the values should be strings or sequences of strings.
|
| 192 |
+
|
| 193 |
+
Args:
|
| 194 |
+
flag_overrider_cls: The class that will do the overriding.
|
| 195 |
+
*args: Tuples of FlagHolder and the new flag value.
|
| 196 |
+
**kwargs: Keword args mapping flag name to new flag value.
|
| 197 |
+
|
| 198 |
+
Returns:
|
| 199 |
+
A _FlagOverrider to be used as a decorator or context manager.
|
| 200 |
+
"""
|
| 201 |
+
if not args:
|
| 202 |
+
return flag_overrider_cls(**kwargs)
|
| 203 |
+
# args can be [func] if used as `@flagsaver` instead of `@flagsaver(...)`
|
| 204 |
+
if len(args) == 1 and callable(args[0]):
|
| 205 |
+
if kwargs:
|
| 206 |
+
raise ValueError(
|
| 207 |
+
"It's invalid to specify both positional and keyword parameters.")
|
| 208 |
+
func = args[0]
|
| 209 |
+
if inspect.isclass(func):
|
| 210 |
+
raise TypeError('@flagsaver.flagsaver cannot be applied to a class.')
|
| 211 |
+
return _wrap(flag_overrider_cls, func, {})
|
| 212 |
+
# args can be a list of (FlagHolder, value) pairs.
|
| 213 |
+
# In which case they augment any specified kwargs.
|
| 214 |
+
for arg in args:
|
| 215 |
+
if not isinstance(arg, tuple) or len(arg) != 2:
|
| 216 |
+
raise ValueError('Expected (FlagHolder, value) pair, found %r' % (arg,))
|
| 217 |
+
holder, value = arg
|
| 218 |
+
if not isinstance(holder, flags.FlagHolder):
|
| 219 |
+
raise ValueError('Expected (FlagHolder, value) pair, found %r' % (arg,))
|
| 220 |
+
if holder.name in kwargs:
|
| 221 |
+
raise ValueError('Cannot set --%s multiple times' % holder.name)
|
| 222 |
+
kwargs[holder.name] = value
|
| 223 |
+
return flag_overrider_cls(**kwargs)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def save_flag_values(
|
| 227 |
+
flag_values: flags.FlagValues = FLAGS,
|
| 228 |
+
) -> Dict[str, Dict[str, Any]]:
|
| 229 |
+
"""Returns copy of flag values as a dict.
|
| 230 |
+
|
| 231 |
+
Args:
|
| 232 |
+
flag_values: FlagValues, the FlagValues instance with which the flag will be
|
| 233 |
+
saved. This should almost never need to be overridden.
|
| 234 |
+
|
| 235 |
+
Returns:
|
| 236 |
+
Dictionary mapping keys to values. Keys are flag names, values are
|
| 237 |
+
corresponding ``__dict__`` members. E.g. ``{'key': value_dict, ...}``.
|
| 238 |
+
"""
|
| 239 |
+
return {name: _copy_flag_dict(flag_values[name]) for name in flag_values}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def restore_flag_values(
|
| 243 |
+
saved_flag_values: Mapping[str, Dict[str, Any]],
|
| 244 |
+
flag_values: flags.FlagValues = FLAGS,
|
| 245 |
+
) -> None:
|
| 246 |
+
"""Restores flag values based on the dictionary of flag values.
|
| 247 |
+
|
| 248 |
+
Args:
|
| 249 |
+
saved_flag_values: {'flag_name': value_dict, ...}
|
| 250 |
+
flag_values: FlagValues, the FlagValues instance from which the flag will be
|
| 251 |
+
restored. This should almost never need to be overridden.
|
| 252 |
+
"""
|
| 253 |
+
new_flag_names = list(flag_values)
|
| 254 |
+
for name in new_flag_names:
|
| 255 |
+
saved = saved_flag_values.get(name)
|
| 256 |
+
if saved is None:
|
| 257 |
+
# If __dict__ was not saved delete "new" flag.
|
| 258 |
+
delattr(flag_values, name)
|
| 259 |
+
else:
|
| 260 |
+
if flag_values[name].value != saved['_value']:
|
| 261 |
+
flag_values[name].value = saved['_value'] # Ensure C++ value is set.
|
| 262 |
+
flag_values[name].__dict__ = saved
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
@overload
|
| 266 |
+
def _wrap(flag_overrider_cls: Type['_FlagOverrider'], func: _CallableT,
|
| 267 |
+
overrides: Mapping[str, Any]) -> _CallableT:
|
| 268 |
+
...
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
@overload
|
| 272 |
+
def _wrap(flag_overrider_cls: Type['_ParsingFlagOverrider'], func: _CallableT,
|
| 273 |
+
overrides: Mapping[str, Union[str, Sequence[str]]]) -> _CallableT:
|
| 274 |
+
...
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def _wrap(flag_overrider_cls, func, overrides):
|
| 278 |
+
"""Creates a wrapper function that saves/restores flag values.
|
| 279 |
+
|
| 280 |
+
Args:
|
| 281 |
+
flag_overrider_cls: The class that will be used as a context manager.
|
| 282 |
+
func: This will be called between saving flags and restoring flags.
|
| 283 |
+
overrides: Flag names mapped to their values. These flags will be set after
|
| 284 |
+
saving the original flag state. The type of the values depends on if
|
| 285 |
+
_FlagOverrider or _ParsingFlagOverrider was specified.
|
| 286 |
+
|
| 287 |
+
Returns:
|
| 288 |
+
A wrapped version of func.
|
| 289 |
+
"""
|
| 290 |
+
|
| 291 |
+
@functools.wraps(func)
|
| 292 |
+
def _flagsaver_wrapper(*args, **kwargs):
|
| 293 |
+
"""Wrapper function that saves and restores flags."""
|
| 294 |
+
with flag_overrider_cls(**overrides):
|
| 295 |
+
return func(*args, **kwargs)
|
| 296 |
+
|
| 297 |
+
return _flagsaver_wrapper
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
class _FlagOverrider:
|
| 301 |
+
"""Overrides flags for the duration of the decorated function call.
|
| 302 |
+
|
| 303 |
+
It also restores all original values of flags after decorated method
|
| 304 |
+
completes.
|
| 305 |
+
"""
|
| 306 |
+
|
| 307 |
+
def __init__(self, **overrides: Any):
|
| 308 |
+
self._overrides = overrides
|
| 309 |
+
self._saved_flag_values = None
|
| 310 |
+
|
| 311 |
+
def __call__(self, func: _CallableT) -> _CallableT:
|
| 312 |
+
if inspect.isclass(func):
|
| 313 |
+
raise TypeError('flagsaver cannot be applied to a class.')
|
| 314 |
+
return _wrap(self.__class__, func, self._overrides)
|
| 315 |
+
|
| 316 |
+
def __enter__(self):
|
| 317 |
+
self._saved_flag_values = save_flag_values(FLAGS)
|
| 318 |
+
try:
|
| 319 |
+
FLAGS._set_attributes(**self._overrides)
|
| 320 |
+
except:
|
| 321 |
+
# It may fail because of flag validators.
|
| 322 |
+
restore_flag_values(self._saved_flag_values, FLAGS)
|
| 323 |
+
raise
|
| 324 |
+
|
| 325 |
+
def __exit__(self, exc_type, exc_value, traceback):
|
| 326 |
+
restore_flag_values(self._saved_flag_values, FLAGS)
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
class _ParsingFlagOverrider(_FlagOverrider):
|
| 330 |
+
"""Context manager for overriding flags.
|
| 331 |
+
|
| 332 |
+
Simulates command line parsing.
|
| 333 |
+
|
| 334 |
+
This is simlar to _FlagOverrider except that all **overrides should be
|
| 335 |
+
strings or sequences of strings, and when context is entered this class calls
|
| 336 |
+
.parse(value)
|
| 337 |
+
|
| 338 |
+
This results in the flags having .present set properly.
|
| 339 |
+
"""
|
| 340 |
+
|
| 341 |
+
def __init__(self, **overrides: Union[str, Sequence[str]]):
|
| 342 |
+
for flag_name, new_value in overrides.items():
|
| 343 |
+
if isinstance(new_value, str):
|
| 344 |
+
continue
|
| 345 |
+
if (isinstance(new_value, collections.abc.Sequence) and
|
| 346 |
+
all(isinstance(single_value, str) for single_value in new_value)):
|
| 347 |
+
continue
|
| 348 |
+
raise TypeError(
|
| 349 |
+
f'flagsaver.as_parsed() cannot parse {flag_name}. Expected a single '
|
| 350 |
+
f'string or sequence of strings but {type(new_value)} was provided.')
|
| 351 |
+
super().__init__(**overrides)
|
| 352 |
+
|
| 353 |
+
def __enter__(self):
|
| 354 |
+
self._saved_flag_values = save_flag_values(FLAGS)
|
| 355 |
+
try:
|
| 356 |
+
for flag_name, unparsed_value in self._overrides.items():
|
| 357 |
+
# LINT.IfChange(flag_override_parsing)
|
| 358 |
+
FLAGS[flag_name].parse(unparsed_value)
|
| 359 |
+
FLAGS[flag_name].using_default_value = False
|
| 360 |
+
# LINT.ThenChange()
|
| 361 |
+
|
| 362 |
+
# Perform the validation on all modified flags. This is something that
|
| 363 |
+
# FLAGS._set_attributes() does for you in _FlagOverrider.
|
| 364 |
+
for flag_name in self._overrides:
|
| 365 |
+
FLAGS._assert_validators(FLAGS[flag_name].validators)
|
| 366 |
+
|
| 367 |
+
except KeyError as e:
|
| 368 |
+
# If a flag doesn't exist, an UnrecognizedFlagError is more specific.
|
| 369 |
+
restore_flag_values(self._saved_flag_values, FLAGS)
|
| 370 |
+
raise flags.UnrecognizedFlagError('Unknown command line flag.') from e
|
| 371 |
+
|
| 372 |
+
except:
|
| 373 |
+
# It may fail because of flag validators or general parsing issues.
|
| 374 |
+
restore_flag_values(self._saved_flag_values, FLAGS)
|
| 375 |
+
raise
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def _copy_flag_dict(flag: flags.Flag) -> Dict[str, Any]:
|
| 379 |
+
"""Returns a copy of the flag object's ``__dict__``.
|
| 380 |
+
|
| 381 |
+
It's mostly a shallow copy of the ``__dict__``, except it also does a shallow
|
| 382 |
+
copy of the validator list.
|
| 383 |
+
|
| 384 |
+
Args:
|
| 385 |
+
flag: flags.Flag, the flag to copy.
|
| 386 |
+
|
| 387 |
+
Returns:
|
| 388 |
+
A copy of the flag object's ``__dict__``.
|
| 389 |
+
"""
|
| 390 |
+
copy = flag.__dict__.copy()
|
| 391 |
+
copy['_value'] = flag.value # Ensure correct restore for C++ flags.
|
| 392 |
+
copy['validators'] = list(flag.validators)
|
| 393 |
+
return copy
|
lib/python3.10/site-packages/absl/testing/parameterized.py
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Adds support for parameterized tests to Python's unittest TestCase class.
|
| 16 |
+
|
| 17 |
+
A parameterized test is a method in a test case that is invoked with different
|
| 18 |
+
argument tuples.
|
| 19 |
+
|
| 20 |
+
A simple example::
|
| 21 |
+
|
| 22 |
+
class AdditionExample(parameterized.TestCase):
|
| 23 |
+
@parameterized.parameters(
|
| 24 |
+
(1, 2, 3),
|
| 25 |
+
(4, 5, 9),
|
| 26 |
+
(1, 1, 3))
|
| 27 |
+
def testAddition(self, op1, op2, result):
|
| 28 |
+
self.assertEqual(result, op1 + op2)
|
| 29 |
+
|
| 30 |
+
Each invocation is a separate test case and properly isolated just
|
| 31 |
+
like a normal test method, with its own setUp/tearDown cycle. In the
|
| 32 |
+
example above, there are three separate testcases, one of which will
|
| 33 |
+
fail due to an assertion error (1 + 1 != 3).
|
| 34 |
+
|
| 35 |
+
Parameters for individual test cases can be tuples (with positional parameters)
|
| 36 |
+
or dictionaries (with named parameters)::
|
| 37 |
+
|
| 38 |
+
class AdditionExample(parameterized.TestCase):
|
| 39 |
+
@parameterized.parameters(
|
| 40 |
+
{'op1': 1, 'op2': 2, 'result': 3},
|
| 41 |
+
{'op1': 4, 'op2': 5, 'result': 9},
|
| 42 |
+
)
|
| 43 |
+
def testAddition(self, op1, op2, result):
|
| 44 |
+
self.assertEqual(result, op1 + op2)
|
| 45 |
+
|
| 46 |
+
If a parameterized test fails, the error message will show the
|
| 47 |
+
original test name and the parameters for that test.
|
| 48 |
+
|
| 49 |
+
The id method of the test, used internally by the unittest framework, is also
|
| 50 |
+
modified to show the arguments (but note that the name reported by `id()`
|
| 51 |
+
doesn't match the actual test name, see below). To make sure that test names
|
| 52 |
+
stay the same across several invocations, object representations like::
|
| 53 |
+
|
| 54 |
+
>>> class Foo(object):
|
| 55 |
+
... pass
|
| 56 |
+
>>> repr(Foo())
|
| 57 |
+
'<__main__.Foo object at 0x23d8610>'
|
| 58 |
+
|
| 59 |
+
are turned into ``__main__.Foo``. When selecting a subset of test cases to run
|
| 60 |
+
on the command-line, the test cases contain an index suffix for each argument
|
| 61 |
+
in the order they were passed to :func:`parameters` (eg. testAddition0,
|
| 62 |
+
testAddition1, etc.) This naming scheme is subject to change; for more reliable
|
| 63 |
+
and stable names, especially in test logs, use :func:`named_parameters` instead.
|
| 64 |
+
|
| 65 |
+
Tests using :func:`named_parameters` are similar to :func:`parameters`, except
|
| 66 |
+
only tuples or dicts of args are supported. For tuples, the first parameter arg
|
| 67 |
+
has to be a string (or an object that returns an apt name when converted via
|
| 68 |
+
``str()``). For dicts, a value for the key ``testcase_name`` must be present and
|
| 69 |
+
must be a string (or an object that returns an apt name when converted via
|
| 70 |
+
``str()``)::
|
| 71 |
+
|
| 72 |
+
class NamedExample(parameterized.TestCase):
|
| 73 |
+
@parameterized.named_parameters(
|
| 74 |
+
('Normal', 'aa', 'aaa', True),
|
| 75 |
+
('EmptyPrefix', '', 'abc', True),
|
| 76 |
+
('BothEmpty', '', '', True))
|
| 77 |
+
def testStartsWith(self, prefix, string, result):
|
| 78 |
+
self.assertEqual(result, string.startswith(prefix))
|
| 79 |
+
|
| 80 |
+
class NamedExample(parameterized.TestCase):
|
| 81 |
+
@parameterized.named_parameters(
|
| 82 |
+
{'testcase_name': 'Normal',
|
| 83 |
+
'result': True, 'string': 'aaa', 'prefix': 'aa'},
|
| 84 |
+
{'testcase_name': 'EmptyPrefix',
|
| 85 |
+
'result': True, 'string': 'abc', 'prefix': ''},
|
| 86 |
+
{'testcase_name': 'BothEmpty',
|
| 87 |
+
'result': True, 'string': '', 'prefix': ''})
|
| 88 |
+
def testStartsWith(self, prefix, string, result):
|
| 89 |
+
self.assertEqual(result, string.startswith(prefix))
|
| 90 |
+
|
| 91 |
+
Named tests also have the benefit that they can be run individually
|
| 92 |
+
from the command line::
|
| 93 |
+
|
| 94 |
+
$ testmodule.py NamedExample.testStartsWithNormal
|
| 95 |
+
.
|
| 96 |
+
--------------------------------------------------------------------
|
| 97 |
+
Ran 1 test in 0.000s
|
| 98 |
+
|
| 99 |
+
OK
|
| 100 |
+
|
| 101 |
+
Parameterized Classes
|
| 102 |
+
=====================
|
| 103 |
+
|
| 104 |
+
If invocation arguments are shared across test methods in a single
|
| 105 |
+
TestCase class, instead of decorating all test methods
|
| 106 |
+
individually, the class itself can be decorated::
|
| 107 |
+
|
| 108 |
+
@parameterized.parameters(
|
| 109 |
+
(1, 2, 3),
|
| 110 |
+
(4, 5, 9))
|
| 111 |
+
class ArithmeticTest(parameterized.TestCase):
|
| 112 |
+
def testAdd(self, arg1, arg2, result):
|
| 113 |
+
self.assertEqual(arg1 + arg2, result)
|
| 114 |
+
|
| 115 |
+
def testSubtract(self, arg1, arg2, result):
|
| 116 |
+
self.assertEqual(result - arg1, arg2)
|
| 117 |
+
|
| 118 |
+
Inputs from Iterables
|
| 119 |
+
=====================
|
| 120 |
+
|
| 121 |
+
If parameters should be shared across several test cases, or are dynamically
|
| 122 |
+
created from other sources, a single non-tuple iterable can be passed into
|
| 123 |
+
the decorator. This iterable will be used to obtain the test cases::
|
| 124 |
+
|
| 125 |
+
class AdditionExample(parameterized.TestCase):
|
| 126 |
+
@parameterized.parameters(
|
| 127 |
+
c.op1, c.op2, c.result for c in testcases
|
| 128 |
+
)
|
| 129 |
+
def testAddition(self, op1, op2, result):
|
| 130 |
+
self.assertEqual(result, op1 + op2)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
Single-Argument Test Methods
|
| 134 |
+
============================
|
| 135 |
+
|
| 136 |
+
If a test method takes only one argument, the single arguments must not be
|
| 137 |
+
wrapped into a tuple::
|
| 138 |
+
|
| 139 |
+
class NegativeNumberExample(parameterized.TestCase):
|
| 140 |
+
@parameterized.parameters(
|
| 141 |
+
-1, -3, -4, -5
|
| 142 |
+
)
|
| 143 |
+
def testIsNegative(self, arg):
|
| 144 |
+
self.assertTrue(IsNegative(arg))
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
List/tuple as a Single Argument
|
| 148 |
+
===============================
|
| 149 |
+
|
| 150 |
+
If a test method takes a single argument of a list/tuple, it must be wrapped
|
| 151 |
+
inside a tuple::
|
| 152 |
+
|
| 153 |
+
class ZeroSumExample(parameterized.TestCase):
|
| 154 |
+
@parameterized.parameters(
|
| 155 |
+
([-1, 0, 1], ),
|
| 156 |
+
([-2, 0, 2], ),
|
| 157 |
+
)
|
| 158 |
+
def testSumIsZero(self, arg):
|
| 159 |
+
self.assertEqual(0, sum(arg))
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
Cartesian product of Parameter Values as Parameterized Test Cases
|
| 163 |
+
=================================================================
|
| 164 |
+
|
| 165 |
+
If required to test method over a cartesian product of parameters,
|
| 166 |
+
`parameterized.product` may be used to facilitate generation of parameters
|
| 167 |
+
test combinations::
|
| 168 |
+
|
| 169 |
+
class TestModuloExample(parameterized.TestCase):
|
| 170 |
+
@parameterized.product(
|
| 171 |
+
num=[0, 20, 80],
|
| 172 |
+
modulo=[2, 4],
|
| 173 |
+
expected=[0]
|
| 174 |
+
)
|
| 175 |
+
def testModuloResult(self, num, modulo, expected):
|
| 176 |
+
self.assertEqual(expected, num % modulo)
|
| 177 |
+
|
| 178 |
+
This results in 6 test cases being created - one for each combination of the
|
| 179 |
+
parameters. It is also possible to supply sequences of keyword argument dicts
|
| 180 |
+
as elements of the cartesian product::
|
| 181 |
+
|
| 182 |
+
@parameterized.product(
|
| 183 |
+
(dict(num=5, modulo=3, expected=2),
|
| 184 |
+
dict(num=7, modulo=4, expected=3)),
|
| 185 |
+
dtype=(int, float)
|
| 186 |
+
)
|
| 187 |
+
def testModuloResult(self, num, modulo, expected, dtype):
|
| 188 |
+
self.assertEqual(expected, dtype(num) % modulo)
|
| 189 |
+
|
| 190 |
+
This results in 4 test cases being created - for each of the two sets of test
|
| 191 |
+
data (supplied as kwarg dicts) and for each of the two data types (supplied as
|
| 192 |
+
a named parameter). Multiple keyword argument dicts may be supplied if required.
|
| 193 |
+
|
| 194 |
+
Async Support
|
| 195 |
+
=============
|
| 196 |
+
|
| 197 |
+
If a test needs to call async functions, it can inherit from both
|
| 198 |
+
parameterized.TestCase and another TestCase that supports async calls, such
|
| 199 |
+
as [asynctest](https://github.com/Martiusweb/asynctest)::
|
| 200 |
+
|
| 201 |
+
import asynctest
|
| 202 |
+
|
| 203 |
+
class AsyncExample(parameterized.TestCase, asynctest.TestCase):
|
| 204 |
+
@parameterized.parameters(
|
| 205 |
+
('a', 1),
|
| 206 |
+
('b', 2),
|
| 207 |
+
)
|
| 208 |
+
async def testSomeAsyncFunction(self, arg, expected):
|
| 209 |
+
actual = await someAsyncFunction(arg)
|
| 210 |
+
self.assertEqual(actual, expected)
|
| 211 |
+
"""
|
| 212 |
+
|
| 213 |
+
from collections import abc
|
| 214 |
+
import functools
|
| 215 |
+
import inspect
|
| 216 |
+
import itertools
|
| 217 |
+
import re
|
| 218 |
+
import types
|
| 219 |
+
import unittest
|
| 220 |
+
import warnings
|
| 221 |
+
|
| 222 |
+
from absl.testing import absltest
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
_ADDR_RE = re.compile(r'\<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+\>')
|
| 226 |
+
_NAMED = object()
|
| 227 |
+
_ARGUMENT_REPR = object()
|
| 228 |
+
_NAMED_DICT_KEY = 'testcase_name'
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class NoTestsError(Exception):
|
| 232 |
+
"""Raised when parameterized decorators do not generate any tests."""
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class DuplicateTestNameError(Exception):
|
| 236 |
+
"""Raised when a parameterized test has the same test name multiple times."""
|
| 237 |
+
|
| 238 |
+
def __init__(self, test_class_name, new_test_name, original_test_name):
|
| 239 |
+
super().__init__(
|
| 240 |
+
'Duplicate parameterized test name in {}: generated test name {!r} '
|
| 241 |
+
'(generated from {!r}) already exists. Consider using '
|
| 242 |
+
'named_parameters() to give your tests unique names and/or renaming '
|
| 243 |
+
'the conflicting test method.'.format(
|
| 244 |
+
test_class_name, new_test_name, original_test_name
|
| 245 |
+
)
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def _clean_repr(obj):
|
| 250 |
+
return _ADDR_RE.sub(r'<\1>', repr(obj))
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def _non_string_or_bytes_iterable(obj):
|
| 254 |
+
return (isinstance(obj, abc.Iterable) and not isinstance(obj, str) and
|
| 255 |
+
not isinstance(obj, bytes))
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def _format_parameter_list(testcase_params):
|
| 259 |
+
if isinstance(testcase_params, abc.Mapping):
|
| 260 |
+
return ', '.join('%s=%s' % (argname, _clean_repr(value))
|
| 261 |
+
for argname, value in testcase_params.items())
|
| 262 |
+
elif _non_string_or_bytes_iterable(testcase_params):
|
| 263 |
+
return ', '.join(map(_clean_repr, testcase_params))
|
| 264 |
+
else:
|
| 265 |
+
return _format_parameter_list((testcase_params,))
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def _async_wrapped(func):
|
| 269 |
+
@functools.wraps(func)
|
| 270 |
+
async def wrapper(*args, **kwargs):
|
| 271 |
+
return await func(*args, **kwargs)
|
| 272 |
+
return wrapper
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
class _ParameterizedTestIter:
|
| 276 |
+
"""Callable and iterable class for producing new test cases."""
|
| 277 |
+
|
| 278 |
+
def __init__(self, test_method, testcases, naming_type, original_name=None):
|
| 279 |
+
"""Returns concrete test functions for a test and a list of parameters.
|
| 280 |
+
|
| 281 |
+
The naming_type is used to determine the name of the concrete
|
| 282 |
+
functions as reported by the unittest framework. If naming_type is
|
| 283 |
+
_FIRST_ARG, the testcases must be tuples, and the first element must
|
| 284 |
+
have a string representation that is a valid Python identifier.
|
| 285 |
+
|
| 286 |
+
Args:
|
| 287 |
+
test_method: The decorated test method.
|
| 288 |
+
testcases: (list of tuple/dict) A list of parameter tuples/dicts for
|
| 289 |
+
individual test invocations.
|
| 290 |
+
naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR.
|
| 291 |
+
original_name: The original test method name. When decorated on a test
|
| 292 |
+
method, None is passed to __init__ and test_method.__name__ is used.
|
| 293 |
+
Note test_method.__name__ might be different than the original defined
|
| 294 |
+
test method because of the use of other decorators. A more accurate
|
| 295 |
+
value is set by TestGeneratorMetaclass.__new__ later.
|
| 296 |
+
"""
|
| 297 |
+
self._test_method = test_method
|
| 298 |
+
self.testcases = testcases
|
| 299 |
+
self._naming_type = naming_type
|
| 300 |
+
if original_name is None:
|
| 301 |
+
original_name = test_method.__name__
|
| 302 |
+
self._original_name = original_name
|
| 303 |
+
self.__name__ = _ParameterizedTestIter.__name__
|
| 304 |
+
|
| 305 |
+
def __call__(self, *args, **kwargs):
|
| 306 |
+
raise RuntimeError('You appear to be running a parameterized test case '
|
| 307 |
+
'without having inherited from parameterized.'
|
| 308 |
+
'TestCase. This is bad because none of '
|
| 309 |
+
'your test cases are actually being run. You may also '
|
| 310 |
+
'be using another decorator before the parameterized '
|
| 311 |
+
'one, in which case you should reverse the order.')
|
| 312 |
+
|
| 313 |
+
def __iter__(self):
|
| 314 |
+
test_method = self._test_method
|
| 315 |
+
naming_type = self._naming_type
|
| 316 |
+
|
| 317 |
+
def make_bound_param_test(testcase_params):
|
| 318 |
+
@functools.wraps(test_method)
|
| 319 |
+
def bound_param_test(self):
|
| 320 |
+
if isinstance(testcase_params, abc.Mapping):
|
| 321 |
+
return test_method(self, **testcase_params)
|
| 322 |
+
elif _non_string_or_bytes_iterable(testcase_params):
|
| 323 |
+
return test_method(self, *testcase_params)
|
| 324 |
+
else:
|
| 325 |
+
return test_method(self, testcase_params)
|
| 326 |
+
|
| 327 |
+
if naming_type is _NAMED:
|
| 328 |
+
# Signal the metaclass that the name of the test function is unique
|
| 329 |
+
# and descriptive.
|
| 330 |
+
bound_param_test.__x_use_name__ = True
|
| 331 |
+
|
| 332 |
+
testcase_name = None
|
| 333 |
+
if isinstance(testcase_params, abc.Mapping):
|
| 334 |
+
if _NAMED_DICT_KEY not in testcase_params:
|
| 335 |
+
raise RuntimeError(
|
| 336 |
+
'Dict for named tests must contain key "%s"' % _NAMED_DICT_KEY)
|
| 337 |
+
# Create a new dict to avoid modifying the supplied testcase_params.
|
| 338 |
+
testcase_name = testcase_params[_NAMED_DICT_KEY]
|
| 339 |
+
testcase_params = {
|
| 340 |
+
k: v for k, v in testcase_params.items() if k != _NAMED_DICT_KEY
|
| 341 |
+
}
|
| 342 |
+
elif _non_string_or_bytes_iterable(testcase_params):
|
| 343 |
+
if not isinstance(testcase_params[0], str):
|
| 344 |
+
raise RuntimeError(
|
| 345 |
+
'The first element of named test parameters is the test name '
|
| 346 |
+
'suffix and must be a string')
|
| 347 |
+
testcase_name = testcase_params[0]
|
| 348 |
+
testcase_params = testcase_params[1:]
|
| 349 |
+
else:
|
| 350 |
+
raise RuntimeError(
|
| 351 |
+
'Named tests must be passed a dict or non-string iterable.')
|
| 352 |
+
|
| 353 |
+
test_method_name = self._original_name
|
| 354 |
+
# Support PEP-8 underscore style for test naming if used.
|
| 355 |
+
if (test_method_name.startswith('test_')
|
| 356 |
+
and testcase_name
|
| 357 |
+
and not testcase_name.startswith('_')):
|
| 358 |
+
test_method_name += '_'
|
| 359 |
+
|
| 360 |
+
bound_param_test.__name__ = test_method_name + str(testcase_name)
|
| 361 |
+
elif naming_type is _ARGUMENT_REPR:
|
| 362 |
+
# If it's a generator, convert it to a tuple and treat them as
|
| 363 |
+
# parameters.
|
| 364 |
+
if isinstance(testcase_params, types.GeneratorType):
|
| 365 |
+
testcase_params = tuple(testcase_params)
|
| 366 |
+
# The metaclass creates a unique, but non-descriptive method name for
|
| 367 |
+
# _ARGUMENT_REPR tests using an indexed suffix.
|
| 368 |
+
# To keep test names descriptive, only the original method name is used.
|
| 369 |
+
# To make sure test names are unique, we add a unique descriptive suffix
|
| 370 |
+
# __x_params_repr__ for every test.
|
| 371 |
+
params_repr = '(%s)' % (_format_parameter_list(testcase_params),)
|
| 372 |
+
bound_param_test.__x_params_repr__ = params_repr
|
| 373 |
+
else:
|
| 374 |
+
raise RuntimeError('%s is not a valid naming type.' % (naming_type,))
|
| 375 |
+
|
| 376 |
+
bound_param_test.__doc__ = '%s(%s)' % (
|
| 377 |
+
bound_param_test.__name__, _format_parameter_list(testcase_params))
|
| 378 |
+
if test_method.__doc__:
|
| 379 |
+
bound_param_test.__doc__ += '\n%s' % (test_method.__doc__,)
|
| 380 |
+
if inspect.iscoroutinefunction(test_method):
|
| 381 |
+
return _async_wrapped(bound_param_test)
|
| 382 |
+
return bound_param_test
|
| 383 |
+
|
| 384 |
+
return (make_bound_param_test(c) for c in self.testcases)
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
def _modify_class(class_object, testcases, naming_type):
|
| 388 |
+
assert not getattr(class_object, '_test_params_reprs', None), (
|
| 389 |
+
'Cannot add parameters to %s. Either it already has parameterized '
|
| 390 |
+
'methods, or its super class is also a parameterized class.' % (
|
| 391 |
+
class_object,))
|
| 392 |
+
# NOTE: _test_params_repr is private to parameterized.TestCase and it's
|
| 393 |
+
# metaclass; do not use it outside of those classes.
|
| 394 |
+
class_object._test_params_reprs = test_params_reprs = {}
|
| 395 |
+
for name, obj in class_object.__dict__.copy().items():
|
| 396 |
+
if (name.startswith(unittest.TestLoader.testMethodPrefix)
|
| 397 |
+
and isinstance(obj, types.FunctionType)):
|
| 398 |
+
delattr(class_object, name)
|
| 399 |
+
methods = {}
|
| 400 |
+
_update_class_dict_for_param_test_case(
|
| 401 |
+
class_object.__name__, methods, test_params_reprs, name,
|
| 402 |
+
_ParameterizedTestIter(obj, testcases, naming_type, name))
|
| 403 |
+
for meth_name, meth in methods.items():
|
| 404 |
+
setattr(class_object, meth_name, meth)
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
def _parameter_decorator(naming_type, testcases):
|
| 408 |
+
"""Implementation of the parameterization decorators.
|
| 409 |
+
|
| 410 |
+
Args:
|
| 411 |
+
naming_type: The naming type.
|
| 412 |
+
testcases: Testcase parameters.
|
| 413 |
+
|
| 414 |
+
Raises:
|
| 415 |
+
NoTestsError: Raised when the decorator generates no tests.
|
| 416 |
+
|
| 417 |
+
Returns:
|
| 418 |
+
A function for modifying the decorated object.
|
| 419 |
+
"""
|
| 420 |
+
def _apply(obj):
|
| 421 |
+
if isinstance(obj, type):
|
| 422 |
+
_modify_class(obj, testcases, naming_type)
|
| 423 |
+
return obj
|
| 424 |
+
else:
|
| 425 |
+
return _ParameterizedTestIter(obj, testcases, naming_type)
|
| 426 |
+
|
| 427 |
+
if (len(testcases) == 1 and
|
| 428 |
+
not isinstance(testcases[0], tuple) and
|
| 429 |
+
not isinstance(testcases[0], abc.Mapping)):
|
| 430 |
+
# Support using a single non-tuple parameter as a list of test cases.
|
| 431 |
+
# Note that the single non-tuple parameter can't be Mapping either, which
|
| 432 |
+
# means a single dict parameter case.
|
| 433 |
+
assert _non_string_or_bytes_iterable(testcases[0]), (
|
| 434 |
+
'Single parameter argument must be a non-string non-Mapping iterable')
|
| 435 |
+
testcases = testcases[0]
|
| 436 |
+
|
| 437 |
+
if not isinstance(testcases, abc.Sequence):
|
| 438 |
+
testcases = list(testcases)
|
| 439 |
+
if not testcases:
|
| 440 |
+
raise NoTestsError(
|
| 441 |
+
'parameterized test decorators did not generate any tests. '
|
| 442 |
+
'Make sure you specify non-empty parameters, '
|
| 443 |
+
'and do not reuse generators more than once.')
|
| 444 |
+
|
| 445 |
+
return _apply
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def parameters(*testcases):
|
| 449 |
+
"""A decorator for creating parameterized tests.
|
| 450 |
+
|
| 451 |
+
See the module docstring for a usage example.
|
| 452 |
+
|
| 453 |
+
Args:
|
| 454 |
+
*testcases: Parameters for the decorated method, either a single
|
| 455 |
+
iterable, or a list of tuples/dicts/objects (for tests with only one
|
| 456 |
+
argument).
|
| 457 |
+
|
| 458 |
+
Raises:
|
| 459 |
+
NoTestsError: Raised when the decorator generates no tests.
|
| 460 |
+
|
| 461 |
+
Returns:
|
| 462 |
+
A test generator to be handled by TestGeneratorMetaclass.
|
| 463 |
+
"""
|
| 464 |
+
return _parameter_decorator(_ARGUMENT_REPR, testcases)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
def named_parameters(*testcases):
|
| 468 |
+
"""A decorator for creating parameterized tests.
|
| 469 |
+
|
| 470 |
+
See the module docstring for a usage example. For every parameter tuple
|
| 471 |
+
passed, the first element of the tuple should be a string and will be appended
|
| 472 |
+
to the name of the test method. Each parameter dict passed must have a value
|
| 473 |
+
for the key "testcase_name", the string representation of that value will be
|
| 474 |
+
appended to the name of the test method.
|
| 475 |
+
|
| 476 |
+
Args:
|
| 477 |
+
*testcases: Parameters for the decorated method, either a single iterable,
|
| 478 |
+
or a list of tuples or dicts.
|
| 479 |
+
|
| 480 |
+
Raises:
|
| 481 |
+
NoTestsError: Raised when the decorator generates no tests.
|
| 482 |
+
|
| 483 |
+
Returns:
|
| 484 |
+
A test generator to be handled by TestGeneratorMetaclass.
|
| 485 |
+
"""
|
| 486 |
+
return _parameter_decorator(_NAMED, testcases)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def product(*kwargs_seqs, **testgrid):
|
| 490 |
+
"""A decorator for running tests over cartesian product of parameters values.
|
| 491 |
+
|
| 492 |
+
See the module docstring for a usage example. The test will be run for every
|
| 493 |
+
possible combination of the parameters.
|
| 494 |
+
|
| 495 |
+
Args:
|
| 496 |
+
*kwargs_seqs: Each positional parameter is a sequence of keyword arg dicts;
|
| 497 |
+
every test case generated will include exactly one kwargs dict from each
|
| 498 |
+
positional parameter; these will then be merged to form an overall list
|
| 499 |
+
of arguments for the test case.
|
| 500 |
+
**testgrid: A mapping of parameter names and their possible values. Possible
|
| 501 |
+
values should given as either a list or a tuple.
|
| 502 |
+
|
| 503 |
+
Raises:
|
| 504 |
+
NoTestsError: Raised when the decorator generates no tests.
|
| 505 |
+
|
| 506 |
+
Returns:
|
| 507 |
+
A test generator to be handled by TestGeneratorMetaclass.
|
| 508 |
+
"""
|
| 509 |
+
|
| 510 |
+
for name, values in testgrid.items():
|
| 511 |
+
assert isinstance(values, (list, tuple)), (
|
| 512 |
+
'Values of {} must be given as list or tuple, found {}'.format(
|
| 513 |
+
name, type(values)))
|
| 514 |
+
|
| 515 |
+
prior_arg_names = set()
|
| 516 |
+
for kwargs_seq in kwargs_seqs:
|
| 517 |
+
assert ((isinstance(kwargs_seq, (list, tuple))) and
|
| 518 |
+
all(isinstance(kwargs, dict) for kwargs in kwargs_seq)), (
|
| 519 |
+
'Positional parameters must be a sequence of keyword arg'
|
| 520 |
+
'dicts, found {}'
|
| 521 |
+
.format(kwargs_seq))
|
| 522 |
+
if kwargs_seq:
|
| 523 |
+
arg_names = set(kwargs_seq[0])
|
| 524 |
+
assert all(set(kwargs) == arg_names for kwargs in kwargs_seq), (
|
| 525 |
+
'Keyword argument dicts within a single parameter must all have the '
|
| 526 |
+
'same keys, found {}'.format(kwargs_seq))
|
| 527 |
+
assert not (arg_names & prior_arg_names), (
|
| 528 |
+
'Keyword argument dict sequences must all have distinct argument '
|
| 529 |
+
'names, found duplicate(s) {}'
|
| 530 |
+
.format(sorted(arg_names & prior_arg_names)))
|
| 531 |
+
prior_arg_names |= arg_names
|
| 532 |
+
|
| 533 |
+
assert not (prior_arg_names & set(testgrid)), (
|
| 534 |
+
'Arguments supplied in kwargs dicts in positional parameters must not '
|
| 535 |
+
'overlap with arguments supplied as named parameters; found duplicate '
|
| 536 |
+
'argument(s) {}'.format(sorted(prior_arg_names & set(testgrid))))
|
| 537 |
+
|
| 538 |
+
# Convert testgrid into a sequence of sequences of kwargs dicts and combine
|
| 539 |
+
# with the positional parameters.
|
| 540 |
+
# So foo=[1,2], bar=[3,4] --> [[{foo: 1}, {foo: 2}], [{bar: 3, bar: 4}]]
|
| 541 |
+
testgrid = (tuple({k: v} for v in vs) for k, vs in testgrid.items())
|
| 542 |
+
testgrid = tuple(kwargs_seqs) + tuple(testgrid)
|
| 543 |
+
|
| 544 |
+
# Create all possible combinations of parameters as a cartesian product
|
| 545 |
+
# of parameter values.
|
| 546 |
+
testcases = [
|
| 547 |
+
dict(itertools.chain.from_iterable(case.items()
|
| 548 |
+
for case in cases))
|
| 549 |
+
for cases in itertools.product(*testgrid)
|
| 550 |
+
]
|
| 551 |
+
return _parameter_decorator(_ARGUMENT_REPR, testcases)
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
class TestGeneratorMetaclass(type):
|
| 555 |
+
"""Metaclass for adding tests generated by parameterized decorators."""
|
| 556 |
+
|
| 557 |
+
def __new__(cls, class_name, bases, dct):
|
| 558 |
+
# NOTE: _test_params_repr is private to parameterized.TestCase and it's
|
| 559 |
+
# metaclass; do not use it outside of those classes.
|
| 560 |
+
test_params_reprs = dct.setdefault('_test_params_reprs', {})
|
| 561 |
+
for name, obj in dct.copy().items():
|
| 562 |
+
if (name.startswith(unittest.TestLoader.testMethodPrefix) and
|
| 563 |
+
_non_string_or_bytes_iterable(obj)):
|
| 564 |
+
# NOTE: `obj` might not be a _ParameterizedTestIter in two cases:
|
| 565 |
+
# 1. a class-level iterable named test* that isn't a test, such as
|
| 566 |
+
# a list of something. Such attributes get deleted from the class.
|
| 567 |
+
#
|
| 568 |
+
# 2. If a decorator is applied to the parameterized test, e.g.
|
| 569 |
+
# @morestuff
|
| 570 |
+
# @parameterized.parameters(...)
|
| 571 |
+
# def test_foo(...): ...
|
| 572 |
+
#
|
| 573 |
+
# This is OK so long as the underlying parameterized function state
|
| 574 |
+
# is forwarded (e.g. using functool.wraps() and **without**
|
| 575 |
+
# accessing explicitly accessing the internal attributes.
|
| 576 |
+
if isinstance(obj, _ParameterizedTestIter):
|
| 577 |
+
# Update the original test method name so it's more accurate.
|
| 578 |
+
# The mismatch might happen when another decorator is used inside
|
| 579 |
+
# the parameterized decrators, and the inner decorator doesn't
|
| 580 |
+
# preserve its __name__.
|
| 581 |
+
obj._original_name = name
|
| 582 |
+
iterator = iter(obj)
|
| 583 |
+
dct.pop(name)
|
| 584 |
+
_update_class_dict_for_param_test_case(
|
| 585 |
+
class_name, dct, test_params_reprs, name, iterator)
|
| 586 |
+
# If the base class is a subclass of parameterized.TestCase, inherit its
|
| 587 |
+
# _test_params_reprs too.
|
| 588 |
+
for base in bases:
|
| 589 |
+
# Check if the base has _test_params_reprs first, then check if it's a
|
| 590 |
+
# subclass of parameterized.TestCase. Otherwise when this is called for
|
| 591 |
+
# the parameterized.TestCase definition itself, this raises because
|
| 592 |
+
# itself is not defined yet. This works as long as absltest.TestCase does
|
| 593 |
+
# not define _test_params_reprs.
|
| 594 |
+
base_test_params_reprs = getattr(base, '_test_params_reprs', None)
|
| 595 |
+
if base_test_params_reprs and issubclass(base, TestCase):
|
| 596 |
+
for test_method, test_method_id in base_test_params_reprs.items():
|
| 597 |
+
# test_method may both exists in base and this class.
|
| 598 |
+
# This class's method overrides base class's.
|
| 599 |
+
# That's why it should only inherit it if it does not exist.
|
| 600 |
+
test_params_reprs.setdefault(test_method, test_method_id)
|
| 601 |
+
|
| 602 |
+
return type.__new__(cls, class_name, bases, dct)
|
| 603 |
+
|
| 604 |
+
|
| 605 |
+
def _update_class_dict_for_param_test_case(
|
| 606 |
+
test_class_name, dct, test_params_reprs, name, iterator):
|
| 607 |
+
"""Adds individual test cases to a dictionary.
|
| 608 |
+
|
| 609 |
+
Args:
|
| 610 |
+
test_class_name: The name of the class tests are added to.
|
| 611 |
+
dct: The target dictionary.
|
| 612 |
+
test_params_reprs: The dictionary for mapping names to test IDs.
|
| 613 |
+
name: The original name of the test case.
|
| 614 |
+
iterator: The iterator generating the individual test cases.
|
| 615 |
+
|
| 616 |
+
Raises:
|
| 617 |
+
DuplicateTestNameError: Raised when a test name occurs multiple times.
|
| 618 |
+
RuntimeError: If non-parameterized functions are generated.
|
| 619 |
+
"""
|
| 620 |
+
for idx, func in enumerate(iterator):
|
| 621 |
+
assert callable(func), 'Test generators must yield callables, got %r' % (
|
| 622 |
+
func,)
|
| 623 |
+
if not (getattr(func, '__x_use_name__', None) or
|
| 624 |
+
getattr(func, '__x_params_repr__', None)):
|
| 625 |
+
raise RuntimeError(
|
| 626 |
+
'{}.{} generated a test function without using the parameterized '
|
| 627 |
+
'decorators. Only tests generated using the decorators are '
|
| 628 |
+
'supported.'.format(test_class_name, name))
|
| 629 |
+
|
| 630 |
+
if getattr(func, '__x_use_name__', False):
|
| 631 |
+
original_name = func.__name__
|
| 632 |
+
new_name = original_name
|
| 633 |
+
else:
|
| 634 |
+
original_name = name
|
| 635 |
+
new_name = '%s%d' % (original_name, idx)
|
| 636 |
+
|
| 637 |
+
if new_name in dct:
|
| 638 |
+
raise DuplicateTestNameError(test_class_name, new_name, original_name)
|
| 639 |
+
|
| 640 |
+
dct[new_name] = func
|
| 641 |
+
test_params_reprs[new_name] = getattr(func, '__x_params_repr__', '')
|
| 642 |
+
|
| 643 |
+
|
| 644 |
+
class TestCase(absltest.TestCase, metaclass=TestGeneratorMetaclass):
|
| 645 |
+
"""Base class for test cases using the parameters decorator."""
|
| 646 |
+
|
| 647 |
+
# visibility: private; do not call outside this class.
|
| 648 |
+
def _get_params_repr(self):
|
| 649 |
+
return self._test_params_reprs.get(self._testMethodName, '')
|
| 650 |
+
|
| 651 |
+
def __str__(self):
|
| 652 |
+
params_repr = self._get_params_repr()
|
| 653 |
+
if params_repr:
|
| 654 |
+
params_repr = ' ' + params_repr
|
| 655 |
+
return '{}{} ({})'.format(
|
| 656 |
+
self._testMethodName, params_repr,
|
| 657 |
+
unittest.util.strclass(self.__class__))
|
| 658 |
+
|
| 659 |
+
def id(self):
|
| 660 |
+
"""Returns the descriptive ID of the test.
|
| 661 |
+
|
| 662 |
+
This is used internally by the unittesting framework to get a name
|
| 663 |
+
for the test to be used in reports.
|
| 664 |
+
|
| 665 |
+
Returns:
|
| 666 |
+
The test id.
|
| 667 |
+
"""
|
| 668 |
+
base = super().id()
|
| 669 |
+
params_repr = self._get_params_repr()
|
| 670 |
+
if params_repr:
|
| 671 |
+
# We include the params in the id so that, when reported in the
|
| 672 |
+
# test.xml file, the value is more informative than just "test_foo0".
|
| 673 |
+
# Use a space to separate them so that it's copy/paste friendly and
|
| 674 |
+
# easy to identify the actual test id.
|
| 675 |
+
return f'{base} {params_repr}'
|
| 676 |
+
else:
|
| 677 |
+
return base
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
# This function is kept CamelCase because it's used as a class's base class.
|
| 681 |
+
def CoopTestCase(other_base_class) -> type: # pylint: disable=invalid-name, g-bare-generic
|
| 682 |
+
"""Returns a new base class with a cooperative metaclass base.
|
| 683 |
+
|
| 684 |
+
This enables the TestCase to be used in combination
|
| 685 |
+
with other base classes that have custom metaclasses, such as
|
| 686 |
+
``mox.MoxTestBase``.
|
| 687 |
+
|
| 688 |
+
Only works with metaclasses that do not override ``type.__new__``.
|
| 689 |
+
|
| 690 |
+
Example::
|
| 691 |
+
|
| 692 |
+
from absl.testing import parameterized
|
| 693 |
+
|
| 694 |
+
class ExampleTest(parameterized.CoopTestCase(OtherTestCase)):
|
| 695 |
+
...
|
| 696 |
+
|
| 697 |
+
Args:
|
| 698 |
+
other_base_class: (class) A test case base class.
|
| 699 |
+
|
| 700 |
+
Returns:
|
| 701 |
+
A new class object.
|
| 702 |
+
"""
|
| 703 |
+
# If the other base class has a metaclass of 'type' then trying to combine
|
| 704 |
+
# the metaclasses will result in an MRO error. So simply combine them and
|
| 705 |
+
# return.
|
| 706 |
+
if type(other_base_class) == type: # pylint: disable=unidiomatic-typecheck
|
| 707 |
+
warnings.warn(
|
| 708 |
+
'CoopTestCase is only necessary when combining with a class that uses'
|
| 709 |
+
' a metaclass. Use multiple inheritance like this instead: class'
|
| 710 |
+
f' ExampleTest(paramaterized.TestCase, {other_base_class.__name__}):',
|
| 711 |
+
stacklevel=2,
|
| 712 |
+
)
|
| 713 |
+
|
| 714 |
+
class CoopTestCaseBase(other_base_class, TestCase):
|
| 715 |
+
pass
|
| 716 |
+
|
| 717 |
+
return CoopTestCaseBase
|
| 718 |
+
else:
|
| 719 |
+
|
| 720 |
+
class CoopMetaclass(type(other_base_class), TestGeneratorMetaclass): # type: ignore # pylint: disable=unused-variable
|
| 721 |
+
pass
|
| 722 |
+
|
| 723 |
+
class CoopTestCaseBase(other_base_class, TestCase, metaclass=CoopMetaclass): # type: ignore
|
| 724 |
+
pass
|
| 725 |
+
|
| 726 |
+
return CoopTestCaseBase
|
lib/python3.10/site-packages/absl/testing/xml_reporter.py
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2017 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""A Python test reporter that generates test reports in JUnit XML format."""
|
| 16 |
+
|
| 17 |
+
import datetime
|
| 18 |
+
import re
|
| 19 |
+
import sys
|
| 20 |
+
import threading
|
| 21 |
+
import time
|
| 22 |
+
import traceback
|
| 23 |
+
from typing import Any, Dict
|
| 24 |
+
import unittest
|
| 25 |
+
from xml.sax import saxutils
|
| 26 |
+
from absl.testing import _pretty_print_reporter
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# See http://www.w3.org/TR/REC-xml/#NT-Char
|
| 30 |
+
_bad_control_character_codes = set(range(0, 0x20)) - {0x9, 0xA, 0xD}
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
_control_character_conversions = {
|
| 34 |
+
chr(i): f'\\x{i:02x}' for i in _bad_control_character_codes
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
_escape_xml_attr_conversions = {
|
| 39 |
+
'"': '"',
|
| 40 |
+
"'": ''',
|
| 41 |
+
'\n': '
',
|
| 42 |
+
'\t': '	',
|
| 43 |
+
'\r': '
',
|
| 44 |
+
' ': ' '}
|
| 45 |
+
_escape_xml_attr_conversions.update(_control_character_conversions)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# When class or module level function fails, unittest/suite.py adds a
|
| 49 |
+
# _ErrorHolder instance instead of a real TestCase, and it has a description
|
| 50 |
+
# like "setUpClass (__main__.MyTestCase)".
|
| 51 |
+
_CLASS_OR_MODULE_LEVEL_TEST_DESC_REGEX = re.compile(r'^(\w+) \((\S+)\)$')
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
# NOTE: while saxutils.quoteattr() theoretically does the same thing; it
|
| 55 |
+
# seems to often end up being too smart for it's own good not escaping properly.
|
| 56 |
+
# This function is much more reliable.
|
| 57 |
+
def _escape_xml_attr(content):
|
| 58 |
+
"""Escapes xml attributes."""
|
| 59 |
+
# Note: saxutils doesn't escape the quotes.
|
| 60 |
+
return saxutils.escape(content, _escape_xml_attr_conversions)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _escape_cdata(s):
|
| 64 |
+
"""Escapes a string to be used as XML CDATA.
|
| 65 |
+
|
| 66 |
+
CDATA characters are treated strictly as character data, not as XML markup,
|
| 67 |
+
but there are still certain restrictions on them.
|
| 68 |
+
|
| 69 |
+
Args:
|
| 70 |
+
s: the string to be escaped.
|
| 71 |
+
Returns:
|
| 72 |
+
An escaped version of the input string.
|
| 73 |
+
"""
|
| 74 |
+
for char, escaped in _control_character_conversions.items():
|
| 75 |
+
s = s.replace(char, escaped)
|
| 76 |
+
return s.replace(']]>', ']] >')
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _iso8601_timestamp(timestamp):
|
| 80 |
+
"""Produces an ISO8601 datetime.
|
| 81 |
+
|
| 82 |
+
Args:
|
| 83 |
+
timestamp: an Epoch based timestamp in seconds.
|
| 84 |
+
|
| 85 |
+
Returns:
|
| 86 |
+
A iso8601 format timestamp if the input is a valid timestamp, None otherwise
|
| 87 |
+
"""
|
| 88 |
+
if timestamp is None or timestamp < 0:
|
| 89 |
+
return None
|
| 90 |
+
return datetime.datetime.fromtimestamp(
|
| 91 |
+
timestamp, tz=datetime.timezone.utc).isoformat()
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _print_xml_element_header(element, attributes, stream, indentation=''):
|
| 95 |
+
"""Prints an XML header of an arbitrary element.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
element: element name (testsuites, testsuite, testcase)
|
| 99 |
+
attributes: 2-tuple list with (attributes, values) already escaped
|
| 100 |
+
stream: output stream to write test report XML to
|
| 101 |
+
indentation: indentation added to the element header
|
| 102 |
+
"""
|
| 103 |
+
stream.write('%s<%s' % (indentation, element))
|
| 104 |
+
for attribute in attributes:
|
| 105 |
+
if (len(attribute) == 2 and attribute[0] is not None and
|
| 106 |
+
attribute[1] is not None):
|
| 107 |
+
stream.write(' %s="%s"' % (attribute[0], attribute[1]))
|
| 108 |
+
stream.write('>\n')
|
| 109 |
+
|
| 110 |
+
# Copy time.time which ensures the real time is used internally.
|
| 111 |
+
# This prevents bad interactions with tests that stub out time.
|
| 112 |
+
_time_copy = time.time
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def _safe_str(obj: object) -> str:
|
| 116 |
+
"""Returns a string representation of an object."""
|
| 117 |
+
try:
|
| 118 |
+
return str(obj)
|
| 119 |
+
except Exception: # pylint: disable=broad-except
|
| 120 |
+
return '<unprintable %s.%s object>' % (
|
| 121 |
+
type(obj).__module__,
|
| 122 |
+
type(obj).__name__,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class _TestCaseResult:
|
| 127 |
+
"""Private helper for _TextAndXMLTestResult that represents a test result.
|
| 128 |
+
|
| 129 |
+
Attributes:
|
| 130 |
+
test: A TestCase instance of an individual test method.
|
| 131 |
+
name: The name of the individual test method.
|
| 132 |
+
full_class_name: The full name of the test class.
|
| 133 |
+
run_time: The duration (in seconds) it took to run the test.
|
| 134 |
+
start_time: Epoch relative timestamp of when test started (in seconds)
|
| 135 |
+
errors: A list of error 4-tuples. Error tuple entries are
|
| 136 |
+
1) a string identifier of either "failure" or "error"
|
| 137 |
+
2) an exception_type
|
| 138 |
+
3) an exception_message
|
| 139 |
+
4) a string version of a sys.exc_info()-style tuple of values
|
| 140 |
+
('error', err[0], err[1], self._exc_info_to_string(err))
|
| 141 |
+
If the length of errors is 0, then the test is either passed or
|
| 142 |
+
skipped.
|
| 143 |
+
skip_reason: A string explaining why the test was skipped.
|
| 144 |
+
"""
|
| 145 |
+
|
| 146 |
+
def __init__(self, test):
|
| 147 |
+
self.run_time = -1
|
| 148 |
+
self.start_time = -1
|
| 149 |
+
self.skip_reason = None
|
| 150 |
+
self.errors = []
|
| 151 |
+
self.test = test
|
| 152 |
+
|
| 153 |
+
# Parse the test id to get its test name and full class path.
|
| 154 |
+
# Unfortunately there is no better way of knowning the test and class.
|
| 155 |
+
# Worse, unittest uses _ErrorHandler instances to represent class / module
|
| 156 |
+
# level failures.
|
| 157 |
+
test_desc = test.id() or str(test)
|
| 158 |
+
# Check if it's something like "setUpClass (__main__.TestCase)".
|
| 159 |
+
match = _CLASS_OR_MODULE_LEVEL_TEST_DESC_REGEX.match(test_desc)
|
| 160 |
+
if match:
|
| 161 |
+
name = match.group(1)
|
| 162 |
+
full_class_name = match.group(2)
|
| 163 |
+
else:
|
| 164 |
+
class_name = unittest.util.strclass(test.__class__)
|
| 165 |
+
if isinstance(test, unittest.case._SubTest):
|
| 166 |
+
# If the test case is a _SubTest, the real TestCase instance is
|
| 167 |
+
# available as _SubTest.test_case.
|
| 168 |
+
class_name = unittest.util.strclass(test.test_case.__class__)
|
| 169 |
+
if test_desc.startswith(class_name + '.'):
|
| 170 |
+
# In a typical unittest.TestCase scenario, test.id() returns with
|
| 171 |
+
# a class name formatted using unittest.util.strclass.
|
| 172 |
+
name = test_desc[len(class_name)+1:]
|
| 173 |
+
full_class_name = class_name
|
| 174 |
+
else:
|
| 175 |
+
# Otherwise make a best effort to guess the test name and full class
|
| 176 |
+
# path.
|
| 177 |
+
parts = test_desc.rsplit('.', 1)
|
| 178 |
+
name = parts[-1]
|
| 179 |
+
full_class_name = parts[0] if len(parts) == 2 else ''
|
| 180 |
+
self.name = _escape_xml_attr(name)
|
| 181 |
+
self.full_class_name = _escape_xml_attr(full_class_name)
|
| 182 |
+
|
| 183 |
+
def set_run_time(self, time_in_secs):
|
| 184 |
+
self.run_time = time_in_secs
|
| 185 |
+
|
| 186 |
+
def set_start_time(self, time_in_secs):
|
| 187 |
+
self.start_time = time_in_secs
|
| 188 |
+
|
| 189 |
+
def print_xml_summary(self, stream):
|
| 190 |
+
"""Prints an XML Summary of a TestCase.
|
| 191 |
+
|
| 192 |
+
Status and result are populated as per JUnit XML test result reporter.
|
| 193 |
+
A test that has been skipped will always have a skip reason,
|
| 194 |
+
as every skip method in Python's unittest requires the reason arg to be
|
| 195 |
+
passed.
|
| 196 |
+
|
| 197 |
+
Args:
|
| 198 |
+
stream: output stream to write test report XML to
|
| 199 |
+
"""
|
| 200 |
+
|
| 201 |
+
if self.skip_reason is None:
|
| 202 |
+
status = 'run'
|
| 203 |
+
result = 'completed'
|
| 204 |
+
else:
|
| 205 |
+
status = 'notrun'
|
| 206 |
+
result = 'suppressed'
|
| 207 |
+
|
| 208 |
+
test_case_attributes = [
|
| 209 |
+
('name', '%s' % self.name),
|
| 210 |
+
('status', '%s' % status),
|
| 211 |
+
('result', '%s' % result),
|
| 212 |
+
('time', '%.3f' % self.run_time),
|
| 213 |
+
('classname', self.full_class_name),
|
| 214 |
+
('timestamp', _iso8601_timestamp(self.start_time)),
|
| 215 |
+
]
|
| 216 |
+
_print_xml_element_header('testcase', test_case_attributes, stream, ' ')
|
| 217 |
+
self._print_testcase_details(stream)
|
| 218 |
+
stream.write(' </testcase>\n')
|
| 219 |
+
|
| 220 |
+
def _print_testcase_details(self, stream):
|
| 221 |
+
for error in self.errors:
|
| 222 |
+
outcome, exception_type, message, error_msg = error # pylint: disable=unpacking-non-sequence
|
| 223 |
+
message = _escape_xml_attr(_safe_str(message))
|
| 224 |
+
exception_type = _escape_xml_attr(str(exception_type))
|
| 225 |
+
error_msg = _escape_cdata(error_msg)
|
| 226 |
+
stream.write(' <%s message="%s" type="%s"><![CDATA[%s]]></%s>\n'
|
| 227 |
+
% (outcome, message, exception_type, error_msg, outcome))
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
class _TestSuiteResult:
|
| 231 |
+
"""Private helper for _TextAndXMLTestResult."""
|
| 232 |
+
|
| 233 |
+
def __init__(self):
|
| 234 |
+
self.suites = {}
|
| 235 |
+
self.failure_counts = {}
|
| 236 |
+
self.error_counts = {}
|
| 237 |
+
self.overall_start_time = -1
|
| 238 |
+
self.overall_end_time = -1
|
| 239 |
+
self._testsuites_properties = {}
|
| 240 |
+
|
| 241 |
+
def add_test_case_result(self, test_case_result):
|
| 242 |
+
suite_name = type(test_case_result.test).__name__
|
| 243 |
+
if suite_name == '_ErrorHolder':
|
| 244 |
+
# _ErrorHolder is a special case created by unittest for class / module
|
| 245 |
+
# level functions.
|
| 246 |
+
suite_name = test_case_result.full_class_name.rsplit('.')[-1]
|
| 247 |
+
if isinstance(test_case_result.test, unittest.case._SubTest):
|
| 248 |
+
# If the test case is a _SubTest, the real TestCase instance is
|
| 249 |
+
# available as _SubTest.test_case.
|
| 250 |
+
suite_name = type(test_case_result.test.test_case).__name__
|
| 251 |
+
|
| 252 |
+
self._setup_test_suite(suite_name)
|
| 253 |
+
self.suites[suite_name].append(test_case_result)
|
| 254 |
+
for error in test_case_result.errors:
|
| 255 |
+
# Only count the first failure or error so that the sum is equal to the
|
| 256 |
+
# total number of *testcases* that have failures or errors.
|
| 257 |
+
if error[0] == 'failure':
|
| 258 |
+
self.failure_counts[suite_name] += 1
|
| 259 |
+
break
|
| 260 |
+
elif error[0] == 'error':
|
| 261 |
+
self.error_counts[suite_name] += 1
|
| 262 |
+
break
|
| 263 |
+
|
| 264 |
+
def print_xml_summary(self, stream):
|
| 265 |
+
overall_test_count = sum(len(x) for x in self.suites.values())
|
| 266 |
+
overall_failures = sum(self.failure_counts.values())
|
| 267 |
+
overall_errors = sum(self.error_counts.values())
|
| 268 |
+
overall_attributes = [
|
| 269 |
+
('name', ''),
|
| 270 |
+
('tests', '%d' % overall_test_count),
|
| 271 |
+
('failures', '%d' % overall_failures),
|
| 272 |
+
('errors', '%d' % overall_errors),
|
| 273 |
+
('time', '%.3f' % (self.overall_end_time - self.overall_start_time)),
|
| 274 |
+
('timestamp', _iso8601_timestamp(self.overall_start_time)),
|
| 275 |
+
]
|
| 276 |
+
_print_xml_element_header('testsuites', overall_attributes, stream)
|
| 277 |
+
if self._testsuites_properties:
|
| 278 |
+
stream.write(' <properties>\n')
|
| 279 |
+
for name, value in sorted(self._testsuites_properties.items()):
|
| 280 |
+
stream.write(' <property name="%s" value="%s"></property>\n' %
|
| 281 |
+
(_escape_xml_attr(name), _escape_xml_attr(str(value))))
|
| 282 |
+
stream.write(' </properties>\n')
|
| 283 |
+
|
| 284 |
+
for suite_name in self.suites:
|
| 285 |
+
suite = self.suites[suite_name]
|
| 286 |
+
suite_end_time = max(x.start_time + x.run_time for x in suite)
|
| 287 |
+
suite_start_time = min(x.start_time for x in suite)
|
| 288 |
+
failures = self.failure_counts[suite_name]
|
| 289 |
+
errors = self.error_counts[suite_name]
|
| 290 |
+
suite_attributes = [
|
| 291 |
+
('name', '%s' % suite_name),
|
| 292 |
+
('tests', '%d' % len(suite)),
|
| 293 |
+
('failures', '%d' % failures),
|
| 294 |
+
('errors', '%d' % errors),
|
| 295 |
+
('time', '%.3f' % (suite_end_time - suite_start_time)),
|
| 296 |
+
('timestamp', _iso8601_timestamp(suite_start_time)),
|
| 297 |
+
]
|
| 298 |
+
_print_xml_element_header('testsuite', suite_attributes, stream)
|
| 299 |
+
|
| 300 |
+
# test_case_result entries are not guaranteed to be in any user-friendly
|
| 301 |
+
# order, especially when using subtests. So sort them.
|
| 302 |
+
for test_case_result in sorted(suite, key=lambda t: t.name):
|
| 303 |
+
test_case_result.print_xml_summary(stream)
|
| 304 |
+
stream.write('</testsuite>\n')
|
| 305 |
+
stream.write('</testsuites>\n')
|
| 306 |
+
|
| 307 |
+
def _setup_test_suite(self, suite_name):
|
| 308 |
+
"""Adds a test suite to the set of suites tracked by this test run.
|
| 309 |
+
|
| 310 |
+
Args:
|
| 311 |
+
suite_name: string, The name of the test suite being initialized.
|
| 312 |
+
"""
|
| 313 |
+
if suite_name in self.suites:
|
| 314 |
+
return
|
| 315 |
+
self.suites[suite_name] = []
|
| 316 |
+
self.failure_counts[suite_name] = 0
|
| 317 |
+
self.error_counts[suite_name] = 0
|
| 318 |
+
|
| 319 |
+
def set_end_time(self, timestamp_in_secs):
|
| 320 |
+
"""Sets the start timestamp of this test suite.
|
| 321 |
+
|
| 322 |
+
Args:
|
| 323 |
+
timestamp_in_secs: timestamp in seconds since epoch
|
| 324 |
+
"""
|
| 325 |
+
self.overall_end_time = timestamp_in_secs
|
| 326 |
+
|
| 327 |
+
def set_start_time(self, timestamp_in_secs):
|
| 328 |
+
"""Sets the end timestamp of this test suite.
|
| 329 |
+
|
| 330 |
+
Args:
|
| 331 |
+
timestamp_in_secs: timestamp in seconds since epoch
|
| 332 |
+
"""
|
| 333 |
+
self.overall_start_time = timestamp_in_secs
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
class _TextAndXMLTestResult(_pretty_print_reporter.TextTestResult):
|
| 337 |
+
"""Private TestResult class that produces both formatted text results and XML.
|
| 338 |
+
|
| 339 |
+
Used by TextAndXMLTestRunner.
|
| 340 |
+
"""
|
| 341 |
+
|
| 342 |
+
_TEST_SUITE_RESULT_CLASS = _TestSuiteResult
|
| 343 |
+
_TEST_CASE_RESULT_CLASS = _TestCaseResult
|
| 344 |
+
|
| 345 |
+
def __init__(self, xml_stream, stream, descriptions, verbosity,
|
| 346 |
+
time_getter=_time_copy, testsuites_properties=None):
|
| 347 |
+
super().__init__(stream, descriptions, verbosity)
|
| 348 |
+
self.xml_stream = xml_stream
|
| 349 |
+
self.pending_test_case_results = {}
|
| 350 |
+
self.suite = self._TEST_SUITE_RESULT_CLASS()
|
| 351 |
+
if testsuites_properties:
|
| 352 |
+
self.suite._testsuites_properties = testsuites_properties
|
| 353 |
+
self.time_getter = time_getter
|
| 354 |
+
|
| 355 |
+
# This lock guards any mutations on pending_test_case_results.
|
| 356 |
+
self._pending_test_case_results_lock = threading.RLock()
|
| 357 |
+
|
| 358 |
+
def startTest(self, test):
|
| 359 |
+
self.start_time = self.time_getter()
|
| 360 |
+
super().startTest(test)
|
| 361 |
+
|
| 362 |
+
def stopTest(self, test):
|
| 363 |
+
# Grabbing the write lock to avoid conflicting with stopTestRun.
|
| 364 |
+
with self._pending_test_case_results_lock:
|
| 365 |
+
super().stopTest(test)
|
| 366 |
+
result = self.get_pending_test_case_result(test)
|
| 367 |
+
if not result:
|
| 368 |
+
test_name = test.id() or str(test)
|
| 369 |
+
sys.stderr.write('No pending test case: %s\n' % test_name)
|
| 370 |
+
return
|
| 371 |
+
if getattr(self, 'start_time', None) is None:
|
| 372 |
+
# startTest may not be called for skipped tests since Python 3.12.1.
|
| 373 |
+
self.start_time = self.time_getter()
|
| 374 |
+
test_id = id(test)
|
| 375 |
+
run_time = self.time_getter() - self.start_time
|
| 376 |
+
result.set_run_time(run_time)
|
| 377 |
+
result.set_start_time(self.start_time)
|
| 378 |
+
self.suite.add_test_case_result(result)
|
| 379 |
+
del self.pending_test_case_results[test_id]
|
| 380 |
+
|
| 381 |
+
def startTestRun(self):
|
| 382 |
+
self.suite.set_start_time(self.time_getter())
|
| 383 |
+
super().startTestRun()
|
| 384 |
+
|
| 385 |
+
def stopTestRun(self):
|
| 386 |
+
self.suite.set_end_time(self.time_getter())
|
| 387 |
+
# All pending_test_case_results will be added to the suite and removed from
|
| 388 |
+
# the pending_test_case_results dictionary. Grabbing the write lock to avoid
|
| 389 |
+
# results from being added during this process to avoid duplicating adds or
|
| 390 |
+
# accidentally erasing newly appended pending results.
|
| 391 |
+
with self._pending_test_case_results_lock:
|
| 392 |
+
# Errors in the test fixture (setUpModule, tearDownModule,
|
| 393 |
+
# setUpClass, tearDownClass) can leave a pending result which
|
| 394 |
+
# never gets added to the suite. The runner calls stopTestRun
|
| 395 |
+
# which gives us an opportunity to add these errors for
|
| 396 |
+
# reporting here.
|
| 397 |
+
for test_id in self.pending_test_case_results:
|
| 398 |
+
result = self.pending_test_case_results[test_id]
|
| 399 |
+
if getattr(self, 'start_time', None) is not None:
|
| 400 |
+
run_time = self.suite.overall_end_time - self.start_time
|
| 401 |
+
result.set_run_time(run_time)
|
| 402 |
+
result.set_start_time(self.start_time)
|
| 403 |
+
self.suite.add_test_case_result(result)
|
| 404 |
+
self.pending_test_case_results.clear()
|
| 405 |
+
|
| 406 |
+
def _exc_info_to_string(self, err, test=None):
|
| 407 |
+
"""Converts a sys.exc_info()-style tuple of values into a string.
|
| 408 |
+
|
| 409 |
+
This method must be overridden because the method signature in
|
| 410 |
+
unittest.TestResult changed between Python 2.2 and 2.4.
|
| 411 |
+
|
| 412 |
+
Args:
|
| 413 |
+
err: A sys.exc_info() tuple of values for an error.
|
| 414 |
+
test: The test method.
|
| 415 |
+
|
| 416 |
+
Returns:
|
| 417 |
+
A formatted exception string.
|
| 418 |
+
"""
|
| 419 |
+
if test:
|
| 420 |
+
return super()._exc_info_to_string(err, test)
|
| 421 |
+
return ''.join(traceback.format_exception(*err))
|
| 422 |
+
|
| 423 |
+
def add_pending_test_case_result(self, test, error_summary=None,
|
| 424 |
+
skip_reason=None):
|
| 425 |
+
"""Adds result information to a test case result which may still be running.
|
| 426 |
+
|
| 427 |
+
If a result entry for the test already exists, add_pending_test_case_result
|
| 428 |
+
will add error summary tuples and/or overwrite skip_reason for the result.
|
| 429 |
+
If it does not yet exist, a result entry will be created.
|
| 430 |
+
Note that a test result is considered to have been run and passed
|
| 431 |
+
only if there are no errors or skip_reason.
|
| 432 |
+
|
| 433 |
+
Args:
|
| 434 |
+
test: A test method as defined by unittest
|
| 435 |
+
error_summary: A 4-tuple with the following entries:
|
| 436 |
+
1) a string identifier of either "failure" or "error"
|
| 437 |
+
2) an exception_type
|
| 438 |
+
3) an exception_message
|
| 439 |
+
4) a string version of a sys.exc_info()-style tuple of values
|
| 440 |
+
('error', err[0], err[1], self._exc_info_to_string(err))
|
| 441 |
+
If the length of errors is 0, then the test is either passed or
|
| 442 |
+
skipped.
|
| 443 |
+
skip_reason: a string explaining why the test was skipped
|
| 444 |
+
"""
|
| 445 |
+
with self._pending_test_case_results_lock:
|
| 446 |
+
test_id = id(test)
|
| 447 |
+
if test_id not in self.pending_test_case_results:
|
| 448 |
+
self.pending_test_case_results[test_id] = self._TEST_CASE_RESULT_CLASS(
|
| 449 |
+
test)
|
| 450 |
+
if error_summary:
|
| 451 |
+
self.pending_test_case_results[test_id].errors.append(error_summary)
|
| 452 |
+
if skip_reason:
|
| 453 |
+
self.pending_test_case_results[test_id].skip_reason = skip_reason
|
| 454 |
+
|
| 455 |
+
def delete_pending_test_case_result(self, test):
|
| 456 |
+
with self._pending_test_case_results_lock:
|
| 457 |
+
test_id = id(test)
|
| 458 |
+
del self.pending_test_case_results[test_id]
|
| 459 |
+
|
| 460 |
+
def get_pending_test_case_result(self, test):
|
| 461 |
+
test_id = id(test)
|
| 462 |
+
return self.pending_test_case_results.get(test_id, None)
|
| 463 |
+
|
| 464 |
+
def addSuccess(self, test):
|
| 465 |
+
super().addSuccess(test)
|
| 466 |
+
self.add_pending_test_case_result(test)
|
| 467 |
+
|
| 468 |
+
def addError(self, test, err):
|
| 469 |
+
super().addError(test, err)
|
| 470 |
+
error_summary = ('error', err[0], err[1],
|
| 471 |
+
self._exc_info_to_string(err, test=test))
|
| 472 |
+
self.add_pending_test_case_result(test, error_summary=error_summary)
|
| 473 |
+
|
| 474 |
+
def addFailure(self, test, err):
|
| 475 |
+
super().addFailure(test, err)
|
| 476 |
+
error_summary = ('failure', err[0], err[1],
|
| 477 |
+
self._exc_info_to_string(err, test=test))
|
| 478 |
+
self.add_pending_test_case_result(test, error_summary=error_summary)
|
| 479 |
+
|
| 480 |
+
def addSkip(self, test, reason):
|
| 481 |
+
super().addSkip(test, reason)
|
| 482 |
+
self.add_pending_test_case_result(test, skip_reason=reason)
|
| 483 |
+
|
| 484 |
+
def addExpectedFailure(self, test, err):
|
| 485 |
+
super().addExpectedFailure(test, err)
|
| 486 |
+
if callable(getattr(test, 'recordProperty', None)):
|
| 487 |
+
test.recordProperty('EXPECTED_FAILURE',
|
| 488 |
+
self._exc_info_to_string(err, test=test))
|
| 489 |
+
self.add_pending_test_case_result(test)
|
| 490 |
+
|
| 491 |
+
def addUnexpectedSuccess(self, test):
|
| 492 |
+
super().addUnexpectedSuccess(test)
|
| 493 |
+
test_name = test.id() or str(test)
|
| 494 |
+
error_summary = ('error', '', '',
|
| 495 |
+
'Test case %s should have failed, but passed.'
|
| 496 |
+
% (test_name))
|
| 497 |
+
self.add_pending_test_case_result(test, error_summary=error_summary)
|
| 498 |
+
|
| 499 |
+
def addSubTest(self, test, subtest, err): # pylint: disable=invalid-name
|
| 500 |
+
super().addSubTest(test, subtest, err)
|
| 501 |
+
if err is not None:
|
| 502 |
+
if issubclass(err[0], test.failureException):
|
| 503 |
+
error_summary = ('failure', err[0], err[1],
|
| 504 |
+
self._exc_info_to_string(err, test=test))
|
| 505 |
+
else:
|
| 506 |
+
error_summary = ('error', err[0], err[1],
|
| 507 |
+
self._exc_info_to_string(err, test=test))
|
| 508 |
+
else:
|
| 509 |
+
error_summary = None
|
| 510 |
+
self.add_pending_test_case_result(subtest, error_summary=error_summary)
|
| 511 |
+
|
| 512 |
+
def printErrors(self):
|
| 513 |
+
super().printErrors()
|
| 514 |
+
self.xml_stream.write('<?xml version="1.0"?>\n')
|
| 515 |
+
self.suite.print_xml_summary(self.xml_stream)
|
| 516 |
+
|
| 517 |
+
|
| 518 |
+
class TextAndXMLTestRunner(unittest.TextTestRunner):
|
| 519 |
+
"""A test runner that produces both formatted text results and XML.
|
| 520 |
+
|
| 521 |
+
It prints out the names of tests as they are run, errors as they
|
| 522 |
+
occur, and a summary of the results at the end of the test run.
|
| 523 |
+
"""
|
| 524 |
+
|
| 525 |
+
_TEST_RESULT_CLASS = _TextAndXMLTestResult
|
| 526 |
+
|
| 527 |
+
_xml_stream = None
|
| 528 |
+
_testsuites_properties: Dict[Any, Any] = {}
|
| 529 |
+
|
| 530 |
+
def __init__(self, xml_stream=None, *args, **kwargs):
|
| 531 |
+
"""Initialize a TextAndXMLTestRunner.
|
| 532 |
+
|
| 533 |
+
Args:
|
| 534 |
+
xml_stream: file-like or None; XML-formatted test results are output
|
| 535 |
+
via this object's write() method. If None (the default), the
|
| 536 |
+
new instance behaves as described in the set_default_xml_stream method
|
| 537 |
+
documentation below.
|
| 538 |
+
*args: passed unmodified to unittest.TextTestRunner.__init__.
|
| 539 |
+
**kwargs: passed unmodified to unittest.TextTestRunner.__init__.
|
| 540 |
+
"""
|
| 541 |
+
super().__init__(*args, **kwargs)
|
| 542 |
+
if xml_stream is not None:
|
| 543 |
+
self._xml_stream = xml_stream
|
| 544 |
+
# else, do not set self._xml_stream to None -- this allows implicit fallback
|
| 545 |
+
# to the class attribute's value.
|
| 546 |
+
|
| 547 |
+
@classmethod
|
| 548 |
+
def set_default_xml_stream(cls, xml_stream):
|
| 549 |
+
"""Sets the default XML stream for the class.
|
| 550 |
+
|
| 551 |
+
Args:
|
| 552 |
+
xml_stream: file-like or None; used for instances when xml_stream is None
|
| 553 |
+
or not passed to their constructors. If None is passed, instances
|
| 554 |
+
created with xml_stream=None will act as ordinary TextTestRunner
|
| 555 |
+
instances; this is the default state before any calls to this method
|
| 556 |
+
have been made.
|
| 557 |
+
"""
|
| 558 |
+
cls._xml_stream = xml_stream
|
| 559 |
+
|
| 560 |
+
def _makeResult(self):
|
| 561 |
+
if self._xml_stream is None:
|
| 562 |
+
return super()._makeResult()
|
| 563 |
+
else:
|
| 564 |
+
return self._TEST_RESULT_CLASS(
|
| 565 |
+
self._xml_stream, self.stream, self.descriptions, self.verbosity,
|
| 566 |
+
testsuites_properties=self._testsuites_properties)
|
| 567 |
+
|
| 568 |
+
@classmethod
|
| 569 |
+
def set_testsuites_property(cls, key, value):
|
| 570 |
+
cls._testsuites_properties[key] = value
|
lib/python3.10/site-packages/absl_py-2.3.1.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
lib/python3.10/site-packages/absl_py-2.3.1.dist-info/METADATA
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: absl-py
|
| 3 |
+
Version: 2.3.1
|
| 4 |
+
Summary: Abseil Python Common Libraries, see https://github.com/abseil/abseil-py.
|
| 5 |
+
Project-URL: Changelog, https://github.com/abseil/abseil-py/blob/main/CHANGELOG.md
|
| 6 |
+
Project-URL: Documentation, https://abseil.io/docs/python/
|
| 7 |
+
Project-URL: Issues, https://github.com/abseil/abseil-py/issues
|
| 8 |
+
Project-URL: Source, https://github.com/abseil/abseil-py
|
| 9 |
+
Author: The Abseil Authors
|
| 10 |
+
License-Expression: Apache-2.0
|
| 11 |
+
License-File: AUTHORS
|
| 12 |
+
License-File: LICENSE
|
| 13 |
+
Classifier: Intended Audience :: Developers
|
| 14 |
+
Classifier: License :: OSI Approved :: Apache Software License
|
| 15 |
+
Classifier: Operating System :: OS Independent
|
| 16 |
+
Classifier: Programming Language :: Python
|
| 17 |
+
Classifier: Programming Language :: Python :: 3
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 24 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 25 |
+
Requires-Python: >=3.8
|
| 26 |
+
Description-Content-Type: text/markdown
|
| 27 |
+
|
| 28 |
+
[](https://pypi.org/project/absl-py)
|
| 29 |
+
[](https://pypi.org/project/absl-py)
|
| 30 |
+
[](https://github.com/abseil/abseil-py/blob/main/LICENSE)
|
| 31 |
+
[](https://github.com/abseil/abseil-py/actions)
|
| 32 |
+
[](https://pepy.tech/project/absl-py)
|
| 33 |
+
[](https://pepy.tech/project/absl-py)
|
| 34 |
+
|
| 35 |
+
# Abseil Python Common Libraries
|
| 36 |
+
|
| 37 |
+
This repository is a collection of Python library code for building Python
|
| 38 |
+
applications. The code is collected from Google's own Python code base, and has
|
| 39 |
+
been extensively tested and used in production.
|
| 40 |
+
|
| 41 |
+
## Features
|
| 42 |
+
|
| 43 |
+
* Simple application startup
|
| 44 |
+
* Distributed commandline flags system
|
| 45 |
+
* Custom logging module with additional features
|
| 46 |
+
* Testing utilities
|
| 47 |
+
|
| 48 |
+
## Getting Started
|
| 49 |
+
|
| 50 |
+
### Installation
|
| 51 |
+
|
| 52 |
+
To install the package, simply run:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
pip install absl-py
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Or install from source:
|
| 59 |
+
|
| 60 |
+
```bash
|
| 61 |
+
pip install .
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
### Running Tests
|
| 65 |
+
|
| 66 |
+
To run Abseil tests, you can clone the git repo and run
|
| 67 |
+
[bazel](https://bazel.build/):
|
| 68 |
+
|
| 69 |
+
```bash
|
| 70 |
+
git clone https://github.com/abseil/abseil-py.git
|
| 71 |
+
cd abseil-py
|
| 72 |
+
bazel test absl/...
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
Please also validate the type annotations against the latest mypy:
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
pip install mypy
|
| 79 |
+
mypy absl
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
### Example Code
|
| 83 |
+
|
| 84 |
+
Please refer to
|
| 85 |
+
[smoke_tests/sample_app.py](https://github.com/abseil/abseil-py/blob/main/smoke_tests/sample_app.py)
|
| 86 |
+
as an example to get started.
|
| 87 |
+
|
| 88 |
+
## Documentation
|
| 89 |
+
|
| 90 |
+
See the [Abseil Python Developer Guide](https://abseil.io/docs/python/).
|
| 91 |
+
|
| 92 |
+
## Future Releases
|
| 93 |
+
|
| 94 |
+
The current repository includes an initial set of libraries for early adoption.
|
| 95 |
+
More components and interoperability with Abseil C++ Common Libraries
|
| 96 |
+
will come in future releases.
|
| 97 |
+
|
| 98 |
+
## License
|
| 99 |
+
|
| 100 |
+
The Abseil Python library is licensed under the terms of the Apache
|
| 101 |
+
license. See [LICENSE](LICENSE) for more information.
|
lib/python3.10/site-packages/absl_py-2.3.1.dist-info/RECORD
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
absl/__init__.py,sha256=9iJ5PwnoYh12ztceccjD2JbnSvx6d2JVElEurn9cqJk,607
|
| 2 |
+
absl/__pycache__/__init__.cpython-310.pyc,,
|
| 3 |
+
absl/__pycache__/app.cpython-310.pyc,,
|
| 4 |
+
absl/__pycache__/command_name.cpython-310.pyc,,
|
| 5 |
+
absl/app.py,sha256=0WJpjLaYcgcXtl9NTn-i5jSEfAoweMg4SXAel2XLF0Y,15360
|
| 6 |
+
absl/app.pyi,sha256=NdRh0OBqWHKnc3hEjdLaspLdu6aDHn3XP360ZPZeQE8,1885
|
| 7 |
+
absl/command_name.py,sha256=FgWUMHmlX0yQxEuMPXxFxn5ayWWZSLB0cq8Vx361TpU,2283
|
| 8 |
+
absl/flags/__init__.py,sha256=n_uLeSK-15_1DKLb0wXivmt6A8xvADDC87D1yoJdqVU,7665
|
| 9 |
+
absl/flags/__pycache__/__init__.cpython-310.pyc,,
|
| 10 |
+
absl/flags/__pycache__/_argument_parser.cpython-310.pyc,,
|
| 11 |
+
absl/flags/__pycache__/_defines.cpython-310.pyc,,
|
| 12 |
+
absl/flags/__pycache__/_exceptions.cpython-310.pyc,,
|
| 13 |
+
absl/flags/__pycache__/_flag.cpython-310.pyc,,
|
| 14 |
+
absl/flags/__pycache__/_flagvalues.cpython-310.pyc,,
|
| 15 |
+
absl/flags/__pycache__/_helpers.cpython-310.pyc,,
|
| 16 |
+
absl/flags/__pycache__/_validators.cpython-310.pyc,,
|
| 17 |
+
absl/flags/__pycache__/_validators_classes.cpython-310.pyc,,
|
| 18 |
+
absl/flags/__pycache__/argparse_flags.cpython-310.pyc,,
|
| 19 |
+
absl/flags/_argument_parser.py,sha256=arzkHA3CbPJdJ7diqUyskdoaLtKt8Aij1EZCVH_qJHU,20629
|
| 20 |
+
absl/flags/_defines.py,sha256=NAK489NGv3YMvbYj2Jzl6q89HmAtmMI65di1L_RK7XE,52895
|
| 21 |
+
absl/flags/_exceptions.py,sha256=FZzlzhvkjqPImTxXqbS1pSPYKr_TvtOd5ellvoiVLDI,3619
|
| 22 |
+
absl/flags/_flag.py,sha256=mdMJFklKQdCi9WsWPvUlmBRoQ2IAPW0Z0lDIKj0Lsx8,20079
|
| 23 |
+
absl/flags/_flagvalues.py,sha256=cGzMsWxthqGYfpESReRSJCXjpRujosSmTT-efJW3CbQ,54364
|
| 24 |
+
absl/flags/_helpers.py,sha256=MHbgtRkbNpVnrr7_NCdkFi_x3voQa-1-bypqsunHCJE,14154
|
| 25 |
+
absl/flags/_validators.py,sha256=VcsJtZzohliNxsI974NECYpeozD8rswHNHXggrQ4BLo,14140
|
| 26 |
+
absl/flags/_validators_classes.py,sha256=PGUWzO7v3wPOHb9leIKKzry3q-pPeKCoMB_O7prLdnY,6093
|
| 27 |
+
absl/flags/argparse_flags.py,sha256=usJudgMpy3P6Vvq7-LmJNa2Rj3ygHM3hwDTGd1mbAzc,14386
|
| 28 |
+
absl/logging/__init__.py,sha256=XpcmJrFEzDK2iWTlmDwtNSScSiGiNedFibWuW0tWMbk,43583
|
| 29 |
+
absl/logging/__init__.pyi,sha256=39EEBOH_rAyDQJpwyito2vo4IAZP9hnw3-wXC_Gulvc,5925
|
| 30 |
+
absl/logging/__pycache__/__init__.cpython-310.pyc,,
|
| 31 |
+
absl/logging/__pycache__/converter.cpython-310.pyc,,
|
| 32 |
+
absl/logging/converter.py,sha256=6eBymfv9UNkog0BGat4HPWlxC_oSqvHcQ46jnSdtaMg,6323
|
| 33 |
+
absl/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 34 |
+
absl/testing/__init__.py,sha256=7cM57swk2T1Hc5wxmt-JpcaR6xfdPJyL_lyRqgODvuM,584
|
| 35 |
+
absl/testing/__pycache__/__init__.cpython-310.pyc,,
|
| 36 |
+
absl/testing/__pycache__/_bazelize_command.cpython-310.pyc,,
|
| 37 |
+
absl/testing/__pycache__/_pretty_print_reporter.cpython-310.pyc,,
|
| 38 |
+
absl/testing/__pycache__/absltest.cpython-310.pyc,,
|
| 39 |
+
absl/testing/__pycache__/flagsaver.cpython-310.pyc,,
|
| 40 |
+
absl/testing/__pycache__/parameterized.cpython-310.pyc,,
|
| 41 |
+
absl/testing/__pycache__/xml_reporter.cpython-310.pyc,,
|
| 42 |
+
absl/testing/_bazelize_command.py,sha256=qpioV02ln2sBBJ9kdlHgNpKk8_wxdz2hJGKbG6EWZMI,2287
|
| 43 |
+
absl/testing/_pretty_print_reporter.py,sha256=PZh9NXSXBbXDi0FOk-BOmpse8LXa92Er16tgyBRogMs,3065
|
| 44 |
+
absl/testing/absltest.py,sha256=PW4c4SVlOUPSvFIhayimJY2LEbJ7BAojob6u9SBUrck,105675
|
| 45 |
+
absl/testing/flagsaver.py,sha256=HIWzFyayy-Pa8TqTAXF7BtHQ7s9Uk68KE8AVVAM0o0w,13346
|
| 46 |
+
absl/testing/parameterized.py,sha256=TpTlWTUXjikGeUDE45AgubvnmHsPbFNj-wUtAwT-e6E,27817
|
| 47 |
+
absl/testing/xml_reporter.py,sha256=YFkM7SROW8aeoCCn8IsGZ4cqXu4x8MwL1oN42-OtPKI,21430
|
| 48 |
+
absl_py-2.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 49 |
+
absl_py-2.3.1.dist-info/METADATA,sha256=2HZS24vkmHw7GQqUWcDE_FdVXjW1dgVS7pWPdWJ5Yvg,3331
|
| 50 |
+
absl_py-2.3.1.dist-info/RECORD,,
|
| 51 |
+
absl_py-2.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
| 52 |
+
absl_py-2.3.1.dist-info/licenses/AUTHORS,sha256=YoLudsylaQg7W5mLn4FroQMuEnuNx8RpQrhkd_xvv6U,296
|
| 53 |
+
absl_py-2.3.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|