ZhengyangZhang commited on
Commit
bb58fb0
·
verified ·
1 Parent(s): e782272

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/absl/__init__.py +15 -0
  2. lib/python3.12/site-packages/absl/__pycache__/__init__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/absl/__pycache__/app.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/absl/__pycache__/command_name.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/absl/app.py +539 -0
  6. lib/python3.12/site-packages/absl/app.pyi +89 -0
  7. lib/python3.12/site-packages/absl/command_name.py +63 -0
  8. lib/python3.12/site-packages/absl/flags/__init__.py +220 -0
  9. lib/python3.12/site-packages/absl/flags/__pycache__/__init__.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/absl/flags/__pycache__/_argument_parser.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/absl/flags/__pycache__/_defines.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/absl/flags/__pycache__/_exceptions.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/absl/flags/__pycache__/_flag.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/absl/flags/__pycache__/_flagvalues.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/absl/flags/__pycache__/_helpers.cpython-312.pyc +0 -0
  16. lib/python3.12/site-packages/absl/flags/__pycache__/_validators.cpython-312.pyc +0 -0
  17. lib/python3.12/site-packages/absl/flags/__pycache__/_validators_classes.cpython-312.pyc +0 -0
  18. lib/python3.12/site-packages/absl/flags/__pycache__/argparse_flags.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/absl/flags/_argument_parser.py +632 -0
  20. lib/python3.12/site-packages/absl/flags/_defines.py +1702 -0
  21. lib/python3.12/site-packages/absl/flags/_exceptions.py +107 -0
  22. lib/python3.12/site-packages/absl/flags/_flag.py +566 -0
  23. lib/python3.12/site-packages/absl/flags/_flagvalues.py +1552 -0
  24. lib/python3.12/site-packages/absl/flags/_helpers.py +403 -0
  25. lib/python3.12/site-packages/absl/flags/_validators.py +353 -0
  26. lib/python3.12/site-packages/absl/flags/_validators_classes.py +172 -0
  27. lib/python3.12/site-packages/absl/flags/argparse_flags.py +390 -0
  28. lib/python3.12/site-packages/absl/logging/__init__.py +1335 -0
  29. lib/python3.12/site-packages/absl/logging/__init__.pyi +278 -0
  30. lib/python3.12/site-packages/absl/logging/__pycache__/__init__.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/absl/logging/__pycache__/converter.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/absl/logging/converter.py +214 -0
  33. lib/python3.12/site-packages/absl/py.typed +0 -0
  34. lib/python3.12/site-packages/absl/testing/__init__.py +13 -0
  35. lib/python3.12/site-packages/absl/testing/__pycache__/__init__.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/absl/testing/__pycache__/flagsaver.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/absl/testing/__pycache__/parameterized.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/absl/testing/__pycache__/xml_reporter.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/absl/testing/_bazelize_command.py +68 -0
  42. lib/python3.12/site-packages/absl/testing/_pretty_print_reporter.py +92 -0
  43. lib/python3.12/site-packages/absl/testing/absltest.py +0 -0
  44. lib/python3.12/site-packages/absl/testing/flagsaver.py +403 -0
  45. lib/python3.12/site-packages/absl/testing/parameterized.py +726 -0
  46. lib/python3.12/site-packages/absl/testing/xml_reporter.py +570 -0
  47. lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER +1 -0
  48. lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA +84 -0
  49. lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD +57 -0
  50. lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL +4 -0
lib/python3.12/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.4.0'
lib/python3.12/site-packages/absl/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (211 Bytes). View file
 
lib/python3.12/site-packages/absl/__pycache__/app.cpython-312.pyc ADDED
Binary file (20.8 kB). View file
 
lib/python3.12/site-packages/absl/__pycache__/command_name.cpython-312.pyc ADDED
Binary file (2.34 kB). View file
 
lib/python3.12/site-packages/absl/app.py ADDED
@@ -0,0 +1,539 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 importlib
31
+ import os
32
+ import pdb
33
+ import sys
34
+ import textwrap
35
+ import traceback
36
+
37
+ from absl import command_name
38
+ from absl import flags
39
+ from absl import logging
40
+
41
+ try:
42
+ import faulthandler
43
+ except ImportError:
44
+ faulthandler = None
45
+
46
+ FLAGS = flags.FLAGS
47
+
48
+ flags.DEFINE_boolean(
49
+ 'run_with_pdb',
50
+ False,
51
+ 'Set to true for debug mode. PDB is used by default; $PYTHONBREAKPOINT '
52
+ '(https://docs.python.org/3/using/cmdline.html#envvar-PYTHONBREAKPOINT) '
53
+ 'can be used to specify a custom debugger.',
54
+ )
55
+ flags.DEFINE_boolean(
56
+ 'pdb_post_mortem',
57
+ False,
58
+ 'Set to true to handle uncaught exceptions with the post mortem debugger. '
59
+ 'PDB is used by default; $PYTHONBREAKPOINT '
60
+ '(https://docs.python.org/3/using/cmdline.html#envvar-PYTHONBREAKPOINT) '
61
+ 'can be used to specify a custom one.',
62
+ )
63
+ flags.DEFINE_alias('pdb', 'pdb_post_mortem')
64
+ flags.DEFINE_boolean('run_with_profiling', False,
65
+ 'Set to true for profiling the script. '
66
+ 'Execution will be slower, and the output format might '
67
+ 'change over time.')
68
+ flags.DEFINE_string('profile_file', None,
69
+ 'Dump profile information to a file (for python -m '
70
+ 'pstats). Implies --run_with_profiling.')
71
+ flags.DEFINE_boolean('use_cprofile_for_profiling', True,
72
+ 'Use cProfile instead of the profile module for '
73
+ 'profiling. This has no effect unless '
74
+ '--run_with_profiling is set.')
75
+ flags.DEFINE_boolean('only_check_args', False,
76
+ 'Set to true to validate args and exit.',
77
+ allow_hide_cpp=True)
78
+
79
+
80
+ def _get_debugger_module_with_function(function_name):
81
+ """Provides the `$PYTHONBREAKPOINT` module if it contains `function_name`.
82
+
83
+ Falls back to `pdb` otherwise.
84
+
85
+ Args:
86
+ function_name: The name of the function required.
87
+
88
+ Returns:
89
+ A debugger module providing `function_name`.
90
+ """
91
+ python_breakpoint = os.getenv('PYTHONBREAKPOINT')
92
+ # The special value '0' for `$PYTHONBREAKPOINT` means "do not use a debugger".
93
+ # We don't respect it (if the user explicitly asks to debug) but shouldn't try
94
+ # to import a module with this name.
95
+ if python_breakpoint and python_breakpoint != '0':
96
+ debugger_module_import = python_breakpoint.rsplit('.', 1)[0]
97
+ try:
98
+ debugger_module = importlib.import_module(debugger_module_import)
99
+ except ImportError:
100
+ logging.warning(
101
+ (
102
+ 'Could not import $PYTHONBREAKPOINT debugger module %r, '
103
+ 'falling back to PDB'
104
+ ),
105
+ debugger_module_import,
106
+ )
107
+ else:
108
+ if hasattr(debugger_module, function_name):
109
+ return debugger_module
110
+ logging.warning(
111
+ '$PYTHONBREAKPOINT debugger %r has no function %r, '
112
+ 'falling back to PDB',
113
+ debugger_module_import,
114
+ function_name,
115
+ )
116
+ return pdb
117
+
118
+
119
+ # If main() exits via an abnormal exception, call into these
120
+ # handlers before exiting.
121
+ EXCEPTION_HANDLERS = []
122
+
123
+
124
+ class Error(Exception):
125
+ pass
126
+
127
+
128
+ class UsageError(Error):
129
+ """Exception raised when the arguments supplied by the user are invalid.
130
+
131
+ Raise this when the arguments supplied are invalid from the point of
132
+ view of the application. For example when two mutually exclusive
133
+ flags have been supplied or when there are not enough non-flag
134
+ arguments. It is distinct from flags.Error which covers the lower
135
+ level of parsing and validating individual flags.
136
+ """
137
+
138
+ def __init__(self, message, exitcode=1):
139
+ super().__init__(message)
140
+ self.exitcode = exitcode
141
+
142
+
143
+ class HelpFlag(flags.BooleanFlag):
144
+ """Special boolean flag that displays usage and raises SystemExit."""
145
+ NAME = 'help'
146
+ SHORT_NAME = '?'
147
+
148
+ def __init__(self):
149
+ super().__init__(
150
+ self.NAME,
151
+ False,
152
+ 'show this help',
153
+ short_name=self.SHORT_NAME,
154
+ allow_hide_cpp=True,
155
+ )
156
+
157
+ def parse(self, arg):
158
+ if self._parse(arg):
159
+ usage(shorthelp=True, writeto_stdout=True)
160
+ # Advertise --helpfull on stdout, since usage() was on stdout.
161
+ print()
162
+ print('Try --helpfull to get a list of all flags.')
163
+ sys.exit(1)
164
+
165
+
166
+ class HelpshortFlag(HelpFlag):
167
+ """--helpshort is an alias for --help."""
168
+ NAME = 'helpshort'
169
+ SHORT_NAME = None
170
+
171
+
172
+ class HelpfullFlag(flags.BooleanFlag):
173
+ """Display help for flags in the main module and all dependent modules."""
174
+
175
+ def __init__(self):
176
+ super().__init__('helpfull', False, 'show full help', allow_hide_cpp=True)
177
+
178
+ def parse(self, arg):
179
+ if self._parse(arg):
180
+ usage(writeto_stdout=True)
181
+ sys.exit(1)
182
+
183
+
184
+ class HelpXMLFlag(flags.BooleanFlag):
185
+ """Similar to HelpfullFlag, but generates output in XML format."""
186
+
187
+ def __init__(self):
188
+ super().__init__(
189
+ 'helpxml',
190
+ False,
191
+ 'like --helpfull, but generates XML output',
192
+ allow_hide_cpp=True,
193
+ )
194
+
195
+ def parse(self, arg):
196
+ if self._parse(arg):
197
+ flags.FLAGS.write_help_in_xml_format(sys.stdout)
198
+ sys.exit(1)
199
+
200
+
201
+ def parse_flags_with_usage(args):
202
+ """Tries to parse the flags, print usage, and exit if unparsable.
203
+
204
+ Args:
205
+ args: [str], a non-empty list of the command line arguments including
206
+ program name.
207
+
208
+ Returns:
209
+ [str], a non-empty list of remaining command line arguments after parsing
210
+ flags, including program name.
211
+ """
212
+ try:
213
+ return FLAGS(args)
214
+ except flags.Error as error:
215
+ message = str(error)
216
+ if '\n' in message:
217
+ final_message = 'FATAL Flags parsing error:\n%s\n' % textwrap.indent(
218
+ message, ' ')
219
+ else:
220
+ final_message = 'FATAL Flags parsing error: %s\n' % message
221
+ sys.stderr.write(final_message)
222
+ sys.stderr.write('Pass --helpshort or --helpfull to see help on flags.\n')
223
+ sys.exit(1)
224
+
225
+
226
+ _define_help_flags_called = False
227
+
228
+
229
+ def define_help_flags():
230
+ """Registers help flags. Idempotent."""
231
+ # Use a global to ensure idempotence.
232
+ global _define_help_flags_called
233
+
234
+ if not _define_help_flags_called:
235
+ flags.DEFINE_flag(HelpFlag())
236
+ flags.DEFINE_flag(HelpshortFlag()) # alias for --help
237
+ flags.DEFINE_flag(HelpfullFlag())
238
+ flags.DEFINE_flag(HelpXMLFlag())
239
+ _define_help_flags_called = True
240
+
241
+
242
+ def _register_and_parse_flags_with_usage(
243
+ argv=None,
244
+ flags_parser=parse_flags_with_usage,
245
+ ):
246
+ """Registers help flags, parses arguments and shows usage if appropriate.
247
+
248
+ This also calls sys.exit(0) if flag --only_check_args is True.
249
+
250
+ Args:
251
+ argv: [str], a non-empty list of the command line arguments including
252
+ program name, sys.argv is used if None.
253
+ flags_parser: Callable[[List[str]], Any], the function used to parse flags.
254
+ The return value of this function is passed to `main` untouched. It must
255
+ guarantee FLAGS is parsed after this function is called.
256
+
257
+ Returns:
258
+ The return value of `flags_parser`. When using the default `flags_parser`,
259
+ it returns the following:
260
+ [str], a non-empty list of remaining command line arguments after parsing
261
+ flags, including program name.
262
+
263
+ Raises:
264
+ Error: Raised when flags_parser is called, but FLAGS is not parsed.
265
+ SystemError: Raised when it's called more than once.
266
+ """
267
+ # fmt: on
268
+ if _register_and_parse_flags_with_usage.done:
269
+ raise SystemError('Flag registration can be done only once.')
270
+
271
+ define_help_flags()
272
+
273
+ original_argv = sys.argv if argv is None else argv
274
+ args_to_main = flags_parser(original_argv)
275
+ if not FLAGS.is_parsed():
276
+ raise Error('FLAGS must be parsed after flags_parser is called.')
277
+
278
+ # Exit when told so.
279
+ if FLAGS.only_check_args:
280
+ sys.exit(0)
281
+ # Immediately after flags are parsed, bump verbosity to INFO if the flag has
282
+ # not been set.
283
+ if FLAGS['verbosity'].using_default_value:
284
+ FLAGS.verbosity = 0
285
+ _register_and_parse_flags_with_usage.done = True
286
+
287
+ return args_to_main
288
+
289
+ _register_and_parse_flags_with_usage.done = False
290
+
291
+
292
+ def _run_main(main, argv):
293
+ """Calls main, optionally with a debugger or profiler."""
294
+ if FLAGS.run_with_pdb:
295
+ sys.exit(_get_debugger_module_with_function('runcall').runcall(main, argv))
296
+ elif FLAGS.run_with_profiling or FLAGS.profile_file:
297
+ # Avoid import overhead since most apps (including performance-sensitive
298
+ # ones) won't be run with profiling.
299
+ # pylint: disable=g-import-not-at-top
300
+ import atexit
301
+ if FLAGS.use_cprofile_for_profiling:
302
+ import cProfile as profile
303
+ else:
304
+ import profile
305
+ profiler = profile.Profile()
306
+ if FLAGS.profile_file:
307
+ atexit.register(profiler.dump_stats, FLAGS.profile_file)
308
+ else:
309
+ atexit.register(profiler.print_stats)
310
+ sys.exit(profiler.runcall(main, argv))
311
+ else:
312
+ sys.exit(main(argv))
313
+
314
+
315
+ def _call_exception_handlers(exception):
316
+ """Calls any installed exception handlers."""
317
+ for handler in EXCEPTION_HANDLERS:
318
+ try:
319
+ if handler.wants(exception):
320
+ handler.handle(exception)
321
+ except: # pylint: disable=bare-except
322
+ try:
323
+ # We don't want to stop for exceptions in the exception handlers but
324
+ # we shouldn't hide them either.
325
+ logging.error(traceback.format_exc())
326
+ except: # pylint: disable=bare-except
327
+ # In case even the logging statement fails, ignore.
328
+ pass
329
+
330
+
331
+ def run(
332
+ main,
333
+ argv=None,
334
+ flags_parser=parse_flags_with_usage,
335
+ ):
336
+ """Begins executing the program.
337
+
338
+ Args:
339
+ main: The main function to execute. It takes an single argument "argv",
340
+ which is a list of command line arguments with parsed flags removed.
341
+ The return value is passed to `sys.exit`, and so for example
342
+ a return value of 0 or None results in a successful termination, whereas
343
+ a return value of 1 results in abnormal termination.
344
+ For more details, see https://docs.python.org/3/library/sys#sys.exit
345
+ argv: A non-empty list of the command line arguments including program name,
346
+ sys.argv is used if None.
347
+ flags_parser: Callable[[List[str]], Any], the function used to parse flags.
348
+ The return value of this function is passed to `main` untouched.
349
+ It must guarantee FLAGS is parsed after this function is called.
350
+ Should be passed as a keyword-only arg which will become mandatory in a
351
+ future release.
352
+ - Parses command line flags with the flag module.
353
+ - If there are any errors, prints usage().
354
+ - Calls main() with the remaining arguments.
355
+ - If main() raises a UsageError, prints usage and the error message.
356
+ """
357
+ # fmt: on
358
+ try:
359
+ args = _run_init(
360
+ sys.argv if argv is None else argv,
361
+ flags_parser,
362
+ )
363
+ while _init_callbacks:
364
+ callback = _init_callbacks.popleft()
365
+ callback()
366
+ try:
367
+ _run_main(main, args)
368
+ except UsageError as error:
369
+ usage(shorthelp=True, detailed_error=error, exitcode=error.exitcode)
370
+ except:
371
+ exc = sys.exc_info()[1]
372
+ # Don't try to post-mortem debug successful SystemExits, since those
373
+ # mean there wasn't actually an error. In particular, the test framework
374
+ # raises SystemExit(False) even if all tests passed.
375
+ if isinstance(exc, SystemExit) and not exc.code:
376
+ raise
377
+
378
+ # Check the tty so that we don't hang waiting for input in an
379
+ # non-interactive scenario.
380
+ if FLAGS.pdb_post_mortem and sys.stdout.isatty():
381
+ traceback.print_exc()
382
+ print()
383
+ print(' *** Entering post-mortem debugging ***')
384
+ print()
385
+ _get_debugger_module_with_function('post_mortem').post_mortem()
386
+ raise
387
+ except Exception as e:
388
+ _call_exception_handlers(e)
389
+ raise
390
+
391
+ # Callbacks which have been deferred until after _run_init has been called.
392
+ _init_callbacks = collections.deque()
393
+
394
+
395
+ def call_after_init(callback):
396
+ """Calls the given callback only once ABSL has finished initialization.
397
+
398
+ If ABSL has already finished initialization when ``call_after_init`` is
399
+ called then the callback is executed immediately, otherwise `callback` is
400
+ stored to be executed after ``app.run`` has finished initializing (aka. just
401
+ before the main function is called).
402
+
403
+ If called after ``app.run``, this is equivalent to calling ``callback()`` in
404
+ the caller thread. If called before ``app.run``, callbacks are run
405
+ sequentially (in an undefined order) in the same thread as ``app.run``.
406
+
407
+ Args:
408
+ callback: a callable to be called once ABSL has finished initialization.
409
+ This may be immediate if initialization has already finished. It
410
+ takes no arguments and returns nothing.
411
+ """
412
+ if _run_init.done:
413
+ callback()
414
+ else:
415
+ _init_callbacks.append(callback)
416
+
417
+
418
+ def _run_init(
419
+ argv,
420
+ flags_parser,
421
+ ):
422
+ """Does one-time initialization and re-parses flags on rerun."""
423
+ if _run_init.done:
424
+ return flags_parser(argv)
425
+ command_name.make_process_name_useful()
426
+ # Set up absl logging handler.
427
+ logging.use_absl_handler()
428
+ args = _register_and_parse_flags_with_usage(
429
+ argv=argv,
430
+ flags_parser=flags_parser,
431
+ )
432
+ if faulthandler:
433
+ try:
434
+ faulthandler.enable()
435
+ except Exception: # pylint: disable=broad-except
436
+ # Some tests verify stderr output very closely, so don't print anything.
437
+ # Disabled faulthandler is a low-impact error.
438
+ pass
439
+ _run_init.done = True
440
+ return args
441
+
442
+
443
+ _run_init.done = False
444
+
445
+
446
+ def usage(shorthelp=False, writeto_stdout=False, detailed_error=None,
447
+ exitcode=None):
448
+ """Writes __main__'s docstring to stderr with some help text.
449
+
450
+ Args:
451
+ shorthelp: bool, if True, prints only flags from the main module,
452
+ rather than all flags.
453
+ writeto_stdout: bool, if True, writes help message to stdout,
454
+ rather than to stderr.
455
+ detailed_error: str, additional detail about why usage info was presented.
456
+ exitcode: optional integer, if set, exits with this status code after
457
+ writing help.
458
+ """
459
+ if writeto_stdout:
460
+ stdfile = sys.stdout
461
+ else:
462
+ stdfile = sys.stderr
463
+
464
+ doc = sys.modules['__main__'].__doc__
465
+ if not doc:
466
+ doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
467
+ doc = flags.text_wrap(doc, indent=' ', firstline_indent='')
468
+ else:
469
+ # Replace all '%s' with sys.argv[0], and all '%%' with '%'.
470
+ num_specifiers = doc.count('%') - 2 * doc.count('%%')
471
+ try:
472
+ doc %= (sys.argv[0],) * num_specifiers
473
+ except (OverflowError, TypeError, ValueError):
474
+ # Just display the docstring as-is.
475
+ pass
476
+ if shorthelp:
477
+ flag_str = FLAGS.main_module_help()
478
+ else:
479
+ flag_str = FLAGS.get_help()
480
+ try:
481
+ stdfile.write(doc)
482
+ if flag_str:
483
+ stdfile.write('\nflags:\n')
484
+ stdfile.write(flag_str)
485
+ stdfile.write('\n')
486
+ if detailed_error is not None:
487
+ stdfile.write('\n%s\n' % detailed_error)
488
+ except OSError as e:
489
+ # We avoid printing a huge backtrace if we get EPIPE, because
490
+ # "foo.par --help | less" is a frequent use case.
491
+ if e.errno != errno.EPIPE:
492
+ raise
493
+ if exitcode is not None:
494
+ sys.exit(exitcode)
495
+
496
+
497
+ class ExceptionHandler:
498
+ """Base exception handler from which other may inherit."""
499
+
500
+ def wants(self, exc):
501
+ """Returns whether this handler wants to handle the exception or not.
502
+
503
+ This base class returns True for all exceptions by default. Override in
504
+ subclass if it wants to be more selective.
505
+
506
+ Args:
507
+ exc: Exception, the current exception.
508
+ """
509
+ del exc # Unused.
510
+ return True
511
+
512
+ def handle(self, exc):
513
+ """Do something with the current exception.
514
+
515
+ Args:
516
+ exc: Exception, the current exception
517
+
518
+ This method must be overridden.
519
+ """
520
+ raise NotImplementedError()
521
+
522
+
523
+ def install_exception_handler(handler):
524
+ """Installs an exception handler.
525
+
526
+ Args:
527
+ handler: ExceptionHandler, the exception handler to install.
528
+
529
+ Raises:
530
+ TypeError: Raised when the handler was not of the correct type.
531
+
532
+ All installed exception handlers will be called if main() exits via
533
+ an abnormal exception, i.e. not one of SystemExit, KeyboardInterrupt,
534
+ FlagsError or UsageError.
535
+ """
536
+ if not isinstance(handler, ExceptionHandler):
537
+ raise TypeError('handler of type %s does not inherit from ExceptionHandler'
538
+ % type(handler))
539
+ EXCEPTION_HANDLERS.append(handler)
lib/python3.12/site-packages/absl/app.pyi ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Callable
2
+ from typing import Any, NoReturn, TypeVar, overload
3
+
4
+ from absl.flags import _flag
5
+
6
+ _MainArgs = TypeVar('_MainArgs')
7
+ _Exc = TypeVar('_Exc', bound=Exception)
8
+
9
+ class ExceptionHandler:
10
+
11
+ def wants(self, exc: _Exc) -> bool:
12
+ ...
13
+
14
+ def handle(self, exc: _Exc):
15
+ ...
16
+
17
+ EXCEPTION_HANDLERS: list[ExceptionHandler] = ...
18
+
19
+ class HelpFlag(_flag.BooleanFlag):
20
+ def __init__(self):
21
+ ...
22
+
23
+ class HelpshortFlag(HelpFlag):
24
+ ...
25
+
26
+ class HelpfullFlag(_flag.BooleanFlag):
27
+ def __init__(self):
28
+ ...
29
+
30
+ class HelpXMLFlag(_flag.BooleanFlag):
31
+ def __init__(self):
32
+ ...
33
+
34
+ def define_help_flags() -> None:
35
+ ...
36
+
37
+ @overload
38
+ def usage(shorthelp: bool | int = ...,
39
+ writeto_stdout: bool | int = ...,
40
+ detailed_error: Any | None = ...,
41
+ exitcode: None = ...) -> None:
42
+ ...
43
+
44
+ @overload
45
+ def usage(shorthelp: bool | int,
46
+ writeto_stdout: bool | int,
47
+ detailed_error: Any | None,
48
+ exitcode: int) -> NoReturn:
49
+ ...
50
+
51
+ @overload
52
+ def usage(shorthelp: bool | int = ...,
53
+ writeto_stdout: bool | int = ...,
54
+ detailed_error: Any | None = ...,
55
+ *,
56
+ exitcode: int) -> NoReturn:
57
+ ...
58
+
59
+ def install_exception_handler(handler: ExceptionHandler) -> None:
60
+ ...
61
+
62
+ class Error(Exception):
63
+ ...
64
+
65
+ class UsageError(Error):
66
+ exitcode: int
67
+
68
+ def parse_flags_with_usage(args: list[str]) -> list[str]:
69
+ ...
70
+
71
+ def call_after_init(callback: Callable[[], Any]) -> None:
72
+ ...
73
+
74
+ # Without the flag_parser argument, `main` should require a List[str].
75
+ @overload
76
+ def run(
77
+ main: Callable[[list[str]], Any],
78
+ argv: list[str] | None = ...,
79
+ ) -> NoReturn:
80
+ ...
81
+
82
+ @overload
83
+ def run(
84
+ main: Callable[[_MainArgs], Any],
85
+ argv: list[str] | None = ...,
86
+ *,
87
+ flags_parser: Callable[[list[str]], _MainArgs],
88
+ ) -> NoReturn:
89
+ ...
lib/python3.12/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.12/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.12/site-packages/absl/flags/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (5.75 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_argument_parser.cpython-312.pyc ADDED
Binary file (30.2 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_defines.cpython-312.pyc ADDED
Binary file (53.5 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_exceptions.cpython-312.pyc ADDED
Binary file (4.53 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_flag.cpython-312.pyc ADDED
Binary file (26 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_flagvalues.cpython-312.pyc ADDED
Binary file (62.9 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_helpers.cpython-312.pyc ADDED
Binary file (13.3 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_validators.cpython-312.pyc ADDED
Binary file (15.4 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/_validators_classes.cpython-312.pyc ADDED
Binary file (7.72 kB). View file
 
lib/python3.12/site-packages/absl/flags/__pycache__/argparse_flags.cpython-312.pyc ADDED
Binary file (14.9 kB). View file
 
lib/python3.12/site-packages/absl/flags/_argument_parser.py ADDED
@@ -0,0 +1,632 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from collections.abc import Iterable, Sequence
23
+ import csv
24
+ import enum
25
+ import io
26
+ import string
27
+ from typing import Any, Generic, TypeVar
28
+ from xml.dom import minidom
29
+
30
+ from absl.flags import _helpers
31
+
32
+ _T = TypeVar('_T')
33
+ _ET = TypeVar('_ET', bound=enum.Enum)
34
+ _N = TypeVar('_N', int, float)
35
+
36
+
37
+ class _ArgumentParserCache(type):
38
+ """Metaclass used to cache and share argument parsers among flags."""
39
+
40
+ _instances: dict[Any, Any] = {}
41
+
42
+ def __call__(cls, *args, **kwargs):
43
+ """Returns an instance of the argument parser cls.
44
+
45
+ This method overrides behavior of the __new__ methods in
46
+ all subclasses of ArgumentParser (inclusive). If an instance
47
+ for cls with the same set of arguments exists, this instance is
48
+ returned, otherwise a new instance is created.
49
+
50
+ If any keyword arguments are defined, or the values in args
51
+ are not hashable, this method always returns a new instance of
52
+ cls.
53
+
54
+ Args:
55
+ *args: Positional initializer arguments.
56
+ **kwargs: Initializer keyword arguments.
57
+
58
+ Returns:
59
+ An instance of cls, shared or new.
60
+ """
61
+ if kwargs:
62
+ return type.__call__(cls, *args, **kwargs)
63
+ else:
64
+ instances = cls._instances
65
+ key = (cls,) + tuple(args)
66
+ try:
67
+ return instances[key]
68
+ except KeyError:
69
+ # No cache entry for key exists, create a new one.
70
+ return instances.setdefault(key, type.__call__(cls, *args))
71
+ except TypeError:
72
+ # An object in args cannot be hashed, always return
73
+ # a new instance.
74
+ return type.__call__(cls, *args)
75
+
76
+
77
+ class ArgumentParser(Generic[_T], metaclass=_ArgumentParserCache):
78
+ """Base class used to parse and convert arguments.
79
+
80
+ The :meth:`parse` method checks to make sure that the string argument is a
81
+ legal value and convert it to a native type. If the value cannot be
82
+ converted, it should throw a ``ValueError`` exception with a human
83
+ readable explanation of why the value is illegal.
84
+
85
+ Subclasses should also define a syntactic_help string which may be
86
+ presented to the user to describe the form of the legal values.
87
+
88
+ Argument parser classes must be stateless, since instances are cached
89
+ and shared between flags. Initializer arguments are allowed, but all
90
+ member variables must be derived from initializer arguments only.
91
+ """
92
+
93
+ syntactic_help: str = ''
94
+
95
+ def parse(self, argument: str) -> _T | None:
96
+ """Parses the string argument and returns the native value.
97
+
98
+ By default it returns its argument unmodified.
99
+
100
+ Args:
101
+ argument: string argument passed in the commandline.
102
+
103
+ Raises:
104
+ ValueError: Raised when it fails to parse the argument.
105
+ TypeError: Raised when the argument has the wrong type.
106
+
107
+ Returns:
108
+ The parsed value in native type.
109
+ """
110
+ if not isinstance(argument, str):
111
+ raise TypeError('flag value must be a string, found "{}"'.format(
112
+ type(argument)))
113
+ return argument # type: ignore[return-value]
114
+
115
+ def flag_type(self) -> str:
116
+ """Returns a string representing the type of the flag."""
117
+ return 'string'
118
+
119
+ def _custom_xml_dom_elements(
120
+ self, doc: minidom.Document
121
+ ) -> list[minidom.Element]:
122
+ """Returns a list of minidom.Element to add additional flag information.
123
+
124
+ Args:
125
+ doc: minidom.Document, the DOM document it should create nodes from.
126
+ """
127
+ del doc # Unused.
128
+ return []
129
+
130
+
131
+ class ArgumentSerializer(Generic[_T]):
132
+ """Base class for generating string representations of a flag value."""
133
+
134
+ def serialize(self, value: _T) -> str:
135
+ """Returns a serialized string of the value."""
136
+ return str(value)
137
+
138
+
139
+ class NumericParser(ArgumentParser[_N]):
140
+ """Parser of numeric values.
141
+
142
+ Parsed value may be bounded to a given upper and lower bound.
143
+ """
144
+
145
+ lower_bound: _N | None
146
+ upper_bound: _N | None
147
+
148
+ def is_outside_bounds(self, val: _N) -> bool:
149
+ """Returns whether the value is outside the bounds or not."""
150
+ return ((self.lower_bound is not None and val < self.lower_bound) or
151
+ (self.upper_bound is not None and val > self.upper_bound))
152
+
153
+ def parse(self, argument: str | _N) -> _N:
154
+ """See base class."""
155
+ val = self.convert(argument)
156
+ if self.is_outside_bounds(val):
157
+ raise ValueError('%s is not %s' % (val, self.syntactic_help))
158
+ return val
159
+
160
+ def _custom_xml_dom_elements(
161
+ self, doc: minidom.Document
162
+ ) -> list[minidom.Element]:
163
+ elements = []
164
+ if self.lower_bound is not None:
165
+ elements.append(_helpers.create_xml_dom_element(
166
+ doc, 'lower_bound', self.lower_bound))
167
+ if self.upper_bound is not None:
168
+ elements.append(_helpers.create_xml_dom_element(
169
+ doc, 'upper_bound', self.upper_bound))
170
+ return elements
171
+
172
+ def convert(self, argument: str | _N) -> _N:
173
+ """Returns the correct numeric value of argument.
174
+
175
+ Subclass must implement this method, and raise TypeError if argument is not
176
+ string or has the right numeric type.
177
+
178
+ Args:
179
+ argument: string argument passed in the commandline, or the numeric type.
180
+
181
+ Raises:
182
+ TypeError: Raised when argument is not a string or the right numeric type.
183
+ ValueError: Raised when failed to convert argument to the numeric value.
184
+ """
185
+ raise NotImplementedError
186
+
187
+
188
+ class FloatParser(NumericParser[float]):
189
+ """Parser of floating point values.
190
+
191
+ Parsed value may be bounded to a given upper and lower bound.
192
+ """
193
+ number_article = 'a'
194
+ number_name = 'number'
195
+ syntactic_help = ' '.join((number_article, number_name))
196
+
197
+ def __init__(
198
+ self,
199
+ lower_bound: float | None = None,
200
+ upper_bound: float | None = None,
201
+ ) -> None:
202
+ super().__init__()
203
+ self.lower_bound = lower_bound
204
+ self.upper_bound = upper_bound
205
+ sh = self.syntactic_help
206
+ if lower_bound is not None and upper_bound is not None:
207
+ sh = ('%s in the range [%s, %s]' % (sh, lower_bound, upper_bound))
208
+ elif lower_bound == 0:
209
+ sh = 'a non-negative %s' % self.number_name
210
+ elif upper_bound == 0:
211
+ sh = 'a non-positive %s' % self.number_name
212
+ elif upper_bound is not None:
213
+ sh = '%s <= %s' % (self.number_name, upper_bound)
214
+ elif lower_bound is not None:
215
+ sh = '%s >= %s' % (self.number_name, lower_bound)
216
+ self.syntactic_help = sh
217
+
218
+ def convert(self, argument: int | float | str) -> float:
219
+ """Returns the float value of argument."""
220
+ if (
221
+ (isinstance(argument, int) and not isinstance(argument, bool))
222
+ or isinstance(argument, float)
223
+ or isinstance(argument, str)
224
+ ):
225
+ return float(argument)
226
+ else:
227
+ raise TypeError(
228
+ 'Expect argument to be a string, int, or float, found {}'.format(
229
+ type(argument)))
230
+
231
+ def flag_type(self) -> str:
232
+ """See base class."""
233
+ return 'float'
234
+
235
+
236
+ class IntegerParser(NumericParser[int]):
237
+ """Parser of an integer value.
238
+
239
+ Parsed value may be bounded to a given upper and lower bound.
240
+ """
241
+ number_article = 'an'
242
+ number_name = 'integer'
243
+ syntactic_help = ' '.join((number_article, number_name))
244
+
245
+ def __init__(
246
+ self, lower_bound: int | None = None, upper_bound: int | None = None
247
+ ) -> None:
248
+ super().__init__()
249
+ self.lower_bound = lower_bound
250
+ self.upper_bound = upper_bound
251
+ sh = self.syntactic_help
252
+ if lower_bound is not None and upper_bound is not None:
253
+ sh = ('%s in the range [%s, %s]' % (sh, lower_bound, upper_bound))
254
+ elif lower_bound == 1:
255
+ sh = 'a positive %s' % self.number_name
256
+ elif upper_bound == -1:
257
+ sh = 'a negative %s' % self.number_name
258
+ elif lower_bound == 0:
259
+ sh = 'a non-negative %s' % self.number_name
260
+ elif upper_bound == 0:
261
+ sh = 'a non-positive %s' % self.number_name
262
+ elif upper_bound is not None:
263
+ sh = '%s <= %s' % (self.number_name, upper_bound)
264
+ elif lower_bound is not None:
265
+ sh = '%s >= %s' % (self.number_name, lower_bound)
266
+ self.syntactic_help = sh
267
+
268
+ def convert(self, argument: int | str) -> int:
269
+ """Returns the int value of argument."""
270
+ if isinstance(argument, int) and not isinstance(argument, bool):
271
+ return argument
272
+ elif isinstance(argument, str):
273
+ base = 10
274
+ if len(argument) > 2 and argument[0] == '0':
275
+ if argument[1] == 'o':
276
+ base = 8
277
+ elif argument[1] == 'x':
278
+ base = 16
279
+ return int(argument, base)
280
+ else:
281
+ raise TypeError('Expect argument to be a string or int, found {}'.format(
282
+ type(argument)))
283
+
284
+ def flag_type(self) -> str:
285
+ """See base class."""
286
+ return 'int'
287
+
288
+
289
+ class BooleanParser(ArgumentParser[bool]):
290
+ """Parser of boolean values."""
291
+
292
+ def parse(self, argument: str | int) -> bool:
293
+ """See base class."""
294
+ if isinstance(argument, str):
295
+ if argument.lower() in ('true', 't', '1'):
296
+ return True
297
+ elif argument.lower() in ('false', 'f', '0'):
298
+ return False
299
+ else:
300
+ raise ValueError('Non-boolean argument to boolean flag', argument)
301
+ elif isinstance(argument, int):
302
+ # Only allow bool or integer 0, 1.
303
+ # Note that float 1.0 == True, 0.0 == False.
304
+ bool_value = bool(argument)
305
+ if argument == bool_value:
306
+ return bool_value
307
+ else:
308
+ raise ValueError('Non-boolean argument to boolean flag', argument)
309
+
310
+ raise TypeError('Non-boolean argument to boolean flag', argument)
311
+
312
+ def flag_type(self) -> str:
313
+ """See base class."""
314
+ return 'bool'
315
+
316
+
317
+ class EnumParser(ArgumentParser[str]):
318
+ """Parser of a string enum value (a string value from a given set)."""
319
+
320
+ def __init__(
321
+ self, enum_values: Iterable[str], case_sensitive: bool = True
322
+ ) -> None:
323
+ """Initializes EnumParser.
324
+
325
+ Args:
326
+ enum_values: [str], a non-empty list of string values in the enum.
327
+ case_sensitive: bool, whether or not the enum is to be case-sensitive.
328
+
329
+ Raises:
330
+ ValueError: When enum_values is empty.
331
+ """
332
+ if not enum_values:
333
+ raise ValueError(f'enum_values cannot be empty, found "{enum_values}"')
334
+ if isinstance(enum_values, str):
335
+ raise ValueError(f'enum_values cannot be a str, found "{enum_values}"')
336
+ super().__init__()
337
+ self.enum_values = list(enum_values)
338
+ self.case_sensitive = case_sensitive
339
+
340
+ def parse(self, argument: str) -> str:
341
+ """Determines validity of argument and returns the correct element of enum.
342
+
343
+ Args:
344
+ argument: str, the supplied flag value.
345
+
346
+ Returns:
347
+ The first matching element from enum_values.
348
+
349
+ Raises:
350
+ ValueError: Raised when argument didn't match anything in enum.
351
+ """
352
+ if self.case_sensitive:
353
+ if argument not in self.enum_values:
354
+ raise ValueError('value should be one of <%s>' %
355
+ '|'.join(self.enum_values))
356
+ else:
357
+ return argument
358
+ else:
359
+ if argument.upper() not in [value.upper() for value in self.enum_values]:
360
+ raise ValueError('value should be one of <%s>' %
361
+ '|'.join(self.enum_values))
362
+ else:
363
+ return [value for value in self.enum_values
364
+ if value.upper() == argument.upper()][0]
365
+
366
+ def flag_type(self) -> str:
367
+ """See base class."""
368
+ return 'string enum'
369
+
370
+
371
+ class EnumClassParser(ArgumentParser[_ET]):
372
+ """Parser of an Enum class member."""
373
+
374
+ def __init__(
375
+ self, enum_class: type[_ET], case_sensitive: bool = True
376
+ ) -> None:
377
+ """Initializes EnumParser.
378
+
379
+ Args:
380
+ enum_class: class, the Enum class with all possible flag values.
381
+ case_sensitive: bool, whether or not the enum is to be case-sensitive. If
382
+ False, all member names must be unique when case is ignored.
383
+
384
+ Raises:
385
+ TypeError: When enum_class is not a subclass of Enum.
386
+ ValueError: When enum_class is empty.
387
+ """
388
+ if not issubclass(enum_class, enum.Enum):
389
+ raise TypeError(f'{enum_class} is not a subclass of Enum.')
390
+ if not enum_class.__members__:
391
+ raise ValueError('enum_class cannot be empty, but "{}" is empty.'
392
+ .format(enum_class))
393
+ if not case_sensitive:
394
+ members = collections.Counter(
395
+ name.lower() for name in enum_class.__members__)
396
+ duplicate_keys = {
397
+ member for member, count in members.items() if count > 1
398
+ }
399
+ if duplicate_keys:
400
+ raise ValueError(
401
+ 'Duplicate enum values for {} using case_sensitive=False'.format(
402
+ duplicate_keys))
403
+
404
+ super().__init__()
405
+ self.enum_class = enum_class
406
+ self._case_sensitive = case_sensitive
407
+ if case_sensitive:
408
+ self._member_names = tuple(enum_class.__members__)
409
+ else:
410
+ self._member_names = tuple(
411
+ name.lower() for name in enum_class.__members__)
412
+
413
+ @property
414
+ def member_names(self) -> Sequence[str]:
415
+ """The accepted enum names, in lowercase if not case sensitive."""
416
+ return self._member_names
417
+
418
+ def parse(self, argument: _ET | str) -> _ET:
419
+ """Determines validity of argument and returns the correct element of enum.
420
+
421
+ Args:
422
+ argument: str or Enum class member, the supplied flag value.
423
+
424
+ Returns:
425
+ The first matching Enum class member in Enum class.
426
+
427
+ Raises:
428
+ ValueError: Raised when argument didn't match anything in enum.
429
+ """
430
+ if isinstance(argument, self.enum_class):
431
+ return argument # pytype: disable=bad-return-type
432
+ elif not isinstance(argument, str):
433
+ raise ValueError(
434
+ '{} is not an enum member or a name of a member in {}'.format(
435
+ argument, self.enum_class))
436
+ key = EnumParser(
437
+ self._member_names, case_sensitive=self._case_sensitive).parse(argument)
438
+ if self._case_sensitive:
439
+ return self.enum_class[key]
440
+ else:
441
+ # If EnumParser.parse() return a value, we're guaranteed to find it
442
+ # as a member of the class
443
+ return next(value for name, value in self.enum_class.__members__.items()
444
+ if name.lower() == key.lower())
445
+
446
+ def flag_type(self) -> str:
447
+ """See base class."""
448
+ return 'enum class'
449
+
450
+
451
+ class ListSerializer(Generic[_T], ArgumentSerializer[list[_T]]):
452
+
453
+ def __init__(self, list_sep: str) -> None:
454
+ self.list_sep = list_sep
455
+
456
+ def serialize(self, value: list[_T]) -> str:
457
+ """See base class."""
458
+ return self.list_sep.join([str(x) for x in value])
459
+
460
+
461
+ class EnumClassListSerializer(ListSerializer[_ET]):
462
+ """A serializer for :class:`MultiEnumClass` flags.
463
+
464
+ This serializer simply joins the output of `EnumClassSerializer` using a
465
+ provided separator.
466
+ """
467
+
468
+ _element_serializer: 'EnumClassSerializer'
469
+
470
+ def __init__(self, list_sep: str, **kwargs) -> None:
471
+ """Initializes EnumClassListSerializer.
472
+
473
+ Args:
474
+ list_sep: String to be used as a separator when serializing
475
+ **kwargs: Keyword arguments to the `EnumClassSerializer` used to serialize
476
+ individual values.
477
+ """
478
+ super().__init__(list_sep)
479
+ self._element_serializer = EnumClassSerializer(**kwargs)
480
+
481
+ def serialize(self, value: _ET | list[_ET]) -> str:
482
+ """See base class."""
483
+ if isinstance(value, list):
484
+ return self.list_sep.join(
485
+ self._element_serializer.serialize(x) for x in value)
486
+ else:
487
+ return self._element_serializer.serialize(value)
488
+
489
+
490
+ class CsvListSerializer(ListSerializer[str]):
491
+
492
+ def serialize(self, value: list[str]) -> str:
493
+ """Serializes a list as a CSV string or unicode."""
494
+ output = io.StringIO()
495
+ writer = csv.writer(output, delimiter=self.list_sep)
496
+ writer.writerow([str(x) for x in value])
497
+ serialized_value = output.getvalue().strip()
498
+
499
+ # We need the returned value to be pure ascii or Unicodes so that
500
+ # when the xml help is generated they are usefully encodable.
501
+ return str(serialized_value)
502
+
503
+
504
+ class EnumClassSerializer(ArgumentSerializer[_ET]):
505
+ """Class for generating string representations of an enum class flag value."""
506
+
507
+ def __init__(self, lowercase: bool) -> None:
508
+ """Initializes EnumClassSerializer.
509
+
510
+ Args:
511
+ lowercase: If True, enum member names are lowercased during serialization.
512
+ """
513
+ self._lowercase = lowercase
514
+
515
+ def serialize(self, value: _ET) -> str:
516
+ """Returns a serialized string of the Enum class value."""
517
+ as_string = str(value.name)
518
+ return as_string.lower() if self._lowercase else as_string
519
+
520
+
521
+ class BaseListParser(ArgumentParser):
522
+ """Base class for a parser of lists of strings.
523
+
524
+ To extend, inherit from this class; from the subclass ``__init__``, call::
525
+
526
+ super().__init__(token, name)
527
+
528
+ where token is a character used to tokenize, and name is a description
529
+ of the separator.
530
+ """
531
+
532
+ def __init__(self, token: str | None = None, name: str | None = None) -> None:
533
+ assert name
534
+ super().__init__()
535
+ self._token = token
536
+ self._name = name
537
+ self.syntactic_help = 'a %s separated list' % self._name
538
+
539
+ def parse(self, argument: str) -> list[str]:
540
+ """See base class."""
541
+ if isinstance(argument, list):
542
+ return argument
543
+ elif not argument:
544
+ return []
545
+ else:
546
+ return [s.strip() for s in argument.split(self._token)]
547
+
548
+ def flag_type(self) -> str:
549
+ """See base class."""
550
+ return '%s separated list of strings' % self._name
551
+
552
+
553
+ class ListParser(BaseListParser):
554
+ """Parser for a comma-separated list of strings."""
555
+
556
+ def __init__(self) -> None:
557
+ super().__init__(',', 'comma')
558
+
559
+ def parse(self, argument: str | list[str]) -> list[str]:
560
+ """Parses argument as comma-separated list of strings."""
561
+ if isinstance(argument, list):
562
+ return argument
563
+ elif not argument:
564
+ return []
565
+ else:
566
+ try:
567
+ return [s.strip() for s in list(csv.reader([argument], strict=True))[0]]
568
+ except csv.Error as e:
569
+ # Provide a helpful report for case like
570
+ # --listflag="$(printf 'hello,\nworld')"
571
+ # IOW, list flag values containing naked newlines. This error
572
+ # was previously "reported" by allowing csv.Error to
573
+ # propagate.
574
+ raise ValueError('Unable to parse the value %r as a %s: %s'
575
+ % (argument, self.flag_type(), e))
576
+
577
+ def _custom_xml_dom_elements(
578
+ self, doc: minidom.Document
579
+ ) -> list[minidom.Element]:
580
+ elements = super()._custom_xml_dom_elements(doc)
581
+ elements.append(_helpers.create_xml_dom_element(
582
+ doc, 'list_separator', repr(',')))
583
+ return elements
584
+
585
+
586
+ class WhitespaceSeparatedListParser(BaseListParser):
587
+ """Parser for a whitespace-separated list of strings."""
588
+
589
+ def __init__(self, comma_compat: bool = False) -> None:
590
+ """Initializer.
591
+
592
+ Args:
593
+ comma_compat: bool, whether to support comma as an additional separator.
594
+ If False then only whitespace is supported. This is intended only for
595
+ backwards compatibility with flags that used to be comma-separated.
596
+ """
597
+ self._comma_compat = comma_compat
598
+ name = 'whitespace or comma' if self._comma_compat else 'whitespace'
599
+ super().__init__(None, name)
600
+
601
+ def parse(self, argument: str | list[str]) -> list[str]:
602
+ """Parses argument as whitespace-separated list of strings.
603
+
604
+ It also parses argument as comma-separated list of strings if requested.
605
+
606
+ Args:
607
+ argument: string argument passed in the commandline.
608
+
609
+ Returns:
610
+ [str], the parsed flag value.
611
+ """
612
+ if isinstance(argument, list):
613
+ return argument
614
+ elif not argument:
615
+ return []
616
+ else:
617
+ if self._comma_compat:
618
+ argument = argument.replace(',', ' ')
619
+ return argument.split()
620
+
621
+ def _custom_xml_dom_elements(
622
+ self, doc: minidom.Document
623
+ ) -> list[minidom.Element]:
624
+ elements = super()._custom_xml_dom_elements(doc)
625
+ separators = list(string.whitespace)
626
+ if self._comma_compat:
627
+ separators.append(',')
628
+ separators.sort()
629
+ for sep_char in separators:
630
+ elements.append(_helpers.create_xml_dom_element(
631
+ doc, 'list_separator', repr(sep_char)))
632
+ return elements
lib/python3.12/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
+ from collections.abc import Iterable
21
+ import enum
22
+ import sys
23
+ import types
24
+ from typing import Any, Literal, TypeVar, overload
25
+
26
+ from absl.flags import _argument_parser
27
+ from absl.flags import _exceptions
28
+ from absl.flags import _flag
29
+ from absl.flags import _flagvalues
30
+ from absl.flags import _helpers
31
+ from absl.flags import _validators
32
+
33
+ _helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
34
+
35
+ _T = TypeVar('_T')
36
+ _ET = TypeVar('_ET', bound=enum.Enum)
37
+
38
+
39
+ def _register_bounds_validator_if_needed(parser, name, flag_values):
40
+ """Enforces lower and upper bounds for numeric flags.
41
+
42
+ Args:
43
+ parser: NumericParser (either FloatParser or IntegerParser), provides lower
44
+ and upper bounds, and help text to display.
45
+ name: str, name of the flag
46
+ flag_values: FlagValues.
47
+ """
48
+ if parser.lower_bound is not None or parser.upper_bound is not None:
49
+
50
+ def checker(value):
51
+ if value is not None and parser.is_outside_bounds(value):
52
+ message = '%s is not %s' % (value, parser.syntactic_help)
53
+ raise _exceptions.ValidationError(message)
54
+ return True
55
+
56
+ _validators.register_validator(name, checker, flag_values=flag_values)
57
+
58
+
59
+ @overload
60
+ def DEFINE( # pylint: disable=invalid-name
61
+ parser: _argument_parser.ArgumentParser[_T],
62
+ name: str,
63
+ default: Any,
64
+ help: str | None, # pylint: disable=redefined-builtin
65
+ flag_values: _flagvalues.FlagValues = ...,
66
+ serializer: _argument_parser.ArgumentSerializer[_T] | None = ...,
67
+ module_name: str | None = ...,
68
+ required: Literal[True] = ...,
69
+ **args: Any
70
+ ) -> _flagvalues.FlagHolder[_T]:
71
+ ...
72
+
73
+
74
+ @overload
75
+ def DEFINE( # pylint: disable=invalid-name
76
+ parser: _argument_parser.ArgumentParser[_T],
77
+ name: str,
78
+ default: Any | None,
79
+ help: str | None, # pylint: disable=redefined-builtin
80
+ flag_values: _flagvalues.FlagValues = ...,
81
+ serializer: _argument_parser.ArgumentSerializer[_T] | None = ...,
82
+ module_name: str | None = ...,
83
+ required: bool = ...,
84
+ **args: Any
85
+ ) -> _flagvalues.FlagHolder[_T | None]:
86
+ ...
87
+
88
+
89
+ def DEFINE( # pylint: disable=invalid-name
90
+ parser,
91
+ name,
92
+ default,
93
+ help, # pylint: disable=redefined-builtin
94
+ flag_values=_flagvalues.FLAGS,
95
+ serializer=None,
96
+ module_name=None,
97
+ required=False,
98
+ **args):
99
+ """Registers a generic Flag object.
100
+
101
+ NOTE: in the docstrings of all DEFINE* functions, "registers" is short
102
+ for "creates a new flag and registers it".
103
+
104
+ Auxiliary function: clients should use the specialized ``DEFINE_<type>``
105
+ function instead.
106
+
107
+ Args:
108
+ parser: :class:`ArgumentParser`, used to parse the flag arguments.
109
+ name: str, the flag name.
110
+ default: The default value of the flag.
111
+ help: str, the help message.
112
+ flag_values: :class:`FlagValues`, the FlagValues instance with which the
113
+ flag will be registered. This should almost never need to be overridden.
114
+ serializer: :class:`ArgumentSerializer`, the flag serializer instance.
115
+ module_name: str, the name of the Python module declaring this flag. If not
116
+ provided, it will be computed using the stack trace of this call.
117
+ required: bool, is this a required flag. This must be used as a keyword
118
+ argument.
119
+ **args: dict, the extra keyword args that are passed to ``Flag.__init__``.
120
+
121
+ Returns:
122
+ a handle to defined flag.
123
+ """
124
+ return DEFINE_flag(
125
+ _flag.Flag(parser, serializer, name, default, help, **args),
126
+ flag_values,
127
+ module_name,
128
+ required=True if required else False,
129
+ )
130
+
131
+
132
+ @overload
133
+ def DEFINE_flag( # pylint: disable=invalid-name
134
+ flag: _flag.Flag[_T],
135
+ flag_values: _flagvalues.FlagValues = ...,
136
+ module_name: str | None = ...,
137
+ required: Literal[True] = ...,
138
+ ) -> _flagvalues.FlagHolder[_T]:
139
+ ...
140
+
141
+
142
+ @overload
143
+ def DEFINE_flag( # pylint: disable=invalid-name
144
+ flag: _flag.Flag[_T],
145
+ flag_values: _flagvalues.FlagValues = ...,
146
+ module_name: str | None = ...,
147
+ required: bool = ...,
148
+ ) -> _flagvalues.FlagHolder[_T | None]:
149
+ ...
150
+
151
+
152
+ def DEFINE_flag( # pylint: disable=invalid-name
153
+ flag,
154
+ flag_values=_flagvalues.FLAGS,
155
+ module_name=None,
156
+ required=False):
157
+ """Registers a :class:`Flag` object with a :class:`FlagValues` object.
158
+
159
+ By default, the global :const:`FLAGS` ``FlagValue`` object is used.
160
+
161
+ Typical users will use one of the more specialized DEFINE_xxx
162
+ functions, such as :func:`DEFINE_string` or :func:`DEFINE_integer`. But
163
+ developers who need to create :class:`Flag` objects themselves should use
164
+ this function to register their flags.
165
+
166
+ Args:
167
+ flag: :class:`Flag`, a flag that is key to the module.
168
+ flag_values: :class:`FlagValues`, the ``FlagValues`` instance with which the
169
+ flag will be registered. This should almost never need to be overridden.
170
+ module_name: str, the name of the Python module declaring this flag. If not
171
+ provided, it will be computed using the stack trace of this call.
172
+ required: bool, is this a required flag. This must be used as a keyword
173
+ argument.
174
+
175
+ Returns:
176
+ a handle to defined flag.
177
+ """
178
+ if required and flag.default is not None:
179
+ raise ValueError(
180
+ 'Required flag --%s needs to have None as default' % flag.name
181
+ )
182
+ # Copying the reference to flag_values prevents pychecker warnings.
183
+ fv = flag_values
184
+ fv[flag.name] = flag
185
+ # Tell flag_values who's defining the flag.
186
+ if module_name:
187
+ module = sys.modules.get(module_name)
188
+ else:
189
+ module, module_name = _helpers.get_calling_module_object_and_name()
190
+ flag_values.register_flag_by_module(module_name, flag)
191
+ flag_values.register_flag_by_module_id(id(module), flag)
192
+ if required:
193
+ _validators.mark_flag_as_required(flag.name, fv)
194
+ ensure_non_none_value = (flag.default is not None) or required
195
+ return _flagvalues.FlagHolder(
196
+ fv, flag, ensure_non_none_value=ensure_non_none_value)
197
+
198
+
199
+ def set_default(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
200
+ """Changes the default value of the provided flag object.
201
+
202
+ The flag's current value is also updated if the flag is currently using
203
+ the default value, i.e. not specified in the command line, and not set
204
+ by FLAGS.name = value.
205
+
206
+ Args:
207
+ flag_holder: FlagHolder, the flag to modify.
208
+ value: The new default value.
209
+
210
+ Raises:
211
+ IllegalFlagValueError: Raised when value is not valid.
212
+ """
213
+ flag_holder._flagvalues.set_default(flag_holder.name, value) # pylint: disable=protected-access
214
+
215
+
216
+ def override_value(flag_holder: _flagvalues.FlagHolder[_T], value: _T) -> None:
217
+ """Overrides the value of the provided flag.
218
+
219
+ This value takes precedent over the default value and, when called after flag
220
+ parsing, any value provided at the command line.
221
+
222
+ Args:
223
+ flag_holder: FlagHolder, the flag to modify.
224
+ value: The new value.
225
+
226
+ Raises:
227
+ IllegalFlagValueError: The value did not pass the flag parser or validators.
228
+ """
229
+ fv = flag_holder._flagvalues # pylint: disable=protected-access
230
+ # Ensure the new value satisfies the flag's parser while avoiding side
231
+ # effects of calling parse().
232
+ parsed = fv[flag_holder.name]._parse(value) # pylint: disable=protected-access
233
+ if parsed != value:
234
+ raise _exceptions.IllegalFlagValueError(
235
+ 'flag %s: parsed value %r not equal to original %r'
236
+ % (flag_holder.name, parsed, value)
237
+ )
238
+ setattr(fv, flag_holder.name, value)
239
+
240
+
241
+ def _internal_declare_key_flags(
242
+ flag_names: list[str],
243
+ flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,
244
+ key_flag_values: _flagvalues.FlagValues | None = None,
245
+ ) -> None:
246
+ """Declares a flag as key for the calling module.
247
+
248
+ Internal function. User code should call declare_key_flag or
249
+ adopt_module_key_flags instead.
250
+
251
+ Args:
252
+ flag_names: [str], a list of names of already-registered Flag objects.
253
+ flag_values: :class:`FlagValues`, the FlagValues instance with which the
254
+ flags listed in flag_names have registered (the value of the flag_values
255
+ argument from the ``DEFINE_*`` calls that defined those flags). This
256
+ should almost never need to be overridden.
257
+ key_flag_values: :class:`FlagValues`, the FlagValues instance that (among
258
+ possibly many other things) keeps track of the key flags for each module.
259
+ Default ``None`` means "same as flag_values". This should almost never
260
+ need to be overridden.
261
+
262
+ Raises:
263
+ UnrecognizedFlagError: Raised when the flag is not defined.
264
+ """
265
+ key_flag_values = key_flag_values or flag_values
266
+
267
+ module = _helpers.get_calling_module()
268
+
269
+ for flag_name in flag_names:
270
+ key_flag_values.register_key_flag_for_module(module, flag_values[flag_name])
271
+
272
+
273
+ def declare_key_flag(
274
+ flag_name: str | _flagvalues.FlagHolder,
275
+ flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS,
276
+ ) -> None:
277
+ """Declares one flag as key to the current module.
278
+
279
+ Key flags are flags that are deemed really important for a module.
280
+ They are important when listing help messages; e.g., if the
281
+ --helpshort command-line flag is used, then only the key flags of the
282
+ main module are listed (instead of all flags, as in the case of
283
+ --helpfull).
284
+
285
+ Sample usage::
286
+
287
+ flags.declare_key_flag('flag_1')
288
+
289
+ Args:
290
+ flag_name: str | :class:`FlagHolder`, the name or holder of an already
291
+ declared flag. (Redeclaring flags as key, including flags implicitly key
292
+ because they were declared in this module, is a no-op.)
293
+ Positional-only parameter.
294
+ flag_values: :class:`FlagValues`, the FlagValues instance in which the
295
+ flag will be declared as a key flag. This should almost never need to be
296
+ overridden.
297
+
298
+ Raises:
299
+ ValueError: Raised if flag_name not defined as a Python flag.
300
+ """
301
+ flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
302
+ if flag_name in _helpers.SPECIAL_FLAGS:
303
+ # Take care of the special flags, e.g., --flagfile, --undefok.
304
+ # These flags are defined in SPECIAL_FLAGS, and are treated
305
+ # specially during flag parsing, taking precedence over the
306
+ # user-defined flags.
307
+ _internal_declare_key_flags([flag_name],
308
+ flag_values=_helpers.SPECIAL_FLAGS,
309
+ key_flag_values=flag_values)
310
+ return
311
+ try:
312
+ _internal_declare_key_flags([flag_name], flag_values=flag_values)
313
+ except KeyError:
314
+ raise ValueError('Flag --%s is undefined. To set a flag as a key flag '
315
+ 'first define it in Python.' % flag_name)
316
+
317
+
318
+ def adopt_module_key_flags(
319
+ module: Any, flag_values: _flagvalues.FlagValues = _flagvalues.FLAGS
320
+ ) -> None:
321
+ """Declares that all flags key to a module are key to the current module.
322
+
323
+ Args:
324
+ module: module, the module object from which all key flags will be declared
325
+ as key flags to the current module.
326
+ flag_values: :class:`FlagValues`, the FlagValues instance in which the
327
+ flags will be declared as key flags. This should almost never need to be
328
+ overridden.
329
+
330
+ Raises:
331
+ Error: Raised when given an argument that is a module name (a string),
332
+ instead of a module object.
333
+ """
334
+ if not isinstance(module, types.ModuleType):
335
+ raise _exceptions.Error('Expected a module object, not %r.' % (module,))
336
+ _internal_declare_key_flags(
337
+ [f.name for f in flag_values.get_key_flags_for_module(module.__name__)],
338
+ flag_values=flag_values)
339
+ # If module is this flag module, take _helpers.SPECIAL_FLAGS into account.
340
+ if module == _helpers.FLAGS_MODULE:
341
+ _internal_declare_key_flags(
342
+ # As we associate flags with get_calling_module_object_and_name(), the
343
+ # special flags defined in this module are incorrectly registered with
344
+ # a different module. So, we can't use get_key_flags_for_module.
345
+ # Instead, we take all flags from _helpers.SPECIAL_FLAGS (a private
346
+ # FlagValues, where no other module should register flags).
347
+ [_helpers.SPECIAL_FLAGS[name].name for name in _helpers.SPECIAL_FLAGS],
348
+ flag_values=_helpers.SPECIAL_FLAGS,
349
+ key_flag_values=flag_values)
350
+
351
+
352
+ def disclaim_key_flags() -> None:
353
+ """Declares that the current module will not define any more key flags.
354
+
355
+ Normally, the module that calls the DEFINE_xxx functions claims the
356
+ flag to be its key flag. This is undesirable for modules that
357
+ define additional DEFINE_yyy functions with its own flag parsers and
358
+ serializers, since that module will accidentally claim flags defined
359
+ by DEFINE_yyy as its key flags. After calling this function, the
360
+ module disclaims flag definitions thereafter, so the key flags will
361
+ be correctly attributed to the caller of DEFINE_yyy.
362
+
363
+ After calling this function, the module will not be able to define
364
+ any more flags. This function will affect all FlagValues objects.
365
+ """
366
+ globals_for_caller = sys._getframe(1).f_globals # pylint: disable=protected-access
367
+ module = _helpers.get_module_object_and_name(globals_for_caller)
368
+ if module is not None:
369
+ _helpers.disclaim_module_ids.add(id(module.module))
370
+
371
+
372
+ @overload
373
+ def DEFINE_string( # pylint: disable=invalid-name
374
+ name: str,
375
+ default: str | None,
376
+ help: str | None, # pylint: disable=redefined-builtin
377
+ flag_values: _flagvalues.FlagValues = ...,
378
+ *,
379
+ required: Literal[True],
380
+ **args: Any
381
+ ) -> _flagvalues.FlagHolder[str]:
382
+ ...
383
+
384
+
385
+ @overload
386
+ def DEFINE_string( # pylint: disable=invalid-name
387
+ name: str,
388
+ default: None,
389
+ help: str | None, # pylint: disable=redefined-builtin
390
+ flag_values: _flagvalues.FlagValues = ...,
391
+ required: bool = ...,
392
+ **args: Any
393
+ ) -> _flagvalues.FlagHolder[str | None]:
394
+ ...
395
+
396
+
397
+ @overload
398
+ def DEFINE_string( # pylint: disable=invalid-name
399
+ name: str,
400
+ default: str,
401
+ help: str | None, # pylint: disable=redefined-builtin
402
+ flag_values: _flagvalues.FlagValues = ...,
403
+ required: bool = ...,
404
+ **args: Any
405
+ ) -> _flagvalues.FlagHolder[str]:
406
+ ...
407
+
408
+
409
+ def DEFINE_string( # pylint: disable=invalid-name
410
+ name,
411
+ default,
412
+ help, # pylint: disable=redefined-builtin
413
+ flag_values=_flagvalues.FLAGS,
414
+ required=False,
415
+ **args
416
+ ):
417
+ """Registers a flag whose value can be any string."""
418
+ parser = _argument_parser.ArgumentParser[str]()
419
+ serializer = _argument_parser.ArgumentSerializer[str]()
420
+ return DEFINE(
421
+ parser,
422
+ name,
423
+ default,
424
+ help,
425
+ flag_values,
426
+ serializer,
427
+ required=True if required else False,
428
+ **args,
429
+ )
430
+
431
+
432
+ @overload
433
+ def DEFINE_boolean( # pylint: disable=invalid-name
434
+ name: str,
435
+ default: None | str | bool | int,
436
+ help: str | None, # pylint: disable=redefined-builtin
437
+ flag_values: _flagvalues.FlagValues = ...,
438
+ module_name: str | None = ...,
439
+ *,
440
+ required: Literal[True],
441
+ **args: Any
442
+ ) -> _flagvalues.FlagHolder[bool]:
443
+ ...
444
+
445
+
446
+ @overload
447
+ def DEFINE_boolean( # pylint: disable=invalid-name
448
+ name: str,
449
+ default: None,
450
+ help: str | None, # pylint: disable=redefined-builtin
451
+ flag_values: _flagvalues.FlagValues = ...,
452
+ module_name: str | None = ...,
453
+ required: bool = ...,
454
+ **args: Any
455
+ ) -> _flagvalues.FlagHolder[bool | None]:
456
+ ...
457
+
458
+
459
+ @overload
460
+ def DEFINE_boolean( # pylint: disable=invalid-name
461
+ name: str,
462
+ default: str | bool | int,
463
+ help: str | None, # pylint: disable=redefined-builtin
464
+ flag_values: _flagvalues.FlagValues = ...,
465
+ module_name: str | None = ...,
466
+ required: bool = ...,
467
+ **args: Any
468
+ ) -> _flagvalues.FlagHolder[bool]:
469
+ ...
470
+
471
+
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( # pytype: disable=bad-return-type
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: None | float | str,
518
+ help: str | None, # pylint: disable=redefined-builtin
519
+ lower_bound: float | None = ...,
520
+ upper_bound: float | None = ...,
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: str | None, # pylint: disable=redefined-builtin
534
+ lower_bound: float | None = ...,
535
+ upper_bound: float | None = ...,
536
+ flag_values: _flagvalues.FlagValues = ...,
537
+ required: bool = ...,
538
+ **args: Any
539
+ ) -> _flagvalues.FlagHolder[float | None]:
540
+ ...
541
+
542
+
543
+ @overload
544
+ def DEFINE_float( # pylint: disable=invalid-name
545
+ name: str,
546
+ default: float | str,
547
+ help: str | None, # pylint: disable=redefined-builtin
548
+ lower_bound: float | None = ...,
549
+ upper_bound: float | None = ...,
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: None | int | str,
607
+ help: str | None, # pylint: disable=redefined-builtin
608
+ lower_bound: int | None = ...,
609
+ upper_bound: int | None = ...,
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: str | None, # pylint: disable=redefined-builtin
623
+ lower_bound: int | None = ...,
624
+ upper_bound: int | None = ...,
625
+ flag_values: _flagvalues.FlagValues = ...,
626
+ required: bool = ...,
627
+ **args: Any
628
+ ) -> _flagvalues.FlagHolder[int | None]:
629
+ ...
630
+
631
+
632
+ @overload
633
+ def DEFINE_integer( # pylint: disable=invalid-name
634
+ name: str,
635
+ default: int | str,
636
+ help: str | None, # pylint: disable=redefined-builtin
637
+ lower_bound: int | None = ...,
638
+ upper_bound: int | None = ...,
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: str | None,
696
+ enum_values: Iterable[str],
697
+ help: str | None, # pylint: disable=redefined-builtin
698
+ flag_values: _flagvalues.FlagValues = ...,
699
+ module_name: str | None = ...,
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: str | None, # pylint: disable=redefined-builtin
713
+ flag_values: _flagvalues.FlagValues = ...,
714
+ module_name: str | None = ...,
715
+ required: bool = ...,
716
+ **args: Any
717
+ ) -> _flagvalues.FlagHolder[str | None]:
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: str | None, # pylint: disable=redefined-builtin
727
+ flag_values: _flagvalues.FlagValues = ...,
728
+ module_name: str | None = ...,
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: None | _ET | str,
780
+ enum_class: type[_ET],
781
+ help: str | None, # pylint: disable=redefined-builtin
782
+ flag_values: _flagvalues.FlagValues = ...,
783
+ module_name: str | None = ...,
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: str | None, # pylint: disable=redefined-builtin
798
+ flag_values: _flagvalues.FlagValues = ...,
799
+ module_name: str | None = ...,
800
+ case_sensitive: bool = ...,
801
+ required: bool = ...,
802
+ **args: Any
803
+ ) -> _flagvalues.FlagHolder[_ET | None]:
804
+ ...
805
+
806
+
807
+ @overload
808
+ def DEFINE_enum_class( # pylint: disable=invalid-name
809
+ name: str,
810
+ default: _ET | str,
811
+ enum_class: type[_ET],
812
+ help: str | None, # pylint: disable=redefined-builtin
813
+ flag_values: _flagvalues.FlagValues = ...,
814
+ module_name: str | None = ...,
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: 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[list[str] | None]:
887
+ ...
888
+
889
+
890
+ @overload
891
+ def DEFINE_list( # pylint: disable=invalid-name
892
+ name: str,
893
+ default: 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: 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[list[str] | None]:
966
+ ...
967
+
968
+
969
+ @overload
970
+ def DEFINE_spaceseplist( # pylint: disable=invalid-name
971
+ name: str,
972
+ default: 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: str | None = ...,
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: None | _T,
1049
+ help: str, # pylint: disable=redefined-builtin
1050
+ flag_values: _flagvalues.FlagValues = ...,
1051
+ module_name: str | None = ...,
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: str | None = ...,
1068
+ required: bool = ...,
1069
+ **args: Any
1070
+ ) -> _flagvalues.FlagHolder[list[_T] | None]:
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: str | None = ...,
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: str | None = ...,
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: 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[list[str] | None]:
1176
+ ...
1177
+
1178
+
1179
+ @overload
1180
+ def DEFINE_multi_string( # pylint: disable=invalid-name
1181
+ name: str,
1182
+ default: 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: None | Iterable[int] | int | str,
1240
+ help: str, # pylint: disable=redefined-builtin
1241
+ lower_bound: int | None = ...,
1242
+ upper_bound: int | None = ...,
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: int | None = ...,
1257
+ upper_bound: int | None = ...,
1258
+ flag_values: _flagvalues.FlagValues = ...,
1259
+ required: bool = ...,
1260
+ **args: Any
1261
+ ) -> _flagvalues.FlagHolder[list[int] | None]:
1262
+ ...
1263
+
1264
+
1265
+ @overload
1266
+ def DEFINE_multi_integer( # pylint: disable=invalid-name
1267
+ name: str,
1268
+ default: Iterable[int] | int | str,
1269
+ help: str, # pylint: disable=redefined-builtin
1270
+ lower_bound: int | None = ...,
1271
+ upper_bound: int | None = ...,
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: None | Iterable[float] | float | str,
1331
+ help: str, # pylint: disable=redefined-builtin
1332
+ lower_bound: float | None = ...,
1333
+ upper_bound: float | None = ...,
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: float | None = ...,
1348
+ upper_bound: float | None = ...,
1349
+ flag_values: _flagvalues.FlagValues = ...,
1350
+ required: bool = ...,
1351
+ **args: Any
1352
+ ) -> _flagvalues.FlagHolder[list[float] | None]:
1353
+ ...
1354
+
1355
+
1356
+ @overload
1357
+ def DEFINE_multi_float( # pylint: disable=invalid-name
1358
+ name: str,
1359
+ default: Iterable[float] | float | str,
1360
+ help: str, # pylint: disable=redefined-builtin
1361
+ lower_bound: float | None = ...,
1362
+ upper_bound: float | None = ...,
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: 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[list[str] | None]:
1442
+ ...
1443
+
1444
+
1445
+ @overload
1446
+ def DEFINE_multi_enum( # pylint: disable=invalid-name
1447
+ name: str,
1448
+ default: 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: str | None = ...,
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: 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: str | None = ...,
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: str | None = ...,
1549
+ required: bool = ...,
1550
+ **args: Any
1551
+ ) -> _flagvalues.FlagHolder[list[_ET] | None]:
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: str | None = ...,
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: _ET | Iterable[str] | str,
1577
+ enum_class: type[_ET],
1578
+ help: str, # pylint: disable=redefined-builtin
1579
+ flag_values: _flagvalues.FlagValues = ...,
1580
+ module_name: str | None = ...,
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: str | None = 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.12/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.12/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.abc import Iterable
22
+ import copy
23
+ import enum
24
+ import functools
25
+ from typing import Any, Generic, TypeVar
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: _T | None
87
+ default_as_str: str | None
88
+ default_unparsed: _T | None | str
89
+
90
+ parser: _argument_parser.ArgumentParser[_T]
91
+
92
+ def __init__(
93
+ self,
94
+ parser: _argument_parser.ArgumentParser[_T],
95
+ serializer: _argument_parser.ArgumentSerializer[_T] | None,
96
+ name: str,
97
+ default: _T | None | str,
98
+ help_string: str | None,
99
+ short_name: str | None = 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: _T | None = 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) -> _T | None:
136
+ return self._value
137
+
138
+ @value.setter
139
+ def value(self, value: _T | None):
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: _T | None) -> str | None:
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: 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: str | _T) -> _T | None:
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: _T | None) -> 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: _T | None | 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: str | _T) -> _T | None:
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: _T | None) -> 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: bool | None | str,
355
+ help: str | None, # pylint: disable=redefined-builtin
356
+ short_name: str | None = 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: str | None,
372
+ help: str | None, # pylint: disable=redefined-builtin
373
+ enum_values: Iterable[str],
374
+ short_name: str | None = 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: _ET | None | str,
404
+ help: str | None, # pylint: disable=redefined-builtin
405
+ enum_class: type[_ET],
406
+ short_name: str | None = 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: 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: str | _T | Iterable[_T]) -> list[_T]: # pylint: disable=arguments-renamed
467
+ arguments_list: list[str | _T]
468
+
469
+ if isinstance(arguments, str):
470
+ arguments_list = [arguments]
471
+
472
+ elif isinstance(arguments, 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: list[_T] | None) -> 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: 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.12/site-packages/absl/flags/_flagvalues.py ADDED
@@ -0,0 +1,1552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from collections.abc import Callable, Iterable, Iterator, Sequence
21
+ import copy
22
+ from importlib import abc
23
+ import logging
24
+ import os
25
+ import sys
26
+ from typing import Any, Generic, TextIO, TypeVar
27
+ from xml.dom import minidom
28
+
29
+ from absl.flags import _exceptions
30
+ from absl.flags import _flag
31
+ from absl.flags import _helpers
32
+ from absl.flags import _validators_classes
33
+ from absl.flags._flag import Flag
34
+
35
+ # Add flagvalues module to disclaimed module ids.
36
+ _helpers.disclaim_module_ids.add(id(sys.modules[__name__]))
37
+
38
+ _T = TypeVar('_T')
39
+ _T_co = TypeVar('_T_co', covariant=True) # pytype: disable=not-supported-yet
40
+
41
+
42
+ class ReloadDetector(abc.MetaPathFinder):
43
+ """Helper class for detecting reloads."""
44
+
45
+ def __init__(self):
46
+ self.reloading_modules = set()
47
+
48
+ def find_spec(self, fullname, path, target=None):
49
+ if fullname in sys.modules: # Indicates a reload.
50
+ self.reloading_modules.add(fullname)
51
+ return None
52
+
53
+
54
+ reload_detector = ReloadDetector()
55
+
56
+ # Register the hook by inserting it right before the last path finder.
57
+ # This should play nicely with lazy imports.
58
+ sys.meta_path.insert(-1, reload_detector)
59
+
60
+
61
+ class FlagValues:
62
+ """Registry of :class:`~absl.flags.Flag` objects.
63
+
64
+ A :class:`FlagValues` can then scan command line arguments, passing flag
65
+ arguments through to the 'Flag' objects that it owns. It also
66
+ provides easy access to the flag values. Typically only one
67
+ :class:`FlagValues` object is needed by an application:
68
+ :const:`FLAGS`.
69
+
70
+ This class is heavily overloaded:
71
+
72
+ :class:`Flag` objects are registered via ``__setitem__``::
73
+
74
+ FLAGS['longname'] = x # register a new flag
75
+
76
+ The ``.value`` attribute of the registered :class:`~absl.flags.Flag` objects
77
+ can be accessed as attributes of this :class:`FlagValues` object, through
78
+ ``__getattr__``. Both the long and short name of the original
79
+ :class:`~absl.flags.Flag` objects can be used to access its value::
80
+
81
+ FLAGS.longname # parsed flag value
82
+ FLAGS.x # parsed flag value (short name)
83
+
84
+ Command line arguments are scanned and passed to the registered
85
+ :class:`~absl.flags.Flag` objects through the ``__call__`` method. Unparsed
86
+ arguments, including ``argv[0]`` (e.g. the program name) are returned::
87
+
88
+ argv = FLAGS(sys.argv) # scan command line arguments
89
+
90
+ The original registered :class:`~absl.flags.Flag` objects can be retrieved
91
+ through the use of the dictionary-like operator, ``__getitem__``::
92
+
93
+ x = FLAGS['longname'] # access the registered Flag object
94
+
95
+ The ``str()`` operator of a :class:`absl.flags.FlagValues` object provides
96
+ help for all of the registered :class:`~absl.flags.Flag` objects.
97
+ """
98
+
99
+ _HAS_DYNAMIC_ATTRIBUTES = True
100
+
101
+ # A note on collections.abc.Mapping:
102
+ # FlagValues defines __getitem__, __iter__, and __len__. It makes perfect
103
+ # sense to let it be a collections.abc.Mapping class. However, we are not
104
+ # able to do so. The mixin methods, e.g. keys, values, are not uncommon flag
105
+ # names. Those flag values would not be accessible via the FLAGS.xxx form.
106
+
107
+ __dict__: dict[str, Any]
108
+
109
+ def __init__(self):
110
+ # Since everything in this class is so heavily overloaded, the only
111
+ # way of defining and using fields is to access __dict__ directly.
112
+
113
+ # Dictionary: flag name (string) -> Flag object.
114
+ self.__dict__['__flags'] = {}
115
+
116
+ # Set: name of hidden flag (string).
117
+ # Holds flags that should not be directly accessible from Python.
118
+ self.__dict__['__hiddenflags'] = set()
119
+
120
+ # Dictionary: module name (string) -> list of Flag objects that are defined
121
+ # by that module.
122
+ self.__dict__['__flags_by_module'] = {}
123
+ # Dictionary: module id (int) -> list of Flag objects that are defined by
124
+ # that module.
125
+ self.__dict__['__flags_by_module_id'] = {}
126
+ # Dictionary: module name (string) -> list of Flag objects that are
127
+ # key for that module.
128
+ self.__dict__['__key_flags_by_module'] = {}
129
+
130
+ # Bool: True if flags were parsed.
131
+ self.__dict__['__flags_parsed'] = False
132
+
133
+ # Bool: True if unparse_flags() was called.
134
+ self.__dict__['__unparse_flags_called'] = False
135
+
136
+ # None or Method(name, value) to call from __setattr__ for an unknown flag.
137
+ self.__dict__['__set_unknown'] = None
138
+
139
+ # A set of banned flag names. This is to prevent users from accidentally
140
+ # defining a flag that has the same name as a method on this class.
141
+ # Users can still allow defining the flag by passing
142
+ # allow_using_method_names=True in DEFINE_xxx functions.
143
+ self.__dict__['__banned_flag_names'] = frozenset(dir(FlagValues))
144
+
145
+ # Bool: Whether to use GNU style scanning.
146
+ self.__dict__['__use_gnu_getopt'] = True
147
+
148
+ # Bool: Whether use_gnu_getopt has been explicitly set by the user.
149
+ self.__dict__['__use_gnu_getopt_explicitly_set'] = False
150
+
151
+ # Function: Takes a flag name as parameter, returns a tuple
152
+ # (is_retired, type_is_bool).
153
+ self.__dict__['__is_retired_flag_func'] = None
154
+
155
+ def set_gnu_getopt(self, gnu_getopt: bool = True) -> None:
156
+ """Sets whether or not to use GNU style scanning.
157
+
158
+ GNU style allows mixing of flag and non-flag arguments. See
159
+ http://docs.python.org/library/getopt.html#getopt.gnu_getopt
160
+
161
+ Args:
162
+ gnu_getopt: bool, whether or not to use GNU style scanning.
163
+ """
164
+ self.__dict__['__use_gnu_getopt'] = gnu_getopt
165
+ self.__dict__['__use_gnu_getopt_explicitly_set'] = True
166
+
167
+ def is_gnu_getopt(self) -> bool:
168
+ return self.__dict__['__use_gnu_getopt']
169
+
170
+ def _flags(self) -> dict[str, Flag]:
171
+ return self.__dict__['__flags']
172
+
173
+ def flags_by_module_dict(self) -> dict[str, list[Flag]]:
174
+ """Returns the dictionary of module_name -> list of defined flags.
175
+
176
+ Returns:
177
+ A dictionary. Its keys are module names (strings). Its values
178
+ are lists of Flag objects.
179
+ """
180
+ return self.__dict__['__flags_by_module']
181
+
182
+ def flags_by_module_id_dict(self) -> dict[int, list[Flag]]:
183
+ """Returns the dictionary of module_id -> list of defined flags.
184
+
185
+ Returns:
186
+ A dictionary. Its keys are module IDs (ints). Its values
187
+ are lists of Flag objects.
188
+ """
189
+ return self.__dict__['__flags_by_module_id']
190
+
191
+ def key_flags_by_module_dict(self) -> dict[str, list[Flag]]:
192
+ """Returns the dictionary of module_name -> list of key flags.
193
+
194
+ Returns:
195
+ A dictionary. Its keys are module names (strings). Its values
196
+ are lists of Flag objects.
197
+ """
198
+ return self.__dict__['__key_flags_by_module']
199
+
200
+ def register_flag_by_module(self, module_name: str, flag: Flag) -> None:
201
+ """Records the module that defines a specific flag.
202
+
203
+ We keep track of which flag is defined by which module so that we
204
+ can later sort the flags by module.
205
+
206
+ Args:
207
+ module_name: str, the name of a Python module.
208
+ flag: Flag, the Flag instance that is key to the module.
209
+ """
210
+ flags_by_module = self.flags_by_module_dict()
211
+ flags_by_module.setdefault(module_name, []).append(flag)
212
+
213
+ def register_flag_by_module_id(self, module_id: int, flag: Flag) -> None:
214
+ """Records the module that defines a specific flag.
215
+
216
+ Args:
217
+ module_id: int, the ID of the Python module.
218
+ flag: Flag, the Flag instance that is key to the module.
219
+ """
220
+ flags_by_module_id = self.flags_by_module_id_dict()
221
+ flags_by_module_id.setdefault(module_id, []).append(flag)
222
+
223
+ def register_key_flag_for_module(self, module_name: str, flag: Flag) -> None:
224
+ """Specifies that a flag is a key flag for a module.
225
+
226
+ Args:
227
+ module_name: str, the name of a Python module.
228
+ flag: Flag, the Flag instance that is key to the module.
229
+ """
230
+ key_flags_by_module = self.key_flags_by_module_dict()
231
+ # The list of key flags for the module named module_name.
232
+ key_flags = key_flags_by_module.setdefault(module_name, [])
233
+ # Add flag, but avoid duplicates.
234
+ if flag not in key_flags:
235
+ key_flags.append(flag)
236
+
237
+ def _flag_is_registered(self, flag_obj: Flag) -> bool:
238
+ """Checks whether a Flag object is registered under long name or short name.
239
+
240
+ Args:
241
+ flag_obj: Flag, the Flag instance to check for.
242
+
243
+ Returns:
244
+ bool, True iff flag_obj is registered under long name or short name.
245
+ """
246
+ flag_dict = self._flags()
247
+ # Check whether flag_obj is registered under its long name.
248
+ name = flag_obj.name
249
+ if name in flag_dict and flag_dict[name] == flag_obj:
250
+ return True
251
+ # Check whether flag_obj is registered under its short name.
252
+ short_name = flag_obj.short_name
253
+ if (
254
+ short_name is not None
255
+ and short_name in flag_dict
256
+ and flag_dict[short_name] == flag_obj
257
+ ):
258
+ return True
259
+ return False
260
+
261
+ def _cleanup_unregistered_flag_from_module_dicts(
262
+ self, flag_obj: Flag
263
+ ) -> None:
264
+ """Cleans up unregistered flags from all module -> [flags] dictionaries.
265
+
266
+ If flag_obj is registered under either its long name or short name, it
267
+ won't be removed from the dictionaries.
268
+
269
+ Args:
270
+ flag_obj: Flag, the Flag instance to clean up for.
271
+ """
272
+ if self._flag_is_registered(flag_obj):
273
+ return
274
+ # Materialize dict values to list to avoid concurrent modification.
275
+ for flags_in_module in [
276
+ *self.flags_by_module_dict().values(),
277
+ *self.flags_by_module_id_dict().values(),
278
+ *self.key_flags_by_module_dict().values(),
279
+ ]:
280
+ # While (as opposed to if) takes care of multiple occurrences of a
281
+ # flag in the list for the same module.
282
+ while flag_obj in flags_in_module:
283
+ flags_in_module.remove(flag_obj)
284
+
285
+ def get_flags_for_module(self, module: str | Any) -> list[Flag]:
286
+ """Returns the list of flags defined by a module.
287
+
288
+ Args:
289
+ module: module|str, the module to get flags from.
290
+
291
+ Returns:
292
+ [Flag], a new list of Flag instances. Caller may update this list as
293
+ desired: none of those changes will affect the internals of this
294
+ FlagValue instance.
295
+ """
296
+ if not isinstance(module, str):
297
+ module = module.__name__
298
+ if module == '__main__':
299
+ module = sys.argv[0]
300
+
301
+ return list(self.flags_by_module_dict().get(module, []))
302
+
303
+ def get_key_flags_for_module(self, module: str | Any) -> list[Flag]:
304
+ """Returns the list of key flags for a module.
305
+
306
+ Args:
307
+ module: module|str, the module to get key flags from.
308
+
309
+ Returns:
310
+ [Flag], a new list of Flag instances. Caller may update this list as
311
+ desired: none of those changes will affect the internals of this
312
+ FlagValue instance.
313
+ """
314
+ if not isinstance(module, str):
315
+ module = module.__name__
316
+ if module == '__main__':
317
+ module = sys.argv[0]
318
+
319
+ # Any flag is a key flag for the module that defined it. NOTE:
320
+ # key_flags is a fresh list: we can update it without affecting the
321
+ # internals of this FlagValues object.
322
+ key_flags = self.get_flags_for_module(module)
323
+
324
+ # Take into account flags explicitly declared as key for a module.
325
+ for flag in self.key_flags_by_module_dict().get(module, []):
326
+ if flag not in key_flags:
327
+ key_flags.append(flag)
328
+ return key_flags
329
+
330
+ # TODO(yileiyang): Restrict default to Optional[str].
331
+ def find_module_defining_flag(
332
+ self, flagname: str, default: _T | None = None
333
+ ) -> str | _T | None:
334
+ """Return the name of the module defining this flag, or default.
335
+
336
+ Args:
337
+ flagname: str, name of the flag to lookup.
338
+ default: Value to return if flagname is not defined. Defaults to None.
339
+
340
+ Returns:
341
+ The name of the module which registered the flag with this name.
342
+ If no such module exists (i.e. no flag with this name exists),
343
+ we return default.
344
+ """
345
+ registered_flag = self._flags().get(flagname)
346
+ if registered_flag is None:
347
+ return default
348
+ for module, flags in self.flags_by_module_dict().items():
349
+ for flag in flags:
350
+ # It must compare the flag with the one in _flags. This is because a
351
+ # flag might be overridden only for its long name (or short name),
352
+ # and only its short name (or long name) is considered registered.
353
+ if (
354
+ flag.name == registered_flag.name
355
+ and flag.short_name == registered_flag.short_name
356
+ ):
357
+ return module
358
+ return default
359
+
360
+ # TODO(yileiyang): Restrict default to Optional[str].
361
+ def find_module_id_defining_flag(
362
+ self, flagname: str, default: _T | None = None
363
+ ) -> int | _T | None:
364
+ """Return the ID of the module defining this flag, or default.
365
+
366
+ Args:
367
+ flagname: str, name of the flag to lookup.
368
+ default: Value to return if flagname is not defined. Defaults to None.
369
+
370
+ Returns:
371
+ The ID of the module which registered the flag with this name.
372
+ If no such module exists (i.e. no flag with this name exists),
373
+ we return default.
374
+ """
375
+ registered_flag = self._flags().get(flagname)
376
+ if registered_flag is None:
377
+ return default
378
+ for module_id, flags in self.flags_by_module_id_dict().items():
379
+ for flag in flags:
380
+ # It must compare the flag with the one in _flags. This is because a
381
+ # flag might be overridden only for its long name (or short name),
382
+ # and only its short name (or long name) is considered registered.
383
+ if (
384
+ flag.name == registered_flag.name
385
+ and flag.short_name == registered_flag.short_name
386
+ ):
387
+ return module_id
388
+ return default
389
+
390
+ def _register_unknown_flag_setter(
391
+ self, setter: Callable[[str, Any], None]
392
+ ) -> None:
393
+ """Allow set default values for undefined flags.
394
+
395
+ Args:
396
+ setter: Method(name, value) to call to __setattr__ an unknown flag. Must
397
+ raise NameError or ValueError for invalid name/value.
398
+ """
399
+ self.__dict__['__set_unknown'] = setter
400
+
401
+ def _set_unknown_flag(self, name: str, value: _T) -> _T:
402
+ """Returns value if setting flag |name| to |value| returned True.
403
+
404
+ Args:
405
+ name: str, name of the flag to set.
406
+ value: Value to set.
407
+
408
+ Returns:
409
+ Flag value on successful call.
410
+
411
+ Raises:
412
+ UnrecognizedFlagError
413
+ IllegalFlagValueError
414
+ """
415
+ setter = self.__dict__['__set_unknown']
416
+ if setter:
417
+ try:
418
+ setter(name, value)
419
+ return value
420
+ except (TypeError, ValueError): # Flag value is not valid.
421
+ raise _exceptions.IllegalFlagValueError(
422
+ f'"{value}" is not valid for --{name}'
423
+ )
424
+ except NameError: # Flag name is not valid.
425
+ pass
426
+ raise _exceptions.UnrecognizedFlagError(name, value)
427
+
428
+ def append_flag_values(self, flag_values: 'FlagValues') -> None:
429
+ """Appends flags registered in another FlagValues instance.
430
+
431
+ Args:
432
+ flag_values: FlagValues, the FlagValues instance from which to copy flags.
433
+ """
434
+ for flag_name, flag in flag_values._flags().items(): # pylint: disable=protected-access
435
+ # Each flags with short_name appears here twice (once under its
436
+ # normal name, and again with its short name). To prevent
437
+ # problems (DuplicateFlagError) with double flag registration, we
438
+ # perform a check to make sure that the entry we're looking at is
439
+ # for its normal name.
440
+ if flag_name == flag.name:
441
+ try:
442
+ self[flag_name] = flag
443
+ except _exceptions.DuplicateFlagError:
444
+ raise _exceptions.DuplicateFlagError.from_flag(
445
+ flag_name, self, other_flag_values=flag_values
446
+ )
447
+
448
+ def remove_flag_values(
449
+ self, flag_values: 'FlagValues | Iterable[str]'
450
+ ) -> None:
451
+ """Remove flags that were previously appended from another FlagValues.
452
+
453
+ Args:
454
+ flag_values: FlagValues, the FlagValues instance containing flags to
455
+ remove.
456
+ """
457
+ for flag_name in flag_values:
458
+ self.__delattr__(flag_name)
459
+
460
+ def __setitem__(self, name: str, flag: Flag) -> None:
461
+ """Registers a new flag variable."""
462
+ fl = self._flags()
463
+ if not isinstance(flag, _flag.Flag):
464
+ raise _exceptions.IllegalFlagValueError(
465
+ f'Expect Flag instances, found type {type(flag)}. '
466
+ "Maybe you didn't mean to use FlagValue.__setitem__?"
467
+ )
468
+ if not isinstance(name, str):
469
+ raise _exceptions.Error('Flag name must be a string')
470
+ if not name:
471
+ raise _exceptions.Error('Flag name cannot be empty')
472
+ if ' ' in name:
473
+ raise _exceptions.Error('Flag name cannot contain a space')
474
+ self._check_method_name_conflicts(name, flag)
475
+ if name in fl and not flag.allow_override and not fl[name].allow_override:
476
+ module, module_name = _helpers.get_calling_module_object_and_name()
477
+ if self.find_module_defining_flag(name) == module_name and (
478
+ id(module) != self.find_module_id_defining_flag(name)
479
+ or module_name in reload_detector.reloading_modules
480
+ ):
481
+ # If the flag has already been defined by a module with the same name,
482
+ # but a different ID, we can stop here because it indicates that the
483
+ # module is simply being imported a subsequent time.
484
+ # In case the module is being reloaded (using `importlib.reload`), it'll
485
+ # have the same ID, so we detect it using reload_detector.
486
+ return
487
+ raise _exceptions.DuplicateFlagError.from_flag(name, self)
488
+ # If a new flag overrides an old one, we need to cleanup the old flag's
489
+ # modules if it's not registered.
490
+ flags_to_cleanup = set()
491
+ short_name: str | None = flag.short_name
492
+ if short_name is not None:
493
+ if (
494
+ short_name in fl
495
+ and not flag.allow_override
496
+ and not fl[short_name].allow_override
497
+ ):
498
+ raise _exceptions.DuplicateFlagError.from_flag(short_name, self)
499
+ if short_name in fl and fl[short_name] != flag:
500
+ flags_to_cleanup.add(fl[short_name])
501
+ fl[short_name] = flag
502
+ if (
503
+ name not in fl # new flag
504
+ or fl[name].using_default_value
505
+ or not flag.using_default_value
506
+ ):
507
+ if name in fl and fl[name] != flag:
508
+ flags_to_cleanup.add(fl[name])
509
+ fl[name] = flag
510
+ for f in flags_to_cleanup:
511
+ self._cleanup_unregistered_flag_from_module_dicts(f)
512
+
513
+ def __dir__(self) -> list[str]:
514
+ """Returns list of names of all defined flags.
515
+
516
+ Useful for TAB-completion in ipython.
517
+
518
+ Returns:
519
+ [str], a list of names of all defined flags.
520
+ """
521
+ return sorted(self._flags())
522
+
523
+ def __getitem__(self, name: str) -> Flag:
524
+ """Returns the Flag object for the flag --name."""
525
+ return self._flags()[name]
526
+
527
+ def _hide_flag(self, name):
528
+ """Marks the flag --name as hidden."""
529
+ self.__dict__['__hiddenflags'].add(name)
530
+
531
+ def __getattr__(self, name: str) -> Any:
532
+ """Retrieves the 'value' attribute of the flag --name."""
533
+ flag_entry = self._flags().get(name)
534
+ if flag_entry is None:
535
+ raise AttributeError(name)
536
+ if name in self.__dict__['__hiddenflags']:
537
+ raise AttributeError(name)
538
+
539
+ if self.__dict__['__flags_parsed'] or flag_entry.present:
540
+ return flag_entry.value
541
+ else:
542
+ raise _exceptions.UnparsedFlagAccessError(
543
+ 'Trying to access flag --%s before flags were parsed.' % name
544
+ )
545
+
546
+ def __setattr__(self, name: str, value: _T) -> _T:
547
+ """Sets the 'value' attribute of the flag --name."""
548
+ self._set_attributes(**{name: value})
549
+ return value
550
+
551
+ def _set_attributes(self, **attributes: Any) -> None:
552
+ """Sets multiple flag values together, triggers validators afterwards."""
553
+ fl = self._flags()
554
+ known_flag_vals = {}
555
+ known_flag_used_defaults = {}
556
+ try:
557
+ for name, value in attributes.items():
558
+ if name in self.__dict__['__hiddenflags']:
559
+ raise AttributeError(name)
560
+ flag_entry = fl.get(name)
561
+ if flag_entry is not None:
562
+ orig = flag_entry.value
563
+ flag_entry.value = value
564
+ known_flag_vals[name] = orig
565
+ else:
566
+ self._set_unknown_flag(name, value)
567
+ for name in known_flag_vals:
568
+ self._assert_validators(fl[name].validators)
569
+ known_flag_used_defaults[name] = fl[name].using_default_value
570
+ fl[name].using_default_value = False
571
+ except:
572
+ for name, orig in known_flag_vals.items():
573
+ fl[name].value = orig
574
+ for name, orig in known_flag_used_defaults.items():
575
+ fl[name].using_default_value = orig
576
+ # NOTE: We do not attempt to undo unknown flag side effects because we
577
+ # cannot reliably undo the user-configured behavior.
578
+ raise
579
+
580
+ def validate_all_flags(self) -> None:
581
+ """Verifies whether all flags pass validation.
582
+
583
+ Raises:
584
+ AttributeError: Raised if validators work with a non-existing flag.
585
+ IllegalFlagValueError: Raised if validation fails for at least one
586
+ validator.
587
+ """
588
+ all_validators = set()
589
+ for flag in self._flags().values():
590
+ all_validators.update(flag.validators)
591
+ self._assert_validators(all_validators)
592
+
593
+ def _assert_validators(
594
+ self, validators: Iterable[_validators_classes.Validator]
595
+ ) -> None:
596
+ """Asserts if all validators in the list are satisfied.
597
+
598
+ It asserts validators in the order they were created.
599
+
600
+ Args:
601
+ validators: Iterable(validators.Validator), validators to be verified.
602
+
603
+ Raises:
604
+ AttributeError: Raised if validators work with a non-existing flag.
605
+ IllegalFlagValueError: Raised if validation fails for at least one
606
+ validator.
607
+ """
608
+ messages = []
609
+ bad_flags: set[str] = set()
610
+ for validator in sorted(
611
+ validators, key=lambda validator: validator.insertion_index
612
+ ):
613
+ try:
614
+ if isinstance(validator, _validators_classes.SingleFlagValidator):
615
+ if validator.flag_name in bad_flags:
616
+ continue
617
+ elif isinstance(validator, _validators_classes.MultiFlagsValidator):
618
+ if bad_flags & set(validator.flag_names):
619
+ continue
620
+ validator.verify(self)
621
+ except _exceptions.ValidationError as e:
622
+ if isinstance(validator, _validators_classes.SingleFlagValidator):
623
+ bad_flags.add(validator.flag_name)
624
+ elif isinstance(validator, _validators_classes.MultiFlagsValidator):
625
+ bad_flags.update(set(validator.flag_names))
626
+ message = validator.print_flags_with_values(self)
627
+ messages.append('%s: %s' % (message, str(e)))
628
+ if messages:
629
+ raise _exceptions.IllegalFlagValueError('\n'.join(messages))
630
+
631
+ def __delattr__(self, flag_name: str) -> None:
632
+ """Deletes a previously-defined flag from a flag object.
633
+
634
+ This method makes sure we can delete a flag by using
635
+
636
+ del FLAGS.<flag_name>
637
+
638
+ E.g.,
639
+
640
+ flags.DEFINE_integer('foo', 1, 'Integer flag.')
641
+ del flags.FLAGS.foo
642
+
643
+ If a flag is also registered by its the other name (long name or short
644
+ name), the other name won't be deleted.
645
+
646
+ Args:
647
+ flag_name: str, the name of the flag to be deleted.
648
+
649
+ Raises:
650
+ AttributeError: Raised when there is no registered flag named flag_name.
651
+ """
652
+ fl = self._flags()
653
+ flag_entry = fl.get(flag_name)
654
+ if flag_entry is None:
655
+ raise AttributeError(flag_name)
656
+ del fl[flag_name]
657
+
658
+ self._cleanup_unregistered_flag_from_module_dicts(flag_entry)
659
+
660
+ def set_default(self, name: str, value: Any) -> None:
661
+ """Changes the default value of the named flag object.
662
+
663
+ The flag's current value is also updated if the flag is currently using
664
+ the default value, i.e. not specified in the command line, and not set
665
+ by FLAGS.name = value.
666
+
667
+ Args:
668
+ name: str, the name of the flag to modify.
669
+ value: The new default value.
670
+
671
+ Raises:
672
+ UnrecognizedFlagError: Raised when there is no registered flag named name.
673
+ IllegalFlagValueError: Raised when value is not valid.
674
+ """
675
+ fl = self._flags()
676
+ flag_entry = fl.get(name)
677
+ if flag_entry is None:
678
+ self._set_unknown_flag(name, value)
679
+ return
680
+ flag_entry._set_default(value) # pylint: disable=protected-access
681
+ self._assert_validators(flag_entry.validators)
682
+
683
+ def __contains__(self, name: str) -> bool:
684
+ """Returns True if name is a value (flag) in the dict."""
685
+ return name in self._flags()
686
+
687
+ def __len__(self) -> int:
688
+ return len(self.__dict__['__flags'])
689
+
690
+ def __iter__(self) -> Iterator[str]:
691
+ return iter(self._flags())
692
+
693
+ def __call__(
694
+ self, argv: Sequence[str], known_only: bool = False
695
+ ) -> list[str]:
696
+ """Parses flags from argv; stores parsed flags into this FlagValues object.
697
+
698
+ All unparsed arguments are returned.
699
+
700
+ Args:
701
+ argv: a tuple/list of strings.
702
+ known_only: bool, if True, parse and remove known flags; return the rest
703
+ untouched. Unknown flags specified by --undefok are not returned.
704
+
705
+ Returns:
706
+ The list of arguments not parsed as options, including argv[0].
707
+
708
+ Raises:
709
+ Error: Raised on any parsing error.
710
+ TypeError: Raised on passing wrong type of arguments.
711
+ ValueError: Raised on flag value parsing error.
712
+ """
713
+ if isinstance(argv, (str, bytes)):
714
+ raise TypeError(
715
+ 'argv should be a tuple/list of strings, not bytes or string.'
716
+ )
717
+ if not argv:
718
+ raise ValueError(
719
+ 'argv cannot be an empty list, and must contain the program name as '
720
+ 'the first element.'
721
+ )
722
+
723
+ # This pre parses the argv list for --flagfile=<> options.
724
+ program_name = argv[0]
725
+ args = self.read_flags_from_files(argv[1:], force_gnu=False)
726
+
727
+ # Parse the arguments.
728
+ unknown_flags, unparsed_args = self._parse_args(args, known_only)
729
+
730
+ # Handle unknown flags by raising UnrecognizedFlagError.
731
+ # Note some users depend on us raising this particular error.
732
+ for name, value in unknown_flags:
733
+ suggestions = _helpers.get_flag_suggestions(name, list(self))
734
+ raise _exceptions.UnrecognizedFlagError(
735
+ name, value, suggestions=suggestions
736
+ )
737
+
738
+ self.mark_as_parsed()
739
+ self.validate_all_flags()
740
+ return [program_name] + unparsed_args
741
+
742
+ def __getstate__(self) -> Any:
743
+ raise TypeError("can't pickle FlagValues")
744
+
745
+ def __copy__(self) -> Any:
746
+ raise TypeError(
747
+ 'FlagValues does not support shallow copies. '
748
+ 'Use absl.testing.flagsaver or copy.deepcopy instead.'
749
+ )
750
+
751
+ def __deepcopy__(self, memo) -> Any:
752
+ result = object.__new__(type(self))
753
+ result.__dict__.update(copy.deepcopy(self.__dict__, memo))
754
+ return result
755
+
756
+ def _set_is_retired_flag_func(self, is_retired_flag_func):
757
+ """Sets a function for checking retired flags.
758
+
759
+ Do not use it. This is a private absl API used to check retired flags
760
+ registered by the absl C++ flags library.
761
+
762
+ Args:
763
+ is_retired_flag_func: Callable(str) -> (bool, bool), a function takes flag
764
+ name as parameter, returns a tuple (is_retired, type_is_bool).
765
+ """
766
+ self.__dict__['__is_retired_flag_func'] = is_retired_flag_func
767
+
768
+ def _parse_args(
769
+ self, args: list[str], known_only: bool
770
+ ) -> tuple[list[tuple[str, Any]], list[str]]:
771
+ """Helper function to do the main argument parsing.
772
+
773
+ This function goes through args and does the bulk of the flag parsing.
774
+ It will find the corresponding flag in our flag dictionary, and call its
775
+ .parse() method on the flag value.
776
+
777
+ Args:
778
+ args: [str], a list of strings with the arguments to parse.
779
+ known_only: bool, if True, parse and remove known flags; return the rest
780
+ untouched. Unknown flags specified by --undefok are not returned.
781
+
782
+ Returns:
783
+ A tuple with the following:
784
+ unknown_flags: List of (flag name, arg) for flags we don't know about.
785
+ unparsed_args: List of arguments we did not parse.
786
+
787
+ Raises:
788
+ Error: Raised on any parsing error.
789
+ ValueError: Raised on flag value parsing error.
790
+ """
791
+ unparsed_names_and_args: list[tuple[str | None, str]] = []
792
+ undefok: set[str] = set()
793
+ retired_flag_func = self.__dict__['__is_retired_flag_func']
794
+
795
+ flag_dict = self._flags()
796
+ args_it = iter(args)
797
+ del args
798
+ for arg in args_it:
799
+ value = None
800
+
801
+ def get_value() -> str:
802
+ try:
803
+ return next(args_it) if value is None else value # pylint: disable=cell-var-from-loop
804
+ except StopIteration:
805
+ raise _exceptions.Error('Missing value for flag ' + arg) from None # pylint: disable=cell-var-from-loop
806
+
807
+ if not arg.startswith('-'):
808
+ # A non-argument: default is break, GNU is skip.
809
+ unparsed_names_and_args.append((None, arg))
810
+ if self.is_gnu_getopt():
811
+ continue
812
+ else:
813
+ break
814
+
815
+ if arg == '--':
816
+ if known_only:
817
+ unparsed_names_and_args.append((None, arg))
818
+ break
819
+
820
+ # At this point, arg must start with '-'.
821
+ if arg.startswith('--'):
822
+ arg_without_dashes = arg[2:]
823
+ else:
824
+ arg_without_dashes = arg[1:]
825
+
826
+ if '=' in arg_without_dashes:
827
+ name, value = arg_without_dashes.split('=', 1)
828
+ else:
829
+ name, value = arg_without_dashes, None
830
+
831
+ if not name:
832
+ # The argument is all dashes (including one dash).
833
+ unparsed_names_and_args.append((None, arg))
834
+ if self.is_gnu_getopt():
835
+ continue
836
+ else:
837
+ break
838
+
839
+ # --undefok is a special case.
840
+ if name == 'undefok':
841
+ value = get_value()
842
+ undefok.update(v.strip() for v in value.split(','))
843
+ undefok.update('no' + v.strip() for v in value.split(','))
844
+ continue
845
+
846
+ flag = flag_dict.get(name)
847
+ if flag is not None:
848
+ if flag.boolean and value is None:
849
+ value = 'true'
850
+ else:
851
+ value = get_value()
852
+ elif name.startswith('no') and len(name) > 2:
853
+ # Boolean flags can take the form of --noflag, with no value.
854
+ noflag = flag_dict.get(name[2:])
855
+ if noflag is not None and noflag.boolean:
856
+ if value is not None:
857
+ raise ValueError(arg + ' does not take an argument')
858
+ flag = noflag
859
+ value = 'false'
860
+
861
+ if retired_flag_func and flag is None:
862
+ is_retired, is_bool = retired_flag_func(name)
863
+
864
+ # If we didn't recognize that flag, but it starts with
865
+ # "no" then maybe it was a boolean flag specified in the
866
+ # --nofoo form.
867
+ if not is_retired and name.startswith('no'):
868
+ is_retired, is_bool = retired_flag_func(name[2:])
869
+ is_retired = is_retired and is_bool
870
+
871
+ if is_retired:
872
+ if not is_bool and value is None:
873
+ # This happens when a non-bool retired flag is specified
874
+ # in format of "--flag value".
875
+ get_value()
876
+ logging.error(
877
+ 'Flag "%s" is retired and should no longer be specified. See '
878
+ 'https://abseil.io/tips/90.',
879
+ name,
880
+ )
881
+ continue
882
+
883
+ if flag is not None:
884
+ # LINT.IfChange
885
+ flag.parse(value)
886
+ flag.using_default_value = False
887
+ # LINT.ThenChange(../testing/flagsaver.py:flag_override_parsing)
888
+ else:
889
+ unparsed_names_and_args.append((name, arg))
890
+
891
+ unknown_flags = []
892
+ unparsed_args = []
893
+ for arg_name, arg in unparsed_names_and_args:
894
+ if arg_name is None:
895
+ # Positional arguments.
896
+ unparsed_args.append(arg)
897
+ elif arg_name in undefok:
898
+ # Remove undefok flags.
899
+ continue
900
+ else:
901
+ # This is an unknown flag.
902
+ if known_only:
903
+ unparsed_args.append(arg)
904
+ else:
905
+ unknown_flags.append((arg_name, arg))
906
+
907
+ unparsed_args.extend(list(args_it))
908
+ return unknown_flags, unparsed_args
909
+
910
+ def is_parsed(self) -> bool:
911
+ """Returns whether flags were parsed."""
912
+ return self.__dict__['__flags_parsed']
913
+
914
+ def mark_as_parsed(self) -> None:
915
+ """Explicitly marks flags as parsed.
916
+
917
+ Use this when the caller knows that this FlagValues has been parsed as if
918
+ a ``__call__()`` invocation has happened. This is only a public method for
919
+ use by things like appcommands which do additional command like parsing.
920
+ """
921
+ self.__dict__['__flags_parsed'] = True
922
+
923
+ def unparse_flags(self) -> None:
924
+ """Unparses all flags to the point before any FLAGS(argv) was called."""
925
+ for f in self._flags().values():
926
+ f.unparse()
927
+ # We log this message before marking flags as unparsed to avoid a
928
+ # problem when the logging library causes flags access.
929
+ logging.info('unparse_flags() called; flags access will now raise errors.')
930
+ self.__dict__['__flags_parsed'] = False
931
+ self.__dict__['__unparse_flags_called'] = True
932
+
933
+ def flag_values_dict(self) -> dict[str, Any]:
934
+ """Returns a dictionary that maps flag names to flag values."""
935
+ return {name: flag.value for name, flag in list(self._flags().items())}
936
+
937
+ def __str__(self):
938
+ """Returns a help string for all known flags."""
939
+ return self.get_help()
940
+
941
+ def get_help(
942
+ self, prefix: str = '', include_special_flags: bool = True
943
+ ) -> str:
944
+ """Returns a help string for all known flags.
945
+
946
+ Args:
947
+ prefix: str, per-line output prefix.
948
+ include_special_flags: bool, whether to include description of
949
+ SPECIAL_FLAGS, i.e. --flagfile and --undefok.
950
+
951
+ Returns:
952
+ str, formatted help message.
953
+ """
954
+ flags_by_module = self.flags_by_module_dict()
955
+ if flags_by_module:
956
+ modules = sorted(flags_by_module)
957
+ # Print the help for the main module first, if possible.
958
+ main_module = sys.argv[0]
959
+ if main_module in modules:
960
+ modules.remove(main_module)
961
+ modules = [main_module] + modules
962
+ return self._get_help_for_modules(modules, prefix, include_special_flags)
963
+ else:
964
+ output_lines: list[str] = []
965
+ # Just print one long list of flags.
966
+ values = list(self._flags().values())
967
+ if include_special_flags:
968
+ values.extend(_helpers.SPECIAL_FLAGS._flags().values()) # pylint: disable=protected-access
969
+ self._render_flag_list(values, output_lines, prefix)
970
+ return '\n'.join(output_lines)
971
+
972
+ def _get_help_for_modules(self, modules, prefix, include_special_flags):
973
+ """Returns the help string for a list of modules.
974
+
975
+ Private to absl.flags package.
976
+
977
+ Args:
978
+ modules: List[str], a list of modules to get the help string for.
979
+ prefix: str, a string that is prepended to each generated help line.
980
+ include_special_flags: bool, whether to include description of
981
+ SPECIAL_FLAGS, i.e. --flagfile and --undefok.
982
+ """
983
+ output_lines = []
984
+ for module in modules:
985
+ self._render_our_module_flags(module, output_lines, prefix)
986
+ if include_special_flags:
987
+ self._render_module_flags(
988
+ 'absl.flags',
989
+ _helpers.SPECIAL_FLAGS._flags().values(), # pylint: disable=protected-access # pytype: disable=attribute-error
990
+ output_lines,
991
+ prefix,
992
+ )
993
+ return '\n'.join(output_lines)
994
+
995
+ def _render_module_flags(self, module, flags, output_lines, prefix=''):
996
+ """Returns a help string for a given module."""
997
+ if not isinstance(module, str):
998
+ module = module.__name__
999
+ output_lines.append('\n%s%s:' % (prefix, module))
1000
+ self._render_flag_list(flags, output_lines, prefix + ' ')
1001
+
1002
+ def _render_our_module_flags(self, module, output_lines, prefix=''):
1003
+ """Returns a help string for a given module."""
1004
+ flags = self.get_flags_for_module(module)
1005
+ if flags:
1006
+ self._render_module_flags(module, flags, output_lines, prefix)
1007
+
1008
+ def _render_our_module_key_flags(self, module, output_lines, prefix=''):
1009
+ """Returns a help string for the key flags of a given module.
1010
+
1011
+ Args:
1012
+ module: module|str, the module to render key flags for.
1013
+ output_lines: [str], a list of strings. The generated help message lines
1014
+ will be appended to this list.
1015
+ prefix: str, a string that is prepended to each generated help line.
1016
+ """
1017
+ key_flags = self.get_key_flags_for_module(module)
1018
+ if key_flags:
1019
+ self._render_module_flags(module, key_flags, output_lines, prefix)
1020
+
1021
+ def module_help(self, module: Any) -> str:
1022
+ """Describes the key flags of a module.
1023
+
1024
+ Args:
1025
+ module: module|str, the module to describe the key flags for.
1026
+
1027
+ Returns:
1028
+ str, describing the key flags of a module.
1029
+ """
1030
+ helplist: list[str] = []
1031
+ self._render_our_module_key_flags(module, helplist)
1032
+ return '\n'.join(helplist)
1033
+
1034
+ def main_module_help(self) -> str:
1035
+ """Describes the key flags of the main module.
1036
+
1037
+ Returns:
1038
+ str, describing the key flags of the main module.
1039
+ """
1040
+ return self.module_help(sys.argv[0])
1041
+
1042
+ def _render_flag_list(self, flaglist, output_lines, prefix=' '):
1043
+ fl = self._flags()
1044
+ special_fl = _helpers.SPECIAL_FLAGS._flags() # pylint: disable=protected-access # pytype: disable=attribute-error
1045
+ flaglist = [(flag.name, flag) for flag in flaglist]
1046
+ flaglist.sort()
1047
+ flagset = {}
1048
+ for name, flag in flaglist:
1049
+ # It's possible this flag got deleted or overridden since being
1050
+ # registered in the per-module flaglist. Check now against the
1051
+ # canonical source of current flag information, the _flags.
1052
+ if fl.get(name, None) != flag and special_fl.get(name, None) != flag:
1053
+ # a different flag is using this name now
1054
+ continue
1055
+ # only print help once
1056
+ if flag in flagset:
1057
+ continue
1058
+ flagset[flag] = 1
1059
+ flaghelp = ''
1060
+ if flag.short_name:
1061
+ flaghelp += '-%s,' % flag.short_name
1062
+ if flag.boolean:
1063
+ flaghelp += '--[no]%s:' % flag.name
1064
+ else:
1065
+ flaghelp += '--%s:' % flag.name
1066
+ flaghelp += ' '
1067
+ if flag.help:
1068
+ flaghelp += flag.help
1069
+ flaghelp = _helpers.text_wrap(
1070
+ flaghelp, indent=prefix + ' ', firstline_indent=prefix
1071
+ )
1072
+ if flag.default_as_str:
1073
+ flaghelp += '\n'
1074
+ flaghelp += _helpers.text_wrap(
1075
+ '(default: %s)' % flag.default_as_str, indent=prefix + ' '
1076
+ )
1077
+ if flag.parser.syntactic_help:
1078
+ flaghelp += '\n'
1079
+ flaghelp += _helpers.text_wrap(
1080
+ '(%s)' % flag.parser.syntactic_help, indent=prefix + ' '
1081
+ )
1082
+ output_lines.append(flaghelp)
1083
+
1084
+ def get_flag_value(self, name: str, default: Any) -> Any: # pylint: disable=invalid-name
1085
+ """Returns the value of a flag (if not None) or a default value.
1086
+
1087
+ Args:
1088
+ name: str, the name of a flag.
1089
+ default: Default value to use if the flag value is None.
1090
+
1091
+ Returns:
1092
+ Requested flag value or default.
1093
+ """
1094
+
1095
+ value = self.__getattr__(name)
1096
+ if value is not None: # Can't do if not value, b/c value might be '0' or ""
1097
+ return value
1098
+ else:
1099
+ return default
1100
+
1101
+ def _is_flag_file_directive(self, flag_string):
1102
+ """Checks whether flag_string contain a --flagfile=<foo> directive."""
1103
+ if isinstance(flag_string, str):
1104
+ if flag_string.startswith('--flagfile='):
1105
+ return 1
1106
+ elif flag_string == '--flagfile':
1107
+ return 1
1108
+ elif flag_string.startswith('-flagfile='):
1109
+ return 1
1110
+ elif flag_string == '-flagfile':
1111
+ return 1
1112
+ else:
1113
+ return 0
1114
+ return 0
1115
+
1116
+ def _extract_filename(self, flagfile_str):
1117
+ """Returns filename from a flagfile_str of form -[-]flagfile=filename.
1118
+
1119
+ The cases of --flagfile foo and -flagfile foo shouldn't be hitting
1120
+ this function, as they are dealt with in the level above this
1121
+ function.
1122
+
1123
+ Args:
1124
+ flagfile_str: str, the flagfile string.
1125
+
1126
+ Returns:
1127
+ str, the filename from a flagfile_str of form -[-]flagfile=filename.
1128
+
1129
+ Raises:
1130
+ Error: Raised when illegal --flagfile is provided.
1131
+ """
1132
+ if flagfile_str.startswith('--flagfile='):
1133
+ return os.path.expanduser((flagfile_str[(len('--flagfile=')) :]).strip())
1134
+ elif flagfile_str.startswith('-flagfile='):
1135
+ return os.path.expanduser((flagfile_str[(len('-flagfile=')) :]).strip())
1136
+ else:
1137
+ raise _exceptions.Error('Hit illegal --flagfile type: %s' % flagfile_str)
1138
+
1139
+ def _get_flag_file_lines(self, filename, parsed_file_stack=None):
1140
+ """Returns the useful (!=comments, etc) lines from a file with flags.
1141
+
1142
+ Args:
1143
+ filename: str, the name of the flag file.
1144
+ parsed_file_stack: [str], a list of the names of the files that we have
1145
+ recursively encountered at the current depth. MUTATED BY THIS FUNCTION
1146
+ (but the original value is preserved upon successfully returning from
1147
+ function call).
1148
+
1149
+ Returns:
1150
+ List of strings. See the note below.
1151
+
1152
+ NOTE(springer): This function checks for a nested --flagfile=<foo>
1153
+ tag and handles the lower file recursively. It returns a list of
1154
+ all the lines that _could_ contain command flags. This is
1155
+ EVERYTHING except whitespace lines and comments (lines starting
1156
+ with '#' or '//').
1157
+ """
1158
+ # For consistency with the cpp version, ignore empty values.
1159
+ if not filename:
1160
+ return []
1161
+ if parsed_file_stack is None:
1162
+ parsed_file_stack = []
1163
+ # We do a little safety check for reparsing a file we've already encountered
1164
+ # at a previous depth.
1165
+ if filename in parsed_file_stack:
1166
+ sys.stderr.write(
1167
+ 'Warning: Hit circular flagfile dependency. Ignoring flagfile: %s\n'
1168
+ % (filename,)
1169
+ )
1170
+ return []
1171
+ else:
1172
+ parsed_file_stack.append(filename)
1173
+
1174
+ line_list = [] # All line from flagfile.
1175
+ flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags.
1176
+ try:
1177
+ file_obj = open(filename)
1178
+ except OSError as e_msg:
1179
+ raise _exceptions.CantOpenFlagFileError(
1180
+ 'ERROR:: Unable to open flagfile: %s' % e_msg
1181
+ )
1182
+
1183
+ with file_obj:
1184
+ line_list = file_obj.readlines()
1185
+
1186
+ # This is where we check each line in the file we just read.
1187
+ for line in line_list:
1188
+ if line.isspace():
1189
+ pass
1190
+ # Checks for comment (a line that starts with '#').
1191
+ elif line.startswith('#') or line.startswith('//'):
1192
+ pass
1193
+ # Checks for a nested "--flagfile=<bar>" flag in the current file.
1194
+ # If we find one, recursively parse down into that file.
1195
+ elif self._is_flag_file_directive(line):
1196
+ sub_filename = self._extract_filename(line)
1197
+ included_flags = self._get_flag_file_lines(
1198
+ sub_filename, parsed_file_stack=parsed_file_stack
1199
+ )
1200
+ flag_line_list.extend(included_flags)
1201
+ else:
1202
+ # Any line that's not a comment or a nested flagfile should get
1203
+ # copied into 2nd position. This leaves earlier arguments
1204
+ # further back in the list, thus giving them higher priority.
1205
+ flag_line_list.append(line.strip())
1206
+
1207
+ parsed_file_stack.pop()
1208
+ return flag_line_list
1209
+
1210
+ def read_flags_from_files(
1211
+ self, argv: Sequence[str], force_gnu: bool = True
1212
+ ) -> list[str]:
1213
+ """Processes command line args, but also allow args to be read from file.
1214
+
1215
+ Args:
1216
+ argv: [str], a list of strings, usually sys.argv[1:], which may contain
1217
+ one or more flagfile directives of the form --flagfile="./filename".
1218
+ Note that the name of the program (sys.argv[0]) should be omitted.
1219
+ force_gnu: bool, if False, --flagfile parsing obeys the
1220
+ FLAGS.is_gnu_getopt() value. If True, ignore the value and always follow
1221
+ gnu_getopt semantics.
1222
+
1223
+ Returns:
1224
+ A new list which has the original list combined with what we read
1225
+ from any flagfile(s).
1226
+
1227
+ Raises:
1228
+ IllegalFlagValueError: Raised when --flagfile is provided with no
1229
+ argument.
1230
+
1231
+ This function is called by FLAGS(argv).
1232
+ It scans the input list for a flag that looks like:
1233
+ --flagfile=<somefile>. Then it opens <somefile>, reads all valid key
1234
+ and value pairs and inserts them into the input list in exactly the
1235
+ place where the --flagfile arg is found.
1236
+
1237
+ Note that your application's flags are still defined the usual way
1238
+ using absl.flags DEFINE_flag() type functions.
1239
+
1240
+ Notes (assuming we're getting a commandline of some sort as our input):
1241
+
1242
+ * For duplicate flags, the last one we hit should "win".
1243
+ * Since flags that appear later win, a flagfile's settings can be "weak"
1244
+ if the --flagfile comes at the beginning of the argument sequence,
1245
+ and it can be "strong" if the --flagfile comes at the end.
1246
+ * A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
1247
+ It will be expanded in exactly the spot where it is found.
1248
+ * In a flagfile, a line beginning with # or // is a comment.
1249
+ * Entirely blank lines _should_ be ignored.
1250
+ """
1251
+ rest_of_args = argv
1252
+ new_argv = []
1253
+ while rest_of_args:
1254
+ current_arg = rest_of_args[0]
1255
+ rest_of_args = rest_of_args[1:]
1256
+ if self._is_flag_file_directive(current_arg):
1257
+ # This handles the case of -(-)flagfile foo. In this case the
1258
+ # next arg really is part of this one.
1259
+ if current_arg == '--flagfile' or current_arg == '-flagfile':
1260
+ if not rest_of_args:
1261
+ raise _exceptions.IllegalFlagValueError(
1262
+ '--flagfile with no argument'
1263
+ )
1264
+ flag_filename = os.path.expanduser(rest_of_args[0])
1265
+ rest_of_args = rest_of_args[1:]
1266
+ else:
1267
+ # This handles the case of (-)-flagfile=foo.
1268
+ flag_filename = self._extract_filename(current_arg)
1269
+ new_argv.extend(self._get_flag_file_lines(flag_filename))
1270
+ else:
1271
+ new_argv.append(current_arg)
1272
+ # Stop parsing after '--', like getopt and gnu_getopt.
1273
+ if current_arg == '--':
1274
+ break
1275
+ # Stop parsing after a non-flag, like getopt.
1276
+ if not current_arg.startswith('-'):
1277
+ if not force_gnu and not self.__dict__['__use_gnu_getopt']:
1278
+ break
1279
+ else:
1280
+ if (
1281
+ '=' not in current_arg
1282
+ and rest_of_args
1283
+ and not rest_of_args[0].startswith('-')
1284
+ ):
1285
+ # If this is an occurrence of a legitimate --x y, skip the value
1286
+ # so that it won't be mistaken for a standalone arg.
1287
+ fl = self._flags()
1288
+ name = current_arg.lstrip('-')
1289
+ if name in fl and not fl[name].boolean:
1290
+ current_arg = rest_of_args[0]
1291
+ rest_of_args = rest_of_args[1:]
1292
+ new_argv.append(current_arg)
1293
+
1294
+ if rest_of_args:
1295
+ new_argv.extend(rest_of_args)
1296
+
1297
+ return new_argv
1298
+
1299
+ def flags_into_string(self) -> str:
1300
+ """Returns a string with the flags assignments from this FlagValues object.
1301
+
1302
+ This function ignores flags whose value is None. Each flag
1303
+ assignment is separated by a newline.
1304
+
1305
+ NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString
1306
+ from https://github.com/gflags/gflags.
1307
+
1308
+ Returns:
1309
+ str, the string with the flags assignments from this FlagValues object.
1310
+ The flags are ordered by (module_name, flag_name).
1311
+ """
1312
+ module_flags = sorted(self.flags_by_module_dict().items())
1313
+ s = ''
1314
+ for unused_module_name, flags in module_flags:
1315
+ flags = sorted(flags, key=lambda f: f.name)
1316
+ for flag in flags:
1317
+ if flag.value is not None:
1318
+ s += flag.serialize() + '\n'
1319
+ return s
1320
+
1321
+ def append_flags_into_file(self, filename: str) -> None:
1322
+ """Appends all flags assignments from this FlagInfo object to a file.
1323
+
1324
+ Output will be in the format of a flagfile.
1325
+
1326
+ NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile
1327
+ from https://github.com/gflags/gflags.
1328
+
1329
+ Args:
1330
+ filename: str, name of the file.
1331
+ """
1332
+ with open(filename, 'a') as out_file:
1333
+ out_file.write(self.flags_into_string())
1334
+
1335
+ def write_help_in_xml_format(self, outfile: TextIO | None = None) -> None:
1336
+ """Outputs flag documentation in XML format.
1337
+
1338
+ NOTE: We use element names that are consistent with those used by
1339
+ the C++ command-line flag library, from
1340
+ https://github.com/gflags/gflags.
1341
+ We also use a few new elements (e.g., <key>), but we do not
1342
+ interfere / overlap with existing XML elements used by the C++
1343
+ library. Please maintain this consistency.
1344
+
1345
+ Args:
1346
+ outfile: File object we write to. Default None means sys.stdout.
1347
+ """
1348
+ doc = minidom.Document()
1349
+ all_flag = doc.createElement('AllFlags')
1350
+ doc.appendChild(all_flag)
1351
+
1352
+ all_flag.appendChild(
1353
+ _helpers.create_xml_dom_element(
1354
+ doc, 'program', os.path.basename(sys.argv[0])
1355
+ )
1356
+ )
1357
+
1358
+ usage_doc = sys.modules['__main__'].__doc__
1359
+ if not usage_doc:
1360
+ usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
1361
+ else:
1362
+ usage_doc = usage_doc.replace('%s', sys.argv[0])
1363
+ all_flag.appendChild(
1364
+ _helpers.create_xml_dom_element(doc, 'usage', usage_doc)
1365
+ )
1366
+
1367
+ # Get list of key flags for the main module.
1368
+ key_flags = self.get_key_flags_for_module(sys.argv[0])
1369
+
1370
+ flags_by_module = self.flags_by_module_dict()
1371
+ # Sort flags by declaring module name and next by flag name.
1372
+ for module_name in sorted(flags_by_module.keys()):
1373
+ flag_list = [(f.name, f) for f in flags_by_module[module_name]]
1374
+ flag_list.sort()
1375
+ for unused_flag_name, flag in flag_list:
1376
+ is_key = flag in key_flags
1377
+ all_flag.appendChild(
1378
+ flag._create_xml_dom_element( # pylint: disable=protected-access
1379
+ doc, module_name, is_key=is_key
1380
+ )
1381
+ )
1382
+
1383
+ outfile = outfile or sys.stdout
1384
+ outfile.write(
1385
+ doc.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')
1386
+ )
1387
+ outfile.flush()
1388
+
1389
+ def _check_method_name_conflicts(self, name: str, flag: Flag):
1390
+ if flag.allow_using_method_names:
1391
+ return
1392
+ short_name = flag.short_name
1393
+ flag_names = {name} if short_name is None else {name, short_name}
1394
+ for flag_name in flag_names:
1395
+ if flag_name in self.__dict__['__banned_flag_names']:
1396
+ raise _exceptions.FlagNameConflictsWithMethodError(
1397
+ 'Cannot define a flag named "{name}". It conflicts with a method '
1398
+ 'on class "{class_name}". To allow defining it, use '
1399
+ 'allow_using_method_names and access the flag value with '
1400
+ "FLAGS['{name}'].value. FLAGS.{name} returns the method, "
1401
+ 'not the flag value.'.format(
1402
+ name=flag_name, class_name=type(self).__name__
1403
+ )
1404
+ )
1405
+
1406
+
1407
+ FLAGS = FlagValues()
1408
+
1409
+
1410
+ class FlagHolder(Generic[_T_co]):
1411
+ """Holds a defined flag.
1412
+
1413
+ This facilitates a cleaner api around global state. Instead of::
1414
+
1415
+ flags.DEFINE_integer('foo', ...)
1416
+ flags.DEFINE_integer('bar', ...)
1417
+
1418
+ def method():
1419
+ # prints parsed value of 'bar' flag
1420
+ print(flags.FLAGS.foo)
1421
+ # runtime error due to typo or possibly bad coding style.
1422
+ print(flags.FLAGS.baz)
1423
+
1424
+ it encourages code like::
1425
+
1426
+ _FOO_FLAG = flags.DEFINE_integer('foo', ...)
1427
+ _BAR_FLAG = flags.DEFINE_integer('bar', ...)
1428
+
1429
+ def method():
1430
+ print(_FOO_FLAG.value)
1431
+ print(_BAR_FLAG.value)
1432
+
1433
+ since the name of the flag appears only once in the source code.
1434
+ """
1435
+
1436
+ value: _T_co
1437
+
1438
+ def __init__(
1439
+ self,
1440
+ flag_values: FlagValues,
1441
+ flag: Flag[_T_co],
1442
+ ensure_non_none_value: bool = False,
1443
+ ):
1444
+ """Constructs a FlagHolder instance providing typesafe access to flag.
1445
+
1446
+ Args:
1447
+ flag_values: The container the flag is registered to.
1448
+ flag: The flag object for this flag.
1449
+ ensure_non_none_value: Is the value of the flag allowed to be None.
1450
+ """
1451
+ self._flagvalues = flag_values
1452
+ # We take the entire flag object, but only keep the name. Why?
1453
+ # - We want FlagHolder[T] to be generic container
1454
+ # - flag_values contains all flags, so has no reference to T.
1455
+ # - typecheckers don't like to see a generic class where none of the ctor
1456
+ # arguments refer to the generic type.
1457
+ self._name = flag.name
1458
+ # We intentionally do NOT check if the default value is None.
1459
+ # This allows future use of this for "required flags with None default"
1460
+ self._ensure_non_none_value = ensure_non_none_value
1461
+
1462
+ def __eq__(self, other):
1463
+ raise TypeError(
1464
+ "unsupported operand type(s) for ==: '{0}' and '{1}' "
1465
+ "(did you mean to use '{0}.value' instead?)".format(
1466
+ type(self).__name__, type(other).__name__
1467
+ )
1468
+ )
1469
+
1470
+ def __bool__(self):
1471
+ raise TypeError(
1472
+ "bool() not supported for instances of type '{0}' "
1473
+ "(did you mean to use '{0}.value' instead?)".format(type(self).__name__)
1474
+ )
1475
+
1476
+ __nonzero__ = __bool__
1477
+
1478
+ @property
1479
+ def name(self) -> str:
1480
+ return self._name
1481
+
1482
+ @property # type: ignore[no-redef]
1483
+ def value(self) -> _T_co:
1484
+ """Returns the value of the flag.
1485
+
1486
+ If ``_ensure_non_none_value`` is ``True``, then return value is not
1487
+ ``None``.
1488
+
1489
+ Raises:
1490
+ UnparsedFlagAccessError: if flag parsing has not finished.
1491
+ IllegalFlagValueError: if value is None unexpectedly.
1492
+ """
1493
+ val = getattr(self._flagvalues, self._name)
1494
+ if self._ensure_non_none_value and val is None:
1495
+ raise _exceptions.IllegalFlagValueError(
1496
+ 'Unexpected None value for flag %s' % self._name
1497
+ )
1498
+ return val
1499
+
1500
+ @property
1501
+ def default(self) -> _T_co:
1502
+ """Returns the default value of the flag."""
1503
+ return self._flagvalues[self._name].default # type: ignore[return-value]
1504
+
1505
+ @property
1506
+ def present(self) -> bool:
1507
+ """Returns True if the flag was parsed from command-line flags."""
1508
+ return bool(self._flagvalues[self._name].present)
1509
+
1510
+ def serialize(self) -> str:
1511
+ """Returns a serialized representation of the flag."""
1512
+ return self._flagvalues[self._name].serialize()
1513
+
1514
+
1515
+ def resolve_flag_ref(
1516
+ flag_ref: str | FlagHolder, flag_values: FlagValues
1517
+ ) -> tuple[str, FlagValues]:
1518
+ """Helper to validate and resolve a flag reference argument."""
1519
+ if isinstance(flag_ref, FlagHolder):
1520
+ new_flag_values = flag_ref._flagvalues # pylint: disable=protected-access
1521
+ if flag_values != FLAGS and flag_values != new_flag_values:
1522
+ raise ValueError(
1523
+ 'flag_values must not be customized when operating on a FlagHolder'
1524
+ )
1525
+ return flag_ref.name, new_flag_values
1526
+ return flag_ref, flag_values
1527
+
1528
+
1529
+ def resolve_flag_refs(
1530
+ flag_refs: Sequence[str | FlagHolder], flag_values: FlagValues
1531
+ ) -> tuple[list[str], FlagValues]:
1532
+ """Helper to validate and resolve flag reference list arguments."""
1533
+ fv = None
1534
+ names = []
1535
+ for ref in flag_refs:
1536
+ if isinstance(ref, FlagHolder):
1537
+ newfv = ref._flagvalues # pylint: disable=protected-access
1538
+ name = ref.name
1539
+ else:
1540
+ newfv = flag_values
1541
+ name = ref
1542
+ if fv and fv != newfv:
1543
+ raise ValueError(
1544
+ 'multiple FlagValues instances used in invocation. '
1545
+ 'FlagHolders must be registered to the same FlagValues instance as '
1546
+ 'do flag names, if provided.'
1547
+ )
1548
+ fv = newfv
1549
+ names.append(name)
1550
+ if fv is None:
1551
+ raise ValueError('flag_refs argument must not be empty')
1552
+ return names, fv
lib/python3.12/site-packages/absl/flags/_helpers.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from collections.abc import Iterable, Sequence
18
+ import re
19
+ import shutil
20
+ import sys
21
+ import textwrap
22
+ import types
23
+ from typing import Any, NamedTuple
24
+ from xml.dom import minidom
25
+
26
+
27
+ _DEFAULT_HELP_WIDTH = 80 # Default width of help output.
28
+ # Minimal "sane" width of help output. We assume that any value below 40 is
29
+ # unreasonable.
30
+ _MIN_HELP_WIDTH = 40
31
+
32
+ # Define the allowed error rate in an input string to get suggestions.
33
+ #
34
+ # We lean towards a high threshold because we tend to be matching a phrase,
35
+ # and the simple algorithm used here is geared towards correcting word
36
+ # spellings.
37
+ #
38
+ # For manual testing, consider "<command> --list" which produced a large number
39
+ # of spurious suggestions when we used "least_errors > 0.5" instead of
40
+ # "least_erros >= 0.5".
41
+ _SUGGESTION_ERROR_RATE_THRESHOLD = 0.50
42
+
43
+ # Characters that cannot appear or are highly discouraged in an XML 1.0
44
+ # document. (See http://www.w3.org/TR/REC-xml/#charsets or
45
+ # https://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0)
46
+ _ILLEGAL_XML_CHARS_REGEX = re.compile(
47
+ '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]'
48
+ )
49
+
50
+ # This is a set of module ids for the modules that disclaim key flags.
51
+ # This module is explicitly added to this set so that we never consider it to
52
+ # define key flag.
53
+ disclaim_module_ids: set[int] = {id(sys.modules[__name__])}
54
+
55
+
56
+ # Define special flags here so that help may be generated for them.
57
+ # NOTE: Please do NOT use SPECIAL_FLAGS from outside flags module.
58
+ # Initialized inside flagvalues.py.
59
+ # NOTE: This cannot be annotated as its actual FlagValues type since this would
60
+ # create a circular dependency.
61
+ SPECIAL_FLAGS: Any = None
62
+
63
+
64
+ # This points to the flags module, initialized in flags/__init__.py.
65
+ # This should only be used in adopt_module_key_flags to take SPECIAL_FLAGS into
66
+ # account.
67
+ FLAGS_MODULE: types.ModuleType | None = None
68
+
69
+
70
+ class _ModuleObjectAndName(NamedTuple):
71
+ """Module object and name.
72
+
73
+ Fields:
74
+ - module: object, module object.
75
+ - module_name: str, module name.
76
+ """
77
+ module: types.ModuleType
78
+ module_name: str
79
+
80
+
81
+ def get_module_object_and_name(
82
+ globals_dict: dict[str, Any],
83
+ ) -> _ModuleObjectAndName | None:
84
+ """Returns the module that defines a global environment, and its name.
85
+
86
+ Args:
87
+ globals_dict: A dictionary that should correspond to an environment
88
+ providing the values of the globals.
89
+
90
+ Returns:
91
+ _ModuleObjectAndName - pair of module object & module name.
92
+ Returns None if the module could not be identified.
93
+ """
94
+ try:
95
+ name = globals_dict['__name__']
96
+ module = sys.modules[name]
97
+ except KeyError:
98
+ return None
99
+ # Pick a more informative name for the main module.
100
+ return _ModuleObjectAndName(
101
+ module, sys.argv[0] if name == '__main__' else name
102
+ )
103
+
104
+
105
+ def get_calling_module_object_and_name() -> _ModuleObjectAndName:
106
+ """Returns the module that's calling into this module.
107
+
108
+ We generally use this function to get the name of the module calling a
109
+ DEFINE_foo... function.
110
+
111
+ Returns:
112
+ The module object that called into this one.
113
+
114
+ Raises:
115
+ AssertionError: Raised when no calling module could be identified.
116
+ """
117
+ for depth in range(1, sys.getrecursionlimit()):
118
+ # sys._getframe is the right thing to use here, as it's the best
119
+ # way to walk up the call stack.
120
+ globals_for_frame = sys._getframe(depth).f_globals # pylint: disable=protected-access
121
+ module = get_module_object_and_name(globals_for_frame)
122
+ if module is not None and id(module.module) not in disclaim_module_ids:
123
+ return module
124
+ raise AssertionError('No module was found')
125
+
126
+
127
+ def get_calling_module() -> str:
128
+ """Returns the name of the module that's calling into this module."""
129
+ return get_calling_module_object_and_name().module_name
130
+
131
+
132
+ def create_xml_dom_element(
133
+ doc: minidom.Document, name: str, value: Any
134
+ ) -> minidom.Element:
135
+ """Returns an XML DOM element with name and text value.
136
+
137
+ Args:
138
+ doc: minidom.Document, the DOM document it should create nodes from.
139
+ name: str, the tag of XML element.
140
+ value: object, whose string representation will be used
141
+ as the value of the XML element. Illegal or highly discouraged xml 1.0
142
+ characters are stripped.
143
+
144
+ Returns:
145
+ An instance of minidom.Element.
146
+ """
147
+ s = str(value)
148
+ if isinstance(value, bool):
149
+ # Display boolean values as the C++ flag library does: no caps.
150
+ s = s.lower()
151
+ # Remove illegal xml characters.
152
+ s = _ILLEGAL_XML_CHARS_REGEX.sub('', s)
153
+
154
+ e = doc.createElement(name)
155
+ e.appendChild(doc.createTextNode(s))
156
+ return e
157
+
158
+
159
+ def get_help_width() -> int:
160
+ """Returns the integer width of help lines that is used in TextWrap."""
161
+ size = shutil.get_terminal_size(fallback=(_DEFAULT_HELP_WIDTH, 1))
162
+ return size.columns
163
+
164
+
165
+ def get_flag_suggestions(
166
+ attempt: str, longopt_list: Sequence[str]
167
+ ) -> list[str]:
168
+ """Returns helpful similar matches for an invalid flag."""
169
+ # Don't suggest on very short strings, or if no longopts are specified.
170
+ if len(attempt) <= 2 or not longopt_list:
171
+ return []
172
+
173
+ option_names = [v.split('=')[0] for v in longopt_list]
174
+
175
+ # Find close approximations in flag prefixes.
176
+ # This also handles the case where the flag is spelled right but ambiguous.
177
+ distances = [(_damerau_levenshtein(attempt, option[0:len(attempt)]), option)
178
+ for option in option_names]
179
+ # t[0] is distance, and sorting by t[1] allows us to have stable output.
180
+ distances.sort()
181
+
182
+ least_errors, _ = distances[0]
183
+ # Don't suggest excessively bad matches.
184
+ if least_errors >= _SUGGESTION_ERROR_RATE_THRESHOLD * len(attempt):
185
+ return []
186
+
187
+ suggestions = []
188
+ for errors, name in distances:
189
+ if errors == least_errors:
190
+ suggestions.append(name)
191
+ else:
192
+ break
193
+ return suggestions
194
+
195
+
196
+ def _damerau_levenshtein(a, b):
197
+ """Returns Damerau-Levenshtein edit distance from a to b."""
198
+ memo = {}
199
+
200
+ def distance(x, y):
201
+ """Recursively defined string distance with memoization."""
202
+ if (x, y) in memo:
203
+ return memo[x, y]
204
+ if not x:
205
+ d = len(y)
206
+ elif not y:
207
+ d = len(x)
208
+ else:
209
+ d = min(
210
+ distance(x[1:], y) + 1, # correct an insertion error
211
+ distance(x, y[1:]) + 1, # correct a deletion error
212
+ distance(x[1:], y[1:]) + (x[0] != y[0])) # correct a wrong character
213
+ if len(x) >= 2 and len(y) >= 2 and x[0] == y[1] and x[1] == y[0]:
214
+ # Correct a transposition.
215
+ t = distance(x[2:], y[2:]) + 1
216
+ if d > t:
217
+ d = t
218
+
219
+ memo[x, y] = d
220
+ return d
221
+ return distance(a, b)
222
+
223
+
224
+ def text_wrap(
225
+ text: str,
226
+ length: int | None = None,
227
+ indent: str = '',
228
+ firstline_indent: str | None = None,
229
+ ) -> str:
230
+ """Wraps a given text to a maximum line length and returns it.
231
+
232
+ It turns lines that only contain whitespace into empty lines, keeps new lines,
233
+ and expands tabs using 4 spaces.
234
+
235
+ Args:
236
+ text: Text to wrap.
237
+ length: Maximum length of a line, includes indentation. If this is `None`
238
+ then use `get_help_width()`.
239
+ indent: Indent for all but first line.
240
+ firstline_indent: Indent for first line. If `None`, fall back to `indent`.
241
+
242
+ Returns:
243
+ The wrapped text.
244
+
245
+ Raises:
246
+ ValueError: Raised if indent or firstline_indent not shorter than length.
247
+ """
248
+ # Get defaults where callee used None
249
+ if length is None:
250
+ length = get_help_width()
251
+ if indent is None:
252
+ indent = ''
253
+ if firstline_indent is None:
254
+ firstline_indent = indent
255
+
256
+ if len(indent) >= length:
257
+ raise ValueError('Length of indent exceeds length')
258
+ if len(firstline_indent) >= length:
259
+ raise ValueError('Length of first line indent exceeds length')
260
+
261
+ text = text.expandtabs(4)
262
+
263
+ result = []
264
+ # Create one wrapper for the first paragraph and one for subsequent
265
+ # paragraphs that does not have the initial wrapping.
266
+ wrapper = textwrap.TextWrapper(
267
+ width=length, initial_indent=firstline_indent, subsequent_indent=indent)
268
+ subsequent_wrapper = textwrap.TextWrapper(
269
+ width=length, initial_indent=indent, subsequent_indent=indent)
270
+
271
+ # textwrap does not have any special treatment for newlines. From the docs:
272
+ # "...newlines may appear in the middle of a line and cause strange output.
273
+ # For this reason, text should be split into paragraphs (using
274
+ # str.splitlines() or similar) which are wrapped separately."
275
+ for paragraph in (p.strip() for p in text.splitlines()):
276
+ if paragraph:
277
+ result.extend(wrapper.wrap(paragraph))
278
+ else:
279
+ result.append('') # Keep empty lines.
280
+ # Replace initial wrapper with wrapper for subsequent paragraphs.
281
+ wrapper = subsequent_wrapper
282
+
283
+ return '\n'.join(result)
284
+
285
+
286
+ def flag_dict_to_args(
287
+ flag_map: dict[str, Any], multi_flags: set[str] | None = None
288
+ ) -> Iterable[str]:
289
+ """Convert a dict of values into process call parameters.
290
+
291
+ This method is used to convert a dictionary into a sequence of parameters
292
+ for a binary that parses arguments using this module.
293
+
294
+ Args:
295
+ flag_map: dict, a mapping where the keys are flag names (strings).
296
+ values are treated according to their type:
297
+
298
+ * If value is ``None``, then only the name is emitted.
299
+ * If value is ``True``, then only the name is emitted.
300
+ * If value is ``False``, then only the name prepended with 'no' is
301
+ emitted.
302
+ * If value is a string then ``--name=value`` is emitted.
303
+ * If value is a collection, this will emit
304
+ ``--name=value1,value2,value3``, unless the flag name is in
305
+ ``multi_flags``, in which case this will emit
306
+ ``--name=value1 --name=value2 --name=value3``.
307
+ * Everything else is converted to string an passed as such.
308
+
309
+ multi_flags: set, names (strings) of flags that should be treated as
310
+ multi-flags.
311
+ Yields:
312
+ sequence of string suitable for a subprocess execution.
313
+ """
314
+ for key, value in flag_map.items():
315
+ if value is None:
316
+ yield '--%s' % key
317
+ elif isinstance(value, bool):
318
+ if value:
319
+ yield '--%s' % key
320
+ else:
321
+ yield '--no%s' % key
322
+ elif isinstance(value, (bytes, str)):
323
+ # We don't want strings to be handled like python collections.
324
+ yield '--%s=%s' % (key, value) # type: ignore[str-bytes-safe]
325
+ else:
326
+ # Now we attempt to deal with collections.
327
+ try:
328
+ if multi_flags and key in multi_flags:
329
+ for item in value:
330
+ yield '--%s=%s' % (key, str(item))
331
+ else:
332
+ yield '--%s=%s' % (key, ','.join(str(item) for item in value))
333
+ except TypeError:
334
+ # Default case.
335
+ yield '--%s=%s' % (key, value)
336
+
337
+
338
+ def trim_docstring(docstring: str) -> str:
339
+ """Removes indentation from triple-quoted strings.
340
+
341
+ This is the function specified in PEP 257 to handle docstrings:
342
+ https://www.python.org/dev/peps/pep-0257/.
343
+
344
+ Args:
345
+ docstring: str, a python docstring.
346
+
347
+ Returns:
348
+ str, docstring with indentation removed.
349
+ """
350
+ if not docstring:
351
+ return ''
352
+
353
+ # If you've got a line longer than this you have other problems...
354
+ max_indent = 1 << 29
355
+
356
+ # Convert tabs to spaces (following the normal Python rules)
357
+ # and split into a list of lines:
358
+ lines = docstring.expandtabs().splitlines()
359
+
360
+ # Determine minimum indentation (first line doesn't count):
361
+ indent = max_indent
362
+ for line in lines[1:]:
363
+ stripped = line.lstrip()
364
+ if stripped:
365
+ indent = min(indent, len(line) - len(stripped))
366
+ # Remove indentation (first line is special):
367
+ trimmed = [lines[0].strip()]
368
+ if indent < max_indent:
369
+ for line in lines[1:]:
370
+ trimmed.append(line[indent:].rstrip())
371
+ # Strip off trailing and leading blank lines:
372
+ while trimmed and not trimmed[-1]:
373
+ trimmed.pop()
374
+ while trimmed and not trimmed[0]:
375
+ trimmed.pop(0)
376
+ # Return a single string:
377
+ return '\n'.join(trimmed)
378
+
379
+
380
+ def doc_to_help(doc: str) -> str:
381
+ """Takes a __doc__ string and reformats it as help."""
382
+
383
+ # Get rid of starting and ending white space. Using lstrip() or even
384
+ # strip() could drop more than maximum of first line and right space
385
+ # of last line.
386
+ doc = doc.strip()
387
+
388
+ # Get rid of all empty lines.
389
+ whitespace_only_line = re.compile('^[ \t]+$', re.M)
390
+ doc = whitespace_only_line.sub('', doc)
391
+
392
+ # Cut out common space at line beginnings.
393
+ doc = trim_docstring(doc)
394
+
395
+ # Just like this module's comment, comments tend to be aligned somehow.
396
+ # In other words they all start with the same amount of white space.
397
+ # 1) keep double new lines;
398
+ # 2) keep ws after new lines if not empty line;
399
+ # 3) all other new lines shall be changed to a space;
400
+ # Solution: Match new lines between non white space and replace with space.
401
+ doc = re.sub(r'(?<=\S)\n(?=\S)', ' ', doc, flags=re.M)
402
+
403
+ return doc
lib/python3.12/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.12/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.12/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.12/site-packages/absl/logging/__init__.py ADDED
@@ -0,0 +1,1335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.abc import Mapping
81
+ import getpass
82
+ import inspect
83
+ import io
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 warnings
96
+
97
+ from absl import flags
98
+ from absl.logging import converter
99
+
100
+ # pylint: disable=g-import-not-at-top
101
+ try:
102
+ from typing import NoReturn
103
+ except ImportError:
104
+ pass
105
+
106
+ # pylint: enable=g-import-not-at-top
107
+
108
+ FLAGS = flags.FLAGS
109
+
110
+
111
+ # Logging levels.
112
+ FATAL = converter.ABSL_FATAL
113
+ ERROR = converter.ABSL_ERROR
114
+ WARNING = converter.ABSL_WARNING
115
+ WARN = converter.ABSL_WARNING # Deprecated name.
116
+ INFO = converter.ABSL_INFO
117
+ DEBUG = converter.ABSL_DEBUG
118
+
119
+ # Regex to match/parse log line prefixes.
120
+ ABSL_LOGGING_PREFIX_REGEX = (
121
+ r'^(?P<severity>[IWEF])'
122
+ r'(?P<month>\d\d)(?P<day>\d\d) '
123
+ r'(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)'
124
+ r'\.(?P<microsecond>\d\d\d\d\d\d) +'
125
+ r'(?P<thread_id>-?\d+) '
126
+ r'(?P<filename>[a-zA-Z<][\w._<>-]+):(?P<line>\d+)')
127
+
128
+
129
+ # Mask to convert integer thread ids to unsigned quantities for logging purposes
130
+ _THREAD_ID_MASK = 2 ** (struct.calcsize('L') * 8) - 1
131
+
132
+ # Extra property set on the LogRecord created by ABSLLogger when its level is
133
+ # CRITICAL/FATAL.
134
+ _ABSL_LOG_FATAL = '_absl_log_fatal'
135
+ # Extra prefix added to the log message when a non-absl logger logs a
136
+ # CRITICAL/FATAL message.
137
+ _CRITICAL_PREFIX = 'CRITICAL - '
138
+
139
+ # Used by findCaller to skip callers from */logging/__init__.py.
140
+ _LOGGING_FILE_PREFIX = os.path.join('logging', '__init__.')
141
+
142
+ # The ABSL logger instance, initialized in _initialize().
143
+ _absl_logger = None
144
+ # The ABSL handler instance, initialized in _initialize().
145
+ _absl_handler = None
146
+
147
+
148
+ _CPP_NAME_TO_LEVELS = {
149
+ 'debug': '0', # Abseil C++ has no DEBUG level, mapping it to INFO here.
150
+ 'info': '0',
151
+ 'warning': '1',
152
+ 'warn': '1',
153
+ 'error': '2',
154
+ 'fatal': '3'
155
+ }
156
+
157
+ _CPP_LEVEL_TO_NAMES = {
158
+ '0': 'info',
159
+ '1': 'warning',
160
+ '2': 'error',
161
+ '3': 'fatal',
162
+ }
163
+
164
+
165
+ class _VerbosityFlag(flags.Flag):
166
+ """Flag class for -v/--verbosity."""
167
+
168
+ def __init__(self, *args, **kwargs):
169
+ super().__init__(
170
+ flags.IntegerParser(), flags.ArgumentSerializer(), *args, **kwargs
171
+ )
172
+
173
+ @property
174
+ def value(self):
175
+ return self._value
176
+
177
+ @value.setter
178
+ def value(self, v):
179
+ self._value = v
180
+ self._update_logging_levels()
181
+
182
+ def _update_logging_levels(self):
183
+ """Updates absl logging levels to the current verbosity.
184
+
185
+ Visibility: module-private
186
+ """
187
+ if not _absl_logger:
188
+ return
189
+
190
+ if self._value <= converter.ABSL_DEBUG:
191
+ standard_verbosity = converter.absl_to_standard(self._value)
192
+ else:
193
+ # --verbosity is set to higher than 1 for vlog.
194
+ standard_verbosity = logging.DEBUG - (self._value - 1)
195
+
196
+ # Also update root level when absl_handler is used.
197
+ if _absl_handler in logging.root.handlers:
198
+ # Make absl logger inherit from the root logger. absl logger might have
199
+ # a non-NOTSET value if logging.set_verbosity() is called at import time.
200
+ _absl_logger.setLevel(logging.NOTSET)
201
+ logging.root.setLevel(standard_verbosity)
202
+ else:
203
+ _absl_logger.setLevel(standard_verbosity)
204
+
205
+
206
+ class _LoggerLevelsFlag(flags.Flag):
207
+ """Flag class for --logger_levels."""
208
+
209
+ def __init__(self, *args, **kwargs):
210
+ super().__init__(
211
+ _LoggerLevelsParser(), _LoggerLevelsSerializer(), *args, **kwargs
212
+ )
213
+
214
+ @property
215
+ def value(self):
216
+ # For lack of an immutable type, be defensive and return a copy.
217
+ # Modifications to the dict aren't supported and won't have any affect.
218
+ # While Py3 could use MappingProxyType, that isn't deepcopy friendly, so
219
+ # just return a copy.
220
+ return self._value.copy()
221
+
222
+ @value.setter
223
+ def value(self, v):
224
+ self._value = {} if v is None else v
225
+ self._update_logger_levels()
226
+
227
+ def _update_logger_levels(self):
228
+ # Visibility: module-private.
229
+ # This is called by absl.app.run() during initialization.
230
+ for name, level in self._value.items():
231
+ logging.getLogger(name).setLevel(level)
232
+
233
+
234
+ class _LoggerLevelsParser(flags.ArgumentParser):
235
+ """Parser for --logger_levels flag."""
236
+
237
+ def parse(self, value):
238
+ if isinstance(value, Mapping):
239
+ return value
240
+
241
+ pairs = [pair.strip() for pair in value.split(',') if pair.strip()]
242
+
243
+ # Preserve the order so that serialization is deterministic.
244
+ levels = collections.OrderedDict()
245
+ for name_level in pairs:
246
+ name, level = name_level.split(':', 1)
247
+ name = name.strip()
248
+ level = level.strip()
249
+ levels[name] = level
250
+ return levels
251
+
252
+
253
+ class _LoggerLevelsSerializer:
254
+ """Serializer for --logger_levels flag."""
255
+
256
+ def serialize(self, value):
257
+ if isinstance(value, str):
258
+ return value
259
+ return ','.join(f'{name}:{level}' for name, level in value.items())
260
+
261
+
262
+ class _StderrthresholdFlag(flags.Flag):
263
+ """Flag class for --stderrthreshold."""
264
+
265
+ def __init__(self, *args, **kwargs):
266
+ super().__init__(
267
+ flags.ArgumentParser(), flags.ArgumentSerializer(), *args, **kwargs
268
+ )
269
+
270
+ @property
271
+ def value(self):
272
+ return self._value
273
+
274
+ @value.setter
275
+ def value(self, v):
276
+ if v in _CPP_LEVEL_TO_NAMES:
277
+ # --stderrthreshold also accepts numeric strings whose values are
278
+ # Abseil C++ log levels.
279
+ cpp_value = int(v)
280
+ v = _CPP_LEVEL_TO_NAMES[v] # Normalize to strings.
281
+ elif v.lower() in _CPP_NAME_TO_LEVELS:
282
+ v = v.lower()
283
+ if v == 'warn':
284
+ v = 'warning' # Use 'warning' as the canonical name.
285
+ cpp_value = int(_CPP_NAME_TO_LEVELS[v])
286
+ else:
287
+ raise ValueError(
288
+ '--stderrthreshold must be one of (case-insensitive) '
289
+ "'debug', 'info', 'warning', 'error', 'fatal', "
290
+ "or '0', '1', '2', '3', not '%s'" % v)
291
+
292
+ self._value = v
293
+
294
+
295
+ LOGTOSTDERR = flags.DEFINE_boolean(
296
+ 'logtostderr',
297
+ False,
298
+ 'Should only log to stderr?',
299
+ allow_override_cpp=True,
300
+ )
301
+ ALSOLOGTOSTDERR = flags.DEFINE_boolean(
302
+ 'alsologtostderr',
303
+ False,
304
+ 'also log to stderr?',
305
+ allow_override_cpp=True,
306
+ )
307
+ LOG_DIR = flags.DEFINE_string(
308
+ 'log_dir',
309
+ os.getenv('TEST_TMPDIR', ''),
310
+ 'directory to write logfiles into',
311
+ allow_override_cpp=True,
312
+ )
313
+ VERBOSITY = flags.DEFINE_flag(
314
+ _VerbosityFlag(
315
+ 'verbosity',
316
+ -1,
317
+ (
318
+ 'Logging verbosity level. Messages logged at this level or lower'
319
+ ' will be included. Set to 1 for debug logging. If the flag was not'
320
+ ' set or supplied, the value will be changed from the default of -1'
321
+ ' (warning) to 0 (info) after flags are parsed.'
322
+ ),
323
+ short_name='v',
324
+ allow_hide_cpp=True,
325
+ )
326
+ )
327
+ LOGGER_LEVELS = flags.DEFINE_flag(
328
+ _LoggerLevelsFlag(
329
+ 'logger_levels',
330
+ {},
331
+ (
332
+ 'Specify log level of loggers. The format is a CSV list of '
333
+ '`name:level`. Where `name` is the logger name used with '
334
+ '`logging.getLogger()`, and `level` is a level name (INFO, DEBUG, '
335
+ 'etc). e.g. `myapp.foo:INFO,other.logger:DEBUG`'
336
+ ),
337
+ )
338
+ )
339
+ STDERRTHRESHOLD = flags.DEFINE_flag(
340
+ _StderrthresholdFlag(
341
+ 'stderrthreshold',
342
+ 'fatal',
343
+ (
344
+ 'log messages at this level, or more severe, to stderr in '
345
+ 'addition to the logfile. Possible values are '
346
+ "'debug', 'info', 'warning', 'error', and 'fatal'. "
347
+ 'Obsoletes --alsologtostderr. Using --alsologtostderr '
348
+ 'cancels the effect of this flag. Please also note that '
349
+ 'this flag is subject to --verbosity and requires logfile '
350
+ 'not be stderr.'
351
+ ),
352
+ allow_hide_cpp=True,
353
+ )
354
+ )
355
+ SHOWPREFIXFORINFO = flags.DEFINE_boolean(
356
+ 'showprefixforinfo',
357
+ True,
358
+ (
359
+ 'If False, do not prepend prefix to info messages '
360
+ "when it's logged to stderr, "
361
+ '--verbosity is set to INFO level, '
362
+ 'and python logging is used.'
363
+ ),
364
+ )
365
+
366
+
367
+ def get_verbosity():
368
+ """Returns the logging verbosity."""
369
+ return FLAGS['verbosity'].value
370
+
371
+
372
+ def set_verbosity(v):
373
+ """Sets the logging verbosity.
374
+
375
+ Causes all messages of level <= v to be logged,
376
+ and all messages of level > v to be silently discarded.
377
+
378
+ Args:
379
+ v: int|str, the verbosity level as an integer or string. Legal string values
380
+ are those that can be coerced to an integer as well as case-insensitive
381
+ 'debug', 'info', 'warning', 'error', and 'fatal'.
382
+ """
383
+ try:
384
+ new_level = int(v)
385
+ except ValueError:
386
+ new_level = converter.ABSL_NAMES[v.upper()]
387
+ FLAGS.verbosity = new_level
388
+
389
+
390
+ def set_stderrthreshold(s):
391
+ """Sets the stderr threshold to the value passed in.
392
+
393
+ Args:
394
+ s: str|int, valid strings values are case-insensitive 'debug',
395
+ 'info', 'warning', 'error', and 'fatal'; valid integer values are
396
+ logging.DEBUG|INFO|WARNING|ERROR|FATAL.
397
+
398
+ Raises:
399
+ ValueError: Raised when s is an invalid value.
400
+ """
401
+ if s in converter.ABSL_LEVELS:
402
+ FLAGS.stderrthreshold = converter.ABSL_LEVELS[s]
403
+ elif isinstance(s, str) and s.upper() in converter.ABSL_NAMES:
404
+ FLAGS.stderrthreshold = s
405
+ else:
406
+ raise ValueError(
407
+ 'set_stderrthreshold only accepts integer absl logging level '
408
+ 'from -3 to 1, or case-insensitive string values '
409
+ "'debug', 'info', 'warning', 'error', and 'fatal'. "
410
+ 'But found "{}" ({}).'.format(s, type(s)))
411
+
412
+
413
+ def fatal(msg, *args, **kwargs):
414
+ # type: (Any, Any, Any) -> NoReturn
415
+ """Logs a fatal message."""
416
+ log(FATAL, msg, *args, **kwargs)
417
+
418
+
419
+ def error(msg, *args, **kwargs):
420
+ """Logs an error message."""
421
+ log(ERROR, msg, *args, **kwargs)
422
+
423
+
424
+ def warning(msg, *args, **kwargs):
425
+ """Logs a warning message."""
426
+ log(WARNING, msg, *args, **kwargs)
427
+
428
+
429
+ def warn(msg, *args, **kwargs):
430
+ """Deprecated, use 'warning' instead."""
431
+ warnings.warn("The 'warn' function is deprecated, use 'warning' instead",
432
+ DeprecationWarning, 2)
433
+ log(WARNING, msg, *args, **kwargs)
434
+
435
+
436
+ def info(msg, *args, **kwargs):
437
+ """Logs an info message."""
438
+ log(INFO, msg, *args, **kwargs)
439
+
440
+
441
+ def debug(msg, *args, **kwargs):
442
+ """Logs a debug message."""
443
+ log(DEBUG, msg, *args, **kwargs)
444
+
445
+
446
+ def exception(msg, *args, exc_info=True, **kwargs):
447
+ """Logs an exception, with traceback and message."""
448
+ error(msg, *args, exc_info=exc_info, **kwargs)
449
+
450
+
451
+ def _fast_stack_trace():
452
+ """A fast stack trace that gets us the minimal information we need.
453
+
454
+ Compared to using `get_absl_logger().findCaller(stack_info=True)`, this
455
+ function is ~100x faster.
456
+
457
+ Returns:
458
+ A tuple of tuples of (filename, line_number, last_instruction_offset).
459
+ """
460
+ cur_stack = inspect.currentframe()
461
+ if cur_stack is None or cur_stack.f_back is None:
462
+ return tuple()
463
+ # We drop the first frame, which is this function itself.
464
+ cur_stack = cur_stack.f_back
465
+ call_stack = []
466
+ while cur_stack.f_back:
467
+ cur_stack = cur_stack.f_back
468
+ call_stack.append(
469
+ (cur_stack.f_code.co_filename, cur_stack.f_lineno, cur_stack.f_lasti)
470
+ )
471
+ return tuple(call_stack)
472
+
473
+
474
+ # Counter to keep track of number of log entries per token.
475
+ _log_counter_per_token = {}
476
+
477
+
478
+ def _get_next_log_count_per_token(token):
479
+ """Wrapper for _log_counter_per_token. Thread-safe.
480
+
481
+ Args:
482
+ token: The token for which to look up the count.
483
+
484
+ Returns:
485
+ The number of times this function has been called with
486
+ *token* as an argument (starting at 0).
487
+ """
488
+ # Can't use a defaultdict because defaultdict isn't atomic, whereas
489
+ # setdefault is.
490
+ return next(_log_counter_per_token.setdefault(token, itertools.count()))
491
+
492
+
493
+ def log_every_n(level, msg, n, *args, use_call_stack=False, **kwargs):
494
+ """Logs ``msg % args`` at level 'level' once per 'n' times.
495
+
496
+ Logs the 1st call, (N+1)st call, (2N+1)st call, etc.
497
+ Not threadsafe.
498
+
499
+ Args:
500
+ level: int, the absl logging level at which to log.
501
+ msg: str, the message to be logged.
502
+ n: int, the number of times this should be called before it is logged.
503
+ *args: The args to be substituted into the msg.
504
+ use_call_stack: bool, whether to include the call stack when counting the
505
+ number of times the message is logged.
506
+ **kwargs: May contain exc_info to add exception traceback to message.
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, **kwargs)
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(
548
+ level, msg, n_seconds, *args, use_call_stack=False, **kwargs
549
+ ):
550
+ """Logs ``msg % args`` at level ``level`` iff ``n_seconds`` elapsed since last call.
551
+
552
+ Logs the first call, logs subsequent calls if 'n' seconds have elapsed since
553
+ the last logging call from the same call site (file + line). Not thread-safe.
554
+
555
+ Args:
556
+ level: int, the absl logging level at which to log.
557
+ msg: str, the message to be logged.
558
+ n_seconds: float or int, seconds which should elapse before logging again.
559
+ *args: The args to be substituted into the msg.
560
+ use_call_stack: bool, whether to include the call stack when counting the
561
+ number of times the message is logged.
562
+ **kwargs: May contain exc_info to add exception traceback to message.
563
+ """
564
+ caller_info = get_absl_logger().findCaller()
565
+ if use_call_stack:
566
+ # To reduce storage costs, we hash the call stack.
567
+ caller_info = (*caller_info[0:3], hash(_fast_stack_trace()))
568
+ should_log = _seconds_have_elapsed(caller_info, n_seconds)
569
+ log_if(level, msg, should_log, *args, **kwargs)
570
+
571
+
572
+ def log_first_n(level, msg, n, *args, use_call_stack=False, **kwargs):
573
+ """Logs ``msg % args`` at level ``level`` only first ``n`` times.
574
+
575
+ Not threadsafe.
576
+
577
+ Args:
578
+ level: int, the absl logging level at which to log.
579
+ msg: str, the message to be logged.
580
+ n: int, the maximal number of times the message is logged.
581
+ *args: The args to be substituted into the msg.
582
+ use_call_stack: bool, whether to include the call stack when counting the
583
+ number of times the message is logged.
584
+ **kwargs: May contain exc_info to add exception traceback to message.
585
+ """
586
+ caller_info = get_absl_logger().findCaller()
587
+ if use_call_stack:
588
+ # To reduce storage costs, we hash the call stack.
589
+ caller_info = (*caller_info[0:3], hash(_fast_stack_trace()))
590
+ count = _get_next_log_count_per_token(caller_info)
591
+ log_if(level, msg, count < n, *args, **kwargs)
592
+
593
+
594
+ def log_if(level, msg, condition, *args, **kwargs):
595
+ """Logs ``msg % args`` at level ``level`` only if condition is fulfilled."""
596
+ if condition:
597
+ log(level, msg, *args, **kwargs)
598
+
599
+
600
+ def log(level, msg, *args, **kwargs):
601
+ """Logs ``msg % args`` at absl logging level ``level``.
602
+
603
+ If no args are given just print msg, ignoring any interpolation specifiers.
604
+
605
+ Args:
606
+ level: int, the absl logging level at which to log the message
607
+ (logging.DEBUG|INFO|WARNING|ERROR|FATAL). While some C++ verbose logging
608
+ level constants are also supported, callers should prefer explicit
609
+ logging.vlog() calls for such purpose.
610
+
611
+ msg: str, the message to be logged.
612
+ *args: The args to be substituted into the msg.
613
+ **kwargs: May contain exc_info to add exception traceback to message.
614
+ """
615
+ if level > converter.ABSL_DEBUG:
616
+ # Even though this function supports level that is greater than 1, users
617
+ # should use logging.vlog instead for such cases.
618
+ # Treat this as vlog, 1 is equivalent to DEBUG.
619
+ standard_level = converter.STANDARD_DEBUG - (level - 1)
620
+ else:
621
+ if level < converter.ABSL_FATAL:
622
+ level = converter.ABSL_FATAL
623
+ standard_level = converter.absl_to_standard(level)
624
+
625
+ # Match standard logging's behavior. Before use_absl_handler() and
626
+ # logging is configured, there is no handler attached on _absl_logger nor
627
+ # logging.root. So logs go no where.
628
+ if not logging.root.handlers:
629
+ logging.basicConfig()
630
+
631
+ _absl_logger.log(standard_level, msg, *args, **kwargs)
632
+
633
+
634
+ def vlog(level, msg, *args, **kwargs):
635
+ """Log ``msg % args`` at C++ vlog level ``level``.
636
+
637
+ Args:
638
+ level: int, the C++ verbose logging level at which to log the message,
639
+ e.g. 1, 2, 3, 4... While absl level constants are also supported,
640
+ callers should prefer logging.log|debug|info|... calls for such purpose.
641
+ msg: str, the message to be logged.
642
+ *args: The args to be substituted into the msg.
643
+ **kwargs: May contain exc_info to add exception traceback to message.
644
+ """
645
+ log(level, msg, *args, **kwargs)
646
+
647
+
648
+ def vlog_is_on(level):
649
+ """Checks if vlog is enabled for the given level in caller's source file.
650
+
651
+ Args:
652
+ level: int, the C++ verbose logging level at which to log the message,
653
+ e.g. 1, 2, 3, 4... While absl level constants are also supported,
654
+ callers should prefer level_debug|level_info|... calls for
655
+ checking those.
656
+
657
+ Returns:
658
+ True if logging is turned on for that level.
659
+ """
660
+
661
+ if level > converter.ABSL_DEBUG:
662
+ # Even though this function supports level that is greater than 1, users
663
+ # should use logging.vlog instead for such cases.
664
+ # Treat this as vlog, 1 is equivalent to DEBUG.
665
+ standard_level = converter.STANDARD_DEBUG - (level - 1)
666
+ else:
667
+ if level < converter.ABSL_FATAL:
668
+ level = converter.ABSL_FATAL
669
+ standard_level = converter.absl_to_standard(level)
670
+ return _absl_logger.isEnabledFor(standard_level)
671
+
672
+
673
+ def flush():
674
+ """Flushes all log files."""
675
+ get_absl_handler().flush()
676
+
677
+
678
+ def level_debug():
679
+ """Returns True if debug logging is turned on."""
680
+ return get_verbosity() >= DEBUG
681
+
682
+
683
+ def level_info():
684
+ """Returns True if info logging is turned on."""
685
+ return get_verbosity() >= INFO
686
+
687
+
688
+ def level_warning():
689
+ """Returns True if warning logging is turned on."""
690
+ return get_verbosity() >= WARNING
691
+
692
+
693
+ level_warn = level_warning # Deprecated function.
694
+
695
+
696
+ def level_error():
697
+ """Returns True if error logging is turned on."""
698
+ return get_verbosity() >= ERROR
699
+
700
+
701
+ def get_log_file_name(level=INFO):
702
+ """Returns the name of the log file.
703
+
704
+ For Python logging, only one file is used and level is ignored. And it returns
705
+ empty string if it logs to stderr/stdout or the log stream has no `name`
706
+ attribute.
707
+
708
+ Args:
709
+ level: int, the absl.logging level.
710
+
711
+ Raises:
712
+ ValueError: Raised when `level` has an invalid value.
713
+ """
714
+ if level not in converter.ABSL_LEVELS:
715
+ raise ValueError(f'Invalid absl.logging level {level}')
716
+ stream = get_absl_handler().python_handler.stream
717
+ if (stream == sys.stderr or stream == sys.stdout or
718
+ not hasattr(stream, 'name')):
719
+ return ''
720
+ else:
721
+ return stream.name
722
+
723
+
724
+ def find_log_dir_and_names(program_name=None, log_dir=None):
725
+ """Computes the directory and filename prefix for log file.
726
+
727
+ Args:
728
+ program_name: str|None, the filename part of the path to the program that is
729
+ running without its extension. e.g: if your program is called
730
+ ``usr/bin/foobar.py`` this method should probably be called with
731
+ ``program_name='foobar`` However, this is just a convention, you can pass
732
+ in any string you want, and it will be used as part of the log filename.
733
+ If you don't pass in anything, the default behavior is as described in the
734
+ example. In python standard logging mode, the program_name will be
735
+ prepended with ``py_`` if it is the ``program_name`` argument is omitted.
736
+ log_dir: str|None, the desired log directory.
737
+
738
+ Returns:
739
+ (log_dir, file_prefix, symlink_prefix)
740
+
741
+ Raises:
742
+ FileNotFoundError: raised when it cannot find a log directory.
743
+ """
744
+ if not program_name:
745
+ # Strip the extension (foobar.par becomes foobar, and
746
+ # fubar.py becomes fubar). We do this so that the log
747
+ # file names are similar to C++ log file names.
748
+ program_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
749
+
750
+ # Prepend py_ to files so that python code gets a unique file, and
751
+ # so that C++ libraries do not try to write to the same log files as us.
752
+ program_name = 'py_%s' % program_name
753
+
754
+ actual_log_dir = find_log_dir(log_dir=log_dir)
755
+
756
+ try:
757
+ username = getpass.getuser()
758
+ except KeyError:
759
+ # This can happen, e.g. when running under docker w/o passwd file.
760
+ if hasattr(os, 'getuid'):
761
+ # Windows doesn't have os.getuid
762
+ username = str(os.getuid())
763
+ else:
764
+ username = 'unknown'
765
+ hostname = socket.gethostname()
766
+ file_prefix = '%s.%s.%s.log' % (program_name, hostname, username)
767
+
768
+ return actual_log_dir, file_prefix, program_name
769
+
770
+
771
+ def find_log_dir(log_dir=None):
772
+ """Returns the most suitable directory to put log files into.
773
+
774
+ Args:
775
+ log_dir: str|None, if specified, the logfile(s) will be created in that
776
+ directory. Otherwise if the --log_dir command-line flag is provided, the
777
+ logfile will be created in that directory. Otherwise the logfile will be
778
+ created in a standard location.
779
+
780
+ Raises:
781
+ FileNotFoundError: raised when it cannot find a log directory.
782
+ """
783
+ # Get a list of possible log dirs (will try to use them in order).
784
+ # NOTE: Google's internal implementation has a special handling for Google
785
+ # machines, which uses a list of directories. Hence the following uses `dirs`
786
+ # instead of a single directory.
787
+ if log_dir:
788
+ # log_dir was explicitly specified as an arg, so use it and it alone.
789
+ dirs = [log_dir]
790
+ elif FLAGS['log_dir'].value:
791
+ # log_dir flag was provided, so use it and it alone (this mimics the
792
+ # behavior of the same flag in logging.cc).
793
+ dirs = [FLAGS['log_dir'].value]
794
+ else:
795
+ dirs = [tempfile.gettempdir()]
796
+
797
+ # Find the first usable log dir.
798
+ for d in dirs:
799
+ if os.path.isdir(d) and os.access(d, os.W_OK):
800
+ return d
801
+ raise FileNotFoundError(
802
+ "Can't find a writable directory for logs, tried %s" % dirs)
803
+
804
+
805
+ def get_absl_log_prefix(record):
806
+ """Returns the absl log prefix for the log record.
807
+
808
+ Args:
809
+ record: logging.LogRecord, the record to get prefix for.
810
+ """
811
+ created_tuple = time.localtime(record.created)
812
+ created_microsecond = int(record.created % 1.0 * 1e6)
813
+
814
+ critical_prefix = ''
815
+ level = record.levelno
816
+ if _is_non_absl_fatal_record(record):
817
+ # When the level is FATAL, but not logged from absl, lower the level so
818
+ # it's treated as ERROR.
819
+ level = logging.ERROR
820
+ critical_prefix = _CRITICAL_PREFIX
821
+ severity = converter.get_initial_for_level(level)
822
+
823
+ return '%c%02d%02d %02d:%02d:%02d.%06d %5d %s:%d] %s' % (
824
+ severity,
825
+ created_tuple.tm_mon,
826
+ created_tuple.tm_mday,
827
+ created_tuple.tm_hour,
828
+ created_tuple.tm_min,
829
+ created_tuple.tm_sec,
830
+ created_microsecond,
831
+ _get_thread_id(),
832
+ record.filename,
833
+ record.lineno,
834
+ critical_prefix)
835
+
836
+
837
+ def skip_log_prefix(func):
838
+ """Skips reporting the prefix of a given function or name by :class:`~absl.logging.ABSLLogger`.
839
+
840
+ This is a convenience wrapper function / decorator for
841
+ :meth:`~absl.logging.ABSLLogger.register_frame_to_skip`.
842
+
843
+ If a callable function is provided, only that function will be skipped.
844
+ If a function name is provided, all functions with the same name in the
845
+ file that this is called in will be skipped.
846
+
847
+ This can be used as a decorator of the intended function to be skipped.
848
+
849
+ Args:
850
+ func: Callable function or its name as a string.
851
+
852
+ Returns:
853
+ func (the input, unchanged).
854
+
855
+ Raises:
856
+ ValueError: The input is callable but does not have a function code object.
857
+ TypeError: The input is neither callable nor a string.
858
+ """
859
+ if callable(func):
860
+ func_code = getattr(func, '__code__', None)
861
+ if func_code is None:
862
+ raise ValueError('Input callable does not have a function code object.')
863
+ file_name = func_code.co_filename
864
+ func_name = func_code.co_name
865
+ func_lineno = func_code.co_firstlineno
866
+ elif isinstance(func, str):
867
+ file_name = get_absl_logger().findCaller()[0]
868
+ func_name = func
869
+ func_lineno = None
870
+ else:
871
+ raise TypeError('Input is neither callable nor a string.')
872
+ ABSLLogger.register_frame_to_skip(file_name, func_name, func_lineno)
873
+ return func
874
+
875
+
876
+ def _is_non_absl_fatal_record(log_record):
877
+ return (log_record.levelno >= logging.FATAL and
878
+ not log_record.__dict__.get(_ABSL_LOG_FATAL, False))
879
+
880
+
881
+ def _is_absl_fatal_record(log_record):
882
+ return (log_record.levelno >= logging.FATAL and
883
+ log_record.__dict__.get(_ABSL_LOG_FATAL, False))
884
+
885
+
886
+ # Indicates if we still need to warn about pre-init logs going to stderr.
887
+ _warn_preinit_stderr = True
888
+
889
+
890
+ class PythonHandler(logging.StreamHandler):
891
+ """The handler class used by Abseil Python logging implementation."""
892
+
893
+ def __init__(self, stream=None, formatter=None):
894
+ super().__init__(stream)
895
+ self.setFormatter(formatter or PythonFormatter())
896
+
897
+ def start_logging_to_file(self, program_name=None, log_dir=None):
898
+ """Starts logging messages to files instead of standard error."""
899
+ FLAGS.logtostderr = False
900
+
901
+ actual_log_dir, file_prefix, symlink_prefix = find_log_dir_and_names(
902
+ program_name=program_name, log_dir=log_dir)
903
+
904
+ basename = '%s.INFO.%s.%d' % (
905
+ file_prefix,
906
+ time.strftime('%Y%m%d-%H%M%S', time.localtime(time.time())),
907
+ os.getpid())
908
+ filename = os.path.join(actual_log_dir, basename)
909
+
910
+ self.stream = open(filename, 'a', encoding='utf-8')
911
+
912
+ # os.symlink is not available on Windows Python 2.
913
+ if getattr(os, 'symlink', None):
914
+ # Create a symlink to the log file with a canonical name.
915
+ symlink = os.path.join(actual_log_dir, symlink_prefix + '.INFO')
916
+ try:
917
+ if os.path.islink(symlink):
918
+ os.unlink(symlink)
919
+ os.symlink(os.path.basename(filename), symlink)
920
+ except OSError:
921
+ # If it fails, we're sad but it's no error. Commonly, this
922
+ # fails because the symlink was created by another user and so
923
+ # we can't modify it
924
+ pass
925
+
926
+ def use_absl_log_file(self, program_name=None, log_dir=None):
927
+ """Conditionally logs to files, based on --logtostderr."""
928
+ if FLAGS['logtostderr'].value:
929
+ self.stream = sys.stderr
930
+ else:
931
+ self.start_logging_to_file(program_name=program_name, log_dir=log_dir)
932
+
933
+ def flush(self):
934
+ """Flushes all log files."""
935
+ self.acquire()
936
+ try:
937
+ if self.stream and hasattr(self.stream, 'flush'):
938
+ self.stream.flush()
939
+ except (OSError, ValueError):
940
+ # A ValueError is thrown if we try to flush a closed file.
941
+ pass
942
+ finally:
943
+ self.release()
944
+
945
+ def _log_to_stderr(self, record):
946
+ """Emits the record to stderr.
947
+
948
+ This temporarily sets the handler stream to stderr, calls
949
+ StreamHandler.emit, then reverts the stream back.
950
+
951
+ Args:
952
+ record: logging.LogRecord, the record to log.
953
+ """
954
+ # emit() is protected by a lock in logging.Handler, so we don't need to
955
+ # protect here again.
956
+ old_stream = self.stream
957
+ self.stream = sys.stderr
958
+ try:
959
+ super().emit(record)
960
+ finally:
961
+ self.stream = old_stream
962
+
963
+ def emit(self, record):
964
+ """Prints a record out to some streams.
965
+
966
+ 1. If ``FLAGS.logtostderr`` is set, it will print to ``sys.stderr`` ONLY.
967
+ 2. If ``FLAGS.alsologtostderr`` is set, it will print to ``sys.stderr``.
968
+ 3. If ``FLAGS.logtostderr`` is not set, it will log to the stream
969
+ associated with the current thread.
970
+
971
+ Args:
972
+ record: :class:`logging.LogRecord`, the record to emit.
973
+ """
974
+ # People occasionally call logging functions at import time before
975
+ # our flags may have even been defined yet, let alone even parsed, as we
976
+ # rely on the C++ side to define some flags for us and app init to
977
+ # deal with parsing. Match the C++ library behavior of notify and emit
978
+ # such messages to stderr. It encourages people to clean-up and does
979
+ # not hide the message.
980
+ level = record.levelno
981
+ if not FLAGS.is_parsed(): # Also implies "before flag has been defined".
982
+ global _warn_preinit_stderr
983
+ if _warn_preinit_stderr:
984
+ sys.stderr.write(
985
+ 'WARNING: Logging before flag parsing goes to stderr.\n')
986
+ _warn_preinit_stderr = False
987
+ self._log_to_stderr(record)
988
+ elif FLAGS['logtostderr'].value:
989
+ self._log_to_stderr(record)
990
+ else:
991
+ super().emit(record)
992
+ stderr_threshold = converter.string_to_standard(
993
+ FLAGS['stderrthreshold'].value)
994
+ if ((FLAGS['alsologtostderr'].value or level >= stderr_threshold) and
995
+ self.stream != sys.stderr):
996
+ self._log_to_stderr(record)
997
+ # Die when the record is created from ABSLLogger and level is FATAL.
998
+ if _is_absl_fatal_record(record):
999
+ self.flush() # Flush the log before dying.
1000
+
1001
+ # In threaded python, sys.exit() from a non-main thread only
1002
+ # exits the thread in question.
1003
+ os.abort()
1004
+
1005
+ def close(self):
1006
+ """Closes the stream to which we are writing."""
1007
+ self.acquire()
1008
+ try:
1009
+ self.flush()
1010
+ try:
1011
+ # Do not close the stream if it's sys.stderr|stdout. They may be
1012
+ # redirected or overridden to files, which should be managed by users
1013
+ # explicitly.
1014
+ user_managed = sys.stderr, sys.stdout, sys.__stderr__, sys.__stdout__
1015
+ if self.stream not in user_managed and (
1016
+ not hasattr(self.stream, 'isatty') or not self.stream.isatty()):
1017
+ self.stream.close()
1018
+ except ValueError:
1019
+ # A ValueError is thrown if we try to run isatty() on a closed file.
1020
+ pass
1021
+ super().close()
1022
+ finally:
1023
+ self.release()
1024
+
1025
+
1026
+ class ABSLHandler(logging.Handler):
1027
+ """Abseil Python logging module's log handler."""
1028
+
1029
+ def __init__(self, python_logging_formatter):
1030
+ super().__init__()
1031
+
1032
+ self._python_handler = PythonHandler(formatter=python_logging_formatter)
1033
+ self.activate_python_handler()
1034
+
1035
+ def format(self, record):
1036
+ return self._current_handler.format(record)
1037
+
1038
+ def setFormatter(self, fmt):
1039
+ self._current_handler.setFormatter(fmt)
1040
+
1041
+ def emit(self, record):
1042
+ self._current_handler.emit(record)
1043
+
1044
+ def flush(self):
1045
+ self._current_handler.flush()
1046
+
1047
+ def close(self):
1048
+ super().close()
1049
+ self._current_handler.close()
1050
+
1051
+ def handle(self, record):
1052
+ rv = self.filter(record)
1053
+ if rv:
1054
+ return self._current_handler.handle(record)
1055
+ return rv
1056
+
1057
+ @property
1058
+ def python_handler(self):
1059
+ return self._python_handler
1060
+
1061
+ def activate_python_handler(self):
1062
+ """Uses the Python logging handler as the current logging handler."""
1063
+ self._current_handler = self._python_handler
1064
+
1065
+ def use_absl_log_file(self, program_name=None, log_dir=None):
1066
+ self._current_handler.use_absl_log_file(program_name, log_dir)
1067
+
1068
+ def start_logging_to_file(self, program_name=None, log_dir=None):
1069
+ self._current_handler.start_logging_to_file(program_name, log_dir)
1070
+
1071
+
1072
+ class PythonFormatter(logging.Formatter):
1073
+ """Formatter class used by :class:`~absl.logging.PythonHandler`."""
1074
+
1075
+ def format(self, record):
1076
+ """Appends the message from the record to the results of the prefix.
1077
+
1078
+ Args:
1079
+ record: logging.LogRecord, the record to be formatted.
1080
+
1081
+ Returns:
1082
+ The formatted string representing the record.
1083
+ """
1084
+ if (not FLAGS['showprefixforinfo'].value and
1085
+ FLAGS['verbosity'].value == converter.ABSL_INFO and
1086
+ record.levelno == logging.INFO and
1087
+ _absl_handler.python_handler.stream == sys.stderr):
1088
+ prefix = ''
1089
+ else:
1090
+ prefix = get_absl_log_prefix(record)
1091
+ return prefix + super().format(record)
1092
+
1093
+
1094
+ class ABSLLogger(logging.getLoggerClass()):
1095
+ """A logger that will create LogRecords while skipping some stack frames.
1096
+
1097
+ This class maintains an internal list of filenames and method names
1098
+ for use when determining who called the currently executing stack
1099
+ frame. Any method names from specific source files are skipped when
1100
+ walking backwards through the stack.
1101
+
1102
+ Client code should use the register_frame_to_skip method to let the
1103
+ ABSLLogger know which method from which file should be
1104
+ excluded from the walk backwards through the stack.
1105
+ """
1106
+ _frames_to_skip = set()
1107
+
1108
+ def findCaller(self, stack_info=False, stacklevel=1):
1109
+ """Finds the frame of the calling method on the stack.
1110
+
1111
+ This method skips any frames registered with the
1112
+ ABSLLogger and any methods from this file, and whatever
1113
+ method is currently being used to generate the prefix for the log
1114
+ line. Then it returns the file name, line number, and method name
1115
+ of the calling method. An optional fourth item may be returned,
1116
+ callers who only need things from the first three are advised to
1117
+ always slice or index the result rather than using direct unpacking
1118
+ assignment.
1119
+
1120
+ Args:
1121
+ stack_info: bool, when True, include the stack trace as a fourth item
1122
+ returned. On Python 3 there are always four items returned - the fourth
1123
+ will be None when this is False. On Python 2 the stdlib base class API
1124
+ only returns three items. We do the same when this new parameter is
1125
+ unspecified or False for compatibility.
1126
+ stacklevel: int, if greater than 1, that number of frames will be skipped.
1127
+
1128
+ Returns:
1129
+ (filename, lineno, methodname[, sinfo]) of the calling method.
1130
+ """
1131
+ f_to_skip = ABSLLogger._frames_to_skip
1132
+ # Use sys._getframe(2) instead of logging.currentframe(), it's slightly
1133
+ # faster because there is one less frame to traverse.
1134
+ frame = sys._getframe(2) # pylint: disable=protected-access
1135
+ frame_to_return = None
1136
+
1137
+ while frame:
1138
+ code = frame.f_code
1139
+ if (_LOGGING_FILE_PREFIX not in code.co_filename and
1140
+ (code.co_filename, code.co_name,
1141
+ code.co_firstlineno) not in f_to_skip and
1142
+ (code.co_filename, code.co_name) not in f_to_skip):
1143
+ frame_to_return = frame
1144
+ stacklevel -= 1
1145
+ if stacklevel <= 0:
1146
+ break
1147
+ frame = frame.f_back
1148
+
1149
+ if frame_to_return is not None:
1150
+ sinfo = None
1151
+ if stack_info:
1152
+ out = io.StringIO()
1153
+ out.write('Stack (most recent call last):\n')
1154
+ traceback.print_stack(frame, file=out)
1155
+ sinfo = out.getvalue().rstrip('\n')
1156
+ return (
1157
+ frame_to_return.f_code.co_filename,
1158
+ frame_to_return.f_lineno,
1159
+ frame_to_return.f_code.co_name,
1160
+ sinfo,
1161
+ )
1162
+
1163
+ return None
1164
+
1165
+ def critical(self, msg, *args, **kwargs):
1166
+ """Logs ``msg % args`` with severity ``CRITICAL``."""
1167
+ self.log(logging.CRITICAL, msg, *args, **kwargs)
1168
+
1169
+ def fatal(self, msg, *args, **kwargs):
1170
+ """Logs ``msg % args`` with severity ``FATAL``."""
1171
+ self.log(logging.FATAL, msg, *args, **kwargs)
1172
+
1173
+ def error(self, msg, *args, **kwargs):
1174
+ """Logs ``msg % args`` with severity ``ERROR``."""
1175
+ self.log(logging.ERROR, msg, *args, **kwargs)
1176
+
1177
+ def warn(self, msg, *args, **kwargs):
1178
+ """Logs ``msg % args`` with severity ``WARN``."""
1179
+ warnings.warn("The 'warn' method is deprecated, use 'warning' instead",
1180
+ DeprecationWarning, 2)
1181
+ self.log(logging.WARN, msg, *args, **kwargs)
1182
+
1183
+ def warning(self, msg, *args, **kwargs):
1184
+ """Logs ``msg % args`` with severity ``WARNING``."""
1185
+ self.log(logging.WARNING, msg, *args, **kwargs)
1186
+
1187
+ def info(self, msg, *args, **kwargs):
1188
+ """Logs ``msg % args`` with severity ``INFO``."""
1189
+ self.log(logging.INFO, msg, *args, **kwargs)
1190
+
1191
+ def debug(self, msg, *args, **kwargs):
1192
+ """Logs ``msg % args`` with severity ``DEBUG``."""
1193
+ self.log(logging.DEBUG, msg, *args, **kwargs)
1194
+
1195
+ def log(self, level, msg, *args, **kwargs):
1196
+ """Logs a message at a certain level substituting in the supplied arguments.
1197
+
1198
+ This method behaves differently in python and c++ modes.
1199
+
1200
+ Args:
1201
+ level: int, the standard logging level at which to log the message.
1202
+ msg: str, the text of the message to log.
1203
+ *args: The arguments to substitute in the message.
1204
+ **kwargs: The keyword arguments to substitute in the message.
1205
+ """
1206
+ if level >= logging.FATAL:
1207
+ # Add property to the LogRecord created by this logger.
1208
+ # This will be used by the ABSLHandler to determine whether it should
1209
+ # treat CRITICAL/FATAL logs as really FATAL.
1210
+ extra = kwargs.setdefault('extra', {})
1211
+ extra[_ABSL_LOG_FATAL] = True
1212
+ super().log(level, msg, *args, **kwargs)
1213
+
1214
+ def handle(self, record):
1215
+ """Calls handlers without checking ``Logger.disabled``.
1216
+
1217
+ Non-root loggers are set to disabled after setup with :func:`logging.config`
1218
+ if it's not explicitly specified. Historically, absl logging will not be
1219
+ disabled by that. To maintaining this behavior, this function skips
1220
+ checking the ``Logger.disabled`` bit.
1221
+
1222
+ This logger can still be disabled by adding a filter that filters out
1223
+ everything.
1224
+
1225
+ Args:
1226
+ record: logging.LogRecord, the record to handle.
1227
+ """
1228
+ if self.filter(record):
1229
+ self.callHandlers(record)
1230
+
1231
+ @classmethod
1232
+ def register_frame_to_skip(cls, file_name, function_name, line_number=None):
1233
+ """Registers a function name to skip when walking the stack.
1234
+
1235
+ The :class:`~absl.logging.ABSLLogger` sometimes skips method calls on the
1236
+ stack to make the log messages meaningful in their appropriate context.
1237
+ This method registers a function from a particular file as one
1238
+ which should be skipped.
1239
+
1240
+ Args:
1241
+ file_name: str, the name of the file that contains the function.
1242
+ function_name: str, the name of the function to skip.
1243
+ line_number: int, if provided, only the function with this starting line
1244
+ number will be skipped. Otherwise, all functions with the same name
1245
+ in the file will be skipped.
1246
+ """
1247
+ if line_number is not None:
1248
+ cls._frames_to_skip.add((file_name, function_name, line_number))
1249
+ else:
1250
+ cls._frames_to_skip.add((file_name, function_name))
1251
+
1252
+
1253
+ def _get_thread_id():
1254
+ """Gets id of current thread, suitable for logging as an unsigned quantity.
1255
+
1256
+ If pywrapbase is linked, returns GetTID() for the thread ID to be
1257
+ consistent with C++ logging. Otherwise, returns the numeric thread id.
1258
+ The quantities are made unsigned by masking with 2*sys.maxint + 1.
1259
+
1260
+ Returns:
1261
+ Thread ID unique to this process (unsigned)
1262
+ """
1263
+ thread_id = threading.get_ident()
1264
+ return thread_id & _THREAD_ID_MASK
1265
+
1266
+
1267
+ def get_absl_logger():
1268
+ """Returns the absl logger instance."""
1269
+ assert _absl_logger is not None
1270
+ return _absl_logger
1271
+
1272
+
1273
+ def get_absl_handler():
1274
+ """Returns the absl handler instance."""
1275
+ assert _absl_handler is not None
1276
+ return _absl_handler
1277
+
1278
+
1279
+ def use_python_logging(quiet=False):
1280
+ """Uses the python implementation of the logging code.
1281
+
1282
+ Args:
1283
+ quiet: No logging message about switching logging type.
1284
+ """
1285
+ get_absl_handler().activate_python_handler()
1286
+ if not quiet:
1287
+ info('Restoring pure python logging')
1288
+
1289
+
1290
+ _attempted_to_remove_stderr_stream_handlers = False
1291
+
1292
+
1293
+ def use_absl_handler():
1294
+ """Uses the ABSL logging handler for logging.
1295
+
1296
+ This method is called in :func:`app.run()<absl.app.run>` so the absl handler
1297
+ is used in absl apps.
1298
+ """
1299
+ global _attempted_to_remove_stderr_stream_handlers
1300
+ if not _attempted_to_remove_stderr_stream_handlers:
1301
+ # The absl handler logs to stderr by default. To prevent double logging to
1302
+ # stderr, the following code tries its best to remove other handlers that
1303
+ # emit to stderr. Those handlers are most commonly added when
1304
+ # logging.info/debug is called before calling use_absl_handler().
1305
+ handlers = [
1306
+ h for h in logging.root.handlers
1307
+ if isinstance(h, logging.StreamHandler) and h.stream == sys.stderr]
1308
+ for h in handlers:
1309
+ logging.root.removeHandler(h)
1310
+ _attempted_to_remove_stderr_stream_handlers = True
1311
+
1312
+ absl_handler = get_absl_handler()
1313
+ if absl_handler not in logging.root.handlers:
1314
+ logging.root.addHandler(absl_handler)
1315
+ FLAGS['verbosity']._update_logging_levels() # pylint: disable=protected-access
1316
+ FLAGS['logger_levels']._update_logger_levels() # pylint: disable=protected-access
1317
+
1318
+
1319
+ def _initialize():
1320
+ """Initializes loggers and handlers."""
1321
+ global _absl_logger, _absl_handler
1322
+
1323
+ if _absl_logger:
1324
+ return
1325
+
1326
+ original_logger_class = logging.getLoggerClass()
1327
+ logging.setLoggerClass(ABSLLogger)
1328
+ _absl_logger = logging.getLogger('absl')
1329
+ logging.setLoggerClass(original_logger_class)
1330
+
1331
+ python_logging_formatter = PythonFormatter()
1332
+ _absl_handler = ABSLHandler(python_logging_formatter)
1333
+
1334
+
1335
+ _initialize()
lib/python3.12/site-packages/absl/logging/__init__.pyi ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from collections.abc import Callable
16
+ import logging
17
+ from typing import Any, NoReturn, TypeVar
18
+
19
+ from absl import flags
20
+
21
+ # Logging levels.
22
+ FATAL: int
23
+ ERROR: int
24
+ WARNING: int
25
+ WARN: int # Deprecated name.
26
+ INFO: int
27
+ DEBUG: int
28
+
29
+ ABSL_LOGGING_PREFIX_REGEX: str
30
+
31
+ LOGTOSTDERR: flags.FlagHolder[bool]
32
+ ALSOLOGTOSTDERR: flags.FlagHolder[bool]
33
+ LOG_DIR: flags.FlagHolder[str]
34
+ VERBOSITY: flags.FlagHolder[int]
35
+ LOGGER_LEVELS: flags.FlagHolder[dict[str, str]]
36
+ STDERRTHRESHOLD: flags.FlagHolder[str]
37
+ SHOWPREFIXFORINFO: flags.FlagHolder[bool]
38
+
39
+ def get_verbosity() -> int:
40
+ ...
41
+
42
+ def set_verbosity(v: int | str) -> None:
43
+ ...
44
+
45
+ def set_stderrthreshold(s: int | str) -> None:
46
+ ...
47
+
48
+ # TODO(b/277607978): Provide actual args+kwargs shadowing stdlib's logging functions.
49
+ def fatal(msg: Any, *args: Any, **kwargs: Any) -> NoReturn:
50
+ ...
51
+
52
+ def error(msg: Any, *args: Any, **kwargs: Any) -> None:
53
+ ...
54
+
55
+ def warning(msg: Any, *args: Any, **kwargs: Any) -> None:
56
+ ...
57
+
58
+ def warn(msg: Any, *args: Any, **kwargs: Any) -> None:
59
+ ...
60
+
61
+ def info(msg: Any, *args: Any, **kwargs: Any) -> None:
62
+ ...
63
+
64
+ def debug(msg: Any, *args: Any, **kwargs: Any) -> None:
65
+ ...
66
+
67
+ def exception(msg: Any, *args: Any, **kwargs: Any) -> None:
68
+ ...
69
+
70
+ def log_every_n(
71
+ level: int,
72
+ msg: Any,
73
+ n: int,
74
+ *args: Any,
75
+ use_call_stack: bool = ...,
76
+ **kwargs: Any,
77
+ ) -> None:
78
+ ...
79
+
80
+ def log_every_n_seconds(
81
+ level: int,
82
+ msg: Any,
83
+ n_seconds: float,
84
+ *args: Any,
85
+ use_call_stack: bool = ...,
86
+ **kwargs: Any,
87
+ ) -> None:
88
+ ...
89
+
90
+ def log_first_n(
91
+ level: int,
92
+ msg: Any,
93
+ n: int,
94
+ *args: Any,
95
+ use_call_stack: bool = ...,
96
+ **kwargs: Any,
97
+ ) -> None:
98
+ ...
99
+
100
+ def log_if(level: int,
101
+ msg: Any,
102
+ condition: Any,
103
+ *args: Any,
104
+ **kwargs: Any,
105
+ ) -> None:
106
+ ...
107
+
108
+ def log(level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
109
+ ...
110
+
111
+ def vlog(level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
112
+ ...
113
+
114
+ def vlog_is_on(level: int) -> bool:
115
+ ...
116
+
117
+ def flush() -> None:
118
+ ...
119
+
120
+ def level_debug() -> bool:
121
+ ...
122
+
123
+ def level_info() -> bool:
124
+ ...
125
+
126
+ def level_warning() -> bool:
127
+ ...
128
+
129
+ level_warn = level_warning # Deprecated function.
130
+
131
+ def level_error() -> bool:
132
+ ...
133
+
134
+ def get_log_file_name(level: int = ...) -> str:
135
+ ...
136
+
137
+ def find_log_dir_and_names(
138
+ program_name: str | None = ..., log_dir: str | None = ...
139
+ ) -> tuple[str, str, str]:
140
+ ...
141
+
142
+ def find_log_dir(log_dir: str | None = ...) -> str:
143
+ ...
144
+
145
+ def get_absl_log_prefix(record: logging.LogRecord) -> str:
146
+ ...
147
+
148
+ _SkipLogT = TypeVar('_SkipLogT', str, Callable[..., Any])
149
+
150
+ def skip_log_prefix(func: _SkipLogT) -> _SkipLogT:
151
+ ...
152
+
153
+ _StreamT = TypeVar('_StreamT')
154
+
155
+ class PythonHandler(logging.StreamHandler[_StreamT]): # type: ignore[type-var]
156
+
157
+ def __init__(
158
+ self,
159
+ stream: _StreamT | None = ...,
160
+ formatter: logging.Formatter | None = ...,
161
+ ) -> None:
162
+ ...
163
+
164
+ def start_logging_to_file(
165
+ self, program_name: str | None = ..., log_dir: str | None = ...
166
+ ) -> None:
167
+ ...
168
+
169
+ def use_absl_log_file(
170
+ self, program_name: str | None = ..., log_dir: str | None = ...
171
+ ) -> None:
172
+ ...
173
+
174
+ def flush(self) -> None:
175
+ ...
176
+
177
+ def emit(self, record: logging.LogRecord) -> None:
178
+ ...
179
+
180
+ def close(self) -> None:
181
+ ...
182
+
183
+ class ABSLHandler(logging.Handler):
184
+
185
+ def __init__(self, python_logging_formatter: PythonFormatter) -> None:
186
+ ...
187
+
188
+ def format(self, record: logging.LogRecord) -> str:
189
+ ...
190
+
191
+ def setFormatter(self, fmt) -> None:
192
+ ...
193
+
194
+ def emit(self, record: logging.LogRecord) -> None:
195
+ ...
196
+
197
+ def flush(self) -> None:
198
+ ...
199
+
200
+ def close(self) -> None:
201
+ ...
202
+
203
+ def handle(self, record: logging.LogRecord) -> bool:
204
+ ...
205
+
206
+ @property
207
+ def python_handler(self) -> PythonHandler:
208
+ ...
209
+
210
+ def activate_python_handler(self) -> None:
211
+ ...
212
+
213
+ def use_absl_log_file(
214
+ self, program_name: str | None = ..., log_dir: str | None = ...
215
+ ) -> None:
216
+ ...
217
+
218
+ def start_logging_to_file(self, program_name=None, log_dir=None) -> None:
219
+ ...
220
+
221
+ class PythonFormatter(logging.Formatter):
222
+
223
+ def format(self, record: logging.LogRecord) -> str:
224
+ ...
225
+
226
+ class ABSLLogger(logging.Logger):
227
+
228
+ def findCaller(
229
+ self, stack_info: bool = ..., stacklevel: int = ...
230
+ ) -> tuple[str, int, str, str | None]:
231
+ ...
232
+
233
+ def critical(self, msg: Any, *args: Any, **kwargs: Any) -> None:
234
+ ...
235
+
236
+ def fatal(self, msg: Any, *args: Any, **kwargs: Any) -> NoReturn: # type: ignore[override]
237
+ ...
238
+
239
+ def error(self, msg: Any, *args: Any, **kwargs: Any) -> None:
240
+ ...
241
+
242
+ def warn(self, msg: Any, *args: Any, **kwargs: Any) -> None:
243
+ ...
244
+
245
+ def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None:
246
+ ...
247
+
248
+ def info(self, msg: Any, *args: Any, **kwargs: Any) -> None:
249
+ ...
250
+
251
+ def debug(self, msg: Any, *args: Any, **kwargs: Any) -> None:
252
+ ...
253
+
254
+ def log(self, level: int, msg: Any, *args: Any, **kwargs: Any) -> None:
255
+ ...
256
+
257
+ def handle(self, record: logging.LogRecord) -> None:
258
+ ...
259
+
260
+ @classmethod
261
+ def register_frame_to_skip(
262
+ cls, file_name: str, function_name: str, line_number: int | None = ...
263
+ ) -> None:
264
+ ...
265
+
266
+ # NOTE: Returns None before _initialize called but shouldn't occur after import.
267
+ def get_absl_logger() -> ABSLLogger:
268
+ ...
269
+
270
+ # NOTE: Returns None before _initialize called but shouldn't occur after import.
271
+ def get_absl_handler() -> ABSLHandler:
272
+ ...
273
+
274
+ def use_python_logging(quiet: bool = ...) -> None:
275
+ ...
276
+
277
+ def use_absl_handler() -> None:
278
+ ...
lib/python3.12/site-packages/absl/logging/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (54.2 kB). View file
 
lib/python3.12/site-packages/absl/logging/__pycache__/converter.cpython-312.pyc ADDED
Binary file (6.19 kB). View file
 
lib/python3.12/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.12/site-packages/absl/py.typed ADDED
File without changes
lib/python3.12/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.12/site-packages/absl/testing/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (191 Bytes). View file
 
lib/python3.12/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-312.pyc ADDED
Binary file (2.39 kB). View file
 
lib/python3.12/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-312.pyc ADDED
Binary file (4.6 kB). View file
 
lib/python3.12/site-packages/absl/testing/__pycache__/flagsaver.cpython-312.pyc ADDED
Binary file (16 kB). View file
 
lib/python3.12/site-packages/absl/testing/__pycache__/parameterized.cpython-312.pyc ADDED
Binary file (29.7 kB). View file
 
lib/python3.12/site-packages/absl/testing/__pycache__/xml_reporter.cpython-312.pyc ADDED
Binary file (27.4 kB). View file
 
lib/python3.12/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.12/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.12/site-packages/absl/testing/absltest.py ADDED
The diff for this file is too large to render. See raw diff
 
lib/python3.12/site-packages/absl/testing/flagsaver.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from collections.abc import Callable, Mapping, Sequence
93
+ import functools
94
+ import inspect
95
+ from typing import Any, TypeVar, 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(
125
+ *args: tuple[flags.FlagHolder, str | Sequence[str]],
126
+ **kwargs: str | Sequence[str],
127
+ ) -> '_ParsingFlagOverrider':
128
+ ...
129
+
130
+
131
+ @overload
132
+ def as_parsed(func: _CallableT) -> _CallableT:
133
+ ...
134
+
135
+
136
+ def as_parsed(*args, **kwargs):
137
+ """Overrides flags by parsing strings, saves flag state similar to flagsaver.
138
+
139
+ This function can be used as either a decorator or context manager similar to
140
+ flagsaver.flagsaver(). However, where flagsaver.flagsaver() directly sets the
141
+ flags to new values, this function will parse the provided arguments as if
142
+ they were provided on the command line. Among other things, this will cause
143
+ `FLAGS['flag_name'].present == True`.
144
+
145
+ A note on unparsed input: For many flag types, the unparsed version will be
146
+ a single string. However for multi_x (multi_string, multi_integer, multi_enum)
147
+ the unparsed version will be a Sequence of strings.
148
+
149
+ Args:
150
+ *args: Tuples of FlagHolders and their unparsed value.
151
+ **kwargs: The keyword args are flag names, and the values are unparsed
152
+ values.
153
+
154
+ Returns:
155
+ _ParsingFlagOverrider that serves as a context manager or decorator. Will
156
+ save previous flag state and parse new flags, then on cleanup it will
157
+ restore the previous flag state.
158
+ """
159
+ return _construct_overrider(_ParsingFlagOverrider, *args, **kwargs)
160
+
161
+
162
+ # NOTE: the order of these overload declarations matters. The type checker will
163
+ # pick the first match which could be incorrect.
164
+ @overload
165
+ def _construct_overrider(
166
+ flag_overrider_cls: type['_ParsingFlagOverrider'],
167
+ *args: tuple[flags.FlagHolder, str | Sequence[str]],
168
+ **kwargs: str | Sequence[str],
169
+ ) -> '_ParsingFlagOverrider':
170
+ ...
171
+
172
+
173
+ @overload
174
+ def _construct_overrider(
175
+ flag_overrider_cls: type['_FlagOverrider'], func: _CallableT
176
+ ) -> _CallableT:
177
+ ...
178
+
179
+
180
+ @overload
181
+ def _construct_overrider(
182
+ flag_overrider_cls: type['_FlagOverrider'],
183
+ *args: tuple[flags.FlagHolder, Any],
184
+ **kwargs: Any,
185
+ ) -> '_FlagOverrider':
186
+ ...
187
+
188
+
189
+ def _construct_overrider(flag_overrider_cls, *args, **kwargs):
190
+ """Handles the args/kwargs returning an instance of flag_overrider_cls.
191
+
192
+ If flag_overrider_cls is _FlagOverrider then values should be native python
193
+ types matching the python types. Otherwise if flag_overrider_cls is
194
+ _ParsingFlagOverrider the values should be strings or sequences of strings.
195
+
196
+ Args:
197
+ flag_overrider_cls: The class that will do the overriding.
198
+ *args: Tuples of FlagHolder and the new flag value.
199
+ **kwargs: Keword args mapping flag name to new flag value.
200
+
201
+ Returns:
202
+ A _FlagOverrider to be used as a decorator or context manager.
203
+ """
204
+ if not args:
205
+ return flag_overrider_cls(**kwargs)
206
+ # args can be [func] if used as `@flagsaver` instead of `@flagsaver(...)`
207
+ if len(args) == 1 and callable(args[0]):
208
+ if kwargs:
209
+ raise ValueError(
210
+ "It's invalid to specify both positional and keyword parameters.")
211
+ func = args[0]
212
+ if inspect.isclass(func):
213
+ raise TypeError('@flagsaver.flagsaver cannot be applied to a class.')
214
+ return _wrap(flag_overrider_cls, func, {})
215
+ # args can be a list of (FlagHolder, value) pairs.
216
+ # In which case they augment any specified kwargs.
217
+ for arg in args:
218
+ if not isinstance(arg, tuple) or len(arg) != 2:
219
+ raise ValueError('Expected (FlagHolder, value) pair, found %r' % (arg,))
220
+ holder, value = arg
221
+ if not isinstance(holder, flags.FlagHolder):
222
+ raise ValueError('Expected (FlagHolder, value) pair, found %r' % (arg,))
223
+ if holder.name in kwargs:
224
+ raise ValueError('Cannot set --%s multiple times' % holder.name)
225
+ kwargs[holder.name] = value
226
+ return flag_overrider_cls(**kwargs)
227
+
228
+
229
+ def save_flag_values(
230
+ flag_values: flags.FlagValues = FLAGS,
231
+ ) -> dict[str, dict[str, Any]]:
232
+ """Returns copy of flag values as a dict.
233
+
234
+ Args:
235
+ flag_values: FlagValues, the FlagValues instance with which the flag will be
236
+ saved. This should almost never need to be overridden.
237
+
238
+ Returns:
239
+ Dictionary mapping keys to values. Keys are flag names, values are
240
+ corresponding ``__dict__`` members. E.g. ``{'key': value_dict, ...}``.
241
+ """
242
+ return {name: _copy_flag_dict(flag_values[name]) for name in flag_values}
243
+
244
+
245
+ def restore_flag_values(
246
+ saved_flag_values: Mapping[str, dict[str, Any]],
247
+ flag_values: flags.FlagValues = FLAGS,
248
+ ) -> None:
249
+ """Restores flag values based on the dictionary of flag values.
250
+
251
+ Args:
252
+ saved_flag_values: {'flag_name': value_dict, ...}
253
+ flag_values: FlagValues, the FlagValues instance from which the flag will be
254
+ restored. This should almost never need to be overridden.
255
+ """
256
+ new_flag_names = list(flag_values)
257
+ for name in new_flag_names:
258
+ saved = saved_flag_values.get(name)
259
+ if saved is None:
260
+ # If __dict__ was not saved delete "new" flag.
261
+ delattr(flag_values, name)
262
+ else:
263
+ if flag_values[name].value != saved['_value']:
264
+ flag_values[name].value = saved['_value'] # Ensure C++ value is set.
265
+ flag_values[name].__dict__ = saved
266
+
267
+
268
+ @overload
269
+ def _wrap(
270
+ flag_overrider_cls: type['_FlagOverrider'],
271
+ func: _CallableT,
272
+ overrides: Mapping[str, Any],
273
+ ) -> _CallableT:
274
+ ...
275
+
276
+
277
+ @overload
278
+ def _wrap(
279
+ flag_overrider_cls: type['_ParsingFlagOverrider'],
280
+ func: _CallableT,
281
+ overrides: Mapping[str, str | Sequence[str]],
282
+ ) -> _CallableT:
283
+ ...
284
+
285
+
286
+ def _wrap(flag_overrider_cls, func, overrides):
287
+ """Creates a wrapper function that saves/restores flag values.
288
+
289
+ Args:
290
+ flag_overrider_cls: The class that will be used as a context manager.
291
+ func: This will be called between saving flags and restoring flags.
292
+ overrides: Flag names mapped to their values. These flags will be set after
293
+ saving the original flag state. The type of the values depends on if
294
+ _FlagOverrider or _ParsingFlagOverrider was specified.
295
+
296
+ Returns:
297
+ A wrapped version of func.
298
+ """
299
+
300
+ @functools.wraps(func)
301
+ def _flagsaver_wrapper(*args, **kwargs):
302
+ """Wrapper function that saves and restores flags."""
303
+ with flag_overrider_cls(**overrides):
304
+ return func(*args, **kwargs)
305
+
306
+ return _flagsaver_wrapper
307
+
308
+
309
+ class _FlagOverrider:
310
+ """Overrides flags for the duration of the decorated function call.
311
+
312
+ It also restores all original values of flags after decorated method
313
+ completes.
314
+ """
315
+
316
+ def __init__(self, **overrides: Any):
317
+ self._overrides = overrides
318
+ self._saved_flag_values = None
319
+
320
+ def __call__(self, func: _CallableT) -> _CallableT:
321
+ if inspect.isclass(func):
322
+ raise TypeError('flagsaver cannot be applied to a class.')
323
+ return _wrap(self.__class__, func, self._overrides)
324
+
325
+ def __enter__(self):
326
+ self._saved_flag_values = save_flag_values(FLAGS)
327
+ try:
328
+ FLAGS._set_attributes(**self._overrides)
329
+ except:
330
+ # It may fail because of flag validators.
331
+ restore_flag_values(self._saved_flag_values, FLAGS)
332
+ raise
333
+
334
+ def __exit__(self, exc_type, exc_value, traceback):
335
+ restore_flag_values(self._saved_flag_values, FLAGS)
336
+
337
+
338
+ class _ParsingFlagOverrider(_FlagOverrider):
339
+ """Context manager for overriding flags.
340
+
341
+ Simulates command line parsing.
342
+
343
+ This is simlar to _FlagOverrider except that all **overrides should be
344
+ strings or sequences of strings, and when context is entered this class calls
345
+ .parse(value)
346
+
347
+ This results in the flags having .present set properly.
348
+ """
349
+
350
+ def __init__(self, **overrides: str | Sequence[str]):
351
+ for flag_name, new_value in overrides.items():
352
+ if isinstance(new_value, str):
353
+ continue
354
+ if isinstance(new_value, Sequence) and all(
355
+ isinstance(single_value, str) for single_value in new_value
356
+ ):
357
+ continue
358
+ raise TypeError(
359
+ f'flagsaver.as_parsed() cannot parse {flag_name}. Expected a single '
360
+ f'string or sequence of strings but {type(new_value)} was provided.')
361
+ super().__init__(**overrides)
362
+
363
+ def __enter__(self):
364
+ self._saved_flag_values = save_flag_values(FLAGS)
365
+ try:
366
+ for flag_name, unparsed_value in self._overrides.items():
367
+ # LINT.IfChange(flag_override_parsing)
368
+ FLAGS[flag_name].parse(unparsed_value)
369
+ FLAGS[flag_name].using_default_value = False
370
+ # LINT.ThenChange()
371
+
372
+ # Perform the validation on all modified flags. This is something that
373
+ # FLAGS._set_attributes() does for you in _FlagOverrider.
374
+ for flag_name in self._overrides:
375
+ FLAGS._assert_validators(FLAGS[flag_name].validators)
376
+
377
+ except KeyError as e:
378
+ # If a flag doesn't exist, an UnrecognizedFlagError is more specific.
379
+ restore_flag_values(self._saved_flag_values, FLAGS)
380
+ raise flags.UnrecognizedFlagError('Unknown command line flag.') from e
381
+
382
+ except:
383
+ # It may fail because of flag validators or general parsing issues.
384
+ restore_flag_values(self._saved_flag_values, FLAGS)
385
+ raise
386
+
387
+
388
+ def _copy_flag_dict(flag: flags.Flag) -> dict[str, Any]:
389
+ """Returns a copy of the flag object's ``__dict__``.
390
+
391
+ It's mostly a shallow copy of the ``__dict__``, except it also does a shallow
392
+ copy of the validator list.
393
+
394
+ Args:
395
+ flag: flags.Flag, the flag to copy.
396
+
397
+ Returns:
398
+ A copy of the flag object's ``__dict__``.
399
+ """
400
+ copy = flag.__dict__.copy()
401
+ copy['_value'] = flag.value # Ensure correct restore for C++ flags.
402
+ copy['validators'] = list(flag.validators)
403
+ return copy
lib/python3.12/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.12/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
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
+ '"': '&quot;',
40
+ "'": '&apos;',
41
+ '\n': '&#xA;',
42
+ '\t': '&#x9;',
43
+ '\r': '&#xD;',
44
+ ' ': '&#x20;'}
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.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: Jinja2
3
+ Version: 3.1.6
4
+ Summary: A very fast and expressive template engine.
5
+ Maintainer-email: Pallets <contact@palletsprojects.com>
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: Environment :: Web Environment
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: BSD License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
15
+ Classifier: Topic :: Text Processing :: Markup :: HTML
16
+ Classifier: Typing :: Typed
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: MarkupSafe>=2.0
19
+ Requires-Dist: Babel>=2.7 ; extra == "i18n"
20
+ Project-URL: Changes, https://jinja.palletsprojects.com/changes/
21
+ Project-URL: Chat, https://discord.gg/pallets
22
+ Project-URL: Documentation, https://jinja.palletsprojects.com/
23
+ Project-URL: Donate, https://palletsprojects.com/donate
24
+ Project-URL: Source, https://github.com/pallets/jinja/
25
+ Provides-Extra: i18n
26
+
27
+ # Jinja
28
+
29
+ Jinja is a fast, expressive, extensible templating engine. Special
30
+ placeholders in the template allow writing code similar to Python
31
+ syntax. Then the template is passed data to render the final document.
32
+
33
+ It includes:
34
+
35
+ - Template inheritance and inclusion.
36
+ - Define and import macros within templates.
37
+ - HTML templates can use autoescaping to prevent XSS from untrusted
38
+ user input.
39
+ - A sandboxed environment can safely render untrusted templates.
40
+ - AsyncIO support for generating templates and calling async
41
+ functions.
42
+ - I18N support with Babel.
43
+ - Templates are compiled to optimized Python code just-in-time and
44
+ cached, or can be compiled ahead-of-time.
45
+ - Exceptions point to the correct line in templates to make debugging
46
+ easier.
47
+ - Extensible filters, tests, functions, and even syntax.
48
+
49
+ Jinja's philosophy is that while application logic belongs in Python if
50
+ possible, it shouldn't make the template designer's job difficult by
51
+ restricting functionality too much.
52
+
53
+
54
+ ## In A Nutshell
55
+
56
+ ```jinja
57
+ {% extends "base.html" %}
58
+ {% block title %}Members{% endblock %}
59
+ {% block content %}
60
+ <ul>
61
+ {% for user in users %}
62
+ <li><a href="{{ user.url }}">{{ user.username }}</a></li>
63
+ {% endfor %}
64
+ </ul>
65
+ {% endblock %}
66
+ ```
67
+
68
+ ## Donate
69
+
70
+ The Pallets organization develops and supports Jinja and other popular
71
+ packages. In order to grow the community of contributors and users, and
72
+ allow the maintainers to devote more time to the projects, [please
73
+ donate today][].
74
+
75
+ [please donate today]: https://palletsprojects.com/donate
76
+
77
+ ## Contributing
78
+
79
+ See our [detailed contributing documentation][contrib] for many ways to
80
+ contribute, including reporting issues, requesting features, asking or answering
81
+ questions, and making PRs.
82
+
83
+ [contrib]: https://palletsprojects.com/contributing/
84
+
lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jinja2-3.1.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2
+ jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871
3
+ jinja2-3.1.6.dist-info/RECORD,,
4
+ jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
5
+ jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58
6
+ jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475
7
+ jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928
8
+ jinja2/__pycache__/__init__.cpython-312.pyc,,
9
+ jinja2/__pycache__/_identifier.cpython-312.pyc,,
10
+ jinja2/__pycache__/async_utils.cpython-312.pyc,,
11
+ jinja2/__pycache__/bccache.cpython-312.pyc,,
12
+ jinja2/__pycache__/compiler.cpython-312.pyc,,
13
+ jinja2/__pycache__/constants.cpython-312.pyc,,
14
+ jinja2/__pycache__/debug.cpython-312.pyc,,
15
+ jinja2/__pycache__/defaults.cpython-312.pyc,,
16
+ jinja2/__pycache__/environment.cpython-312.pyc,,
17
+ jinja2/__pycache__/exceptions.cpython-312.pyc,,
18
+ jinja2/__pycache__/ext.cpython-312.pyc,,
19
+ jinja2/__pycache__/filters.cpython-312.pyc,,
20
+ jinja2/__pycache__/idtracking.cpython-312.pyc,,
21
+ jinja2/__pycache__/lexer.cpython-312.pyc,,
22
+ jinja2/__pycache__/loaders.cpython-312.pyc,,
23
+ jinja2/__pycache__/meta.cpython-312.pyc,,
24
+ jinja2/__pycache__/nativetypes.cpython-312.pyc,,
25
+ jinja2/__pycache__/nodes.cpython-312.pyc,,
26
+ jinja2/__pycache__/optimizer.cpython-312.pyc,,
27
+ jinja2/__pycache__/parser.cpython-312.pyc,,
28
+ jinja2/__pycache__/runtime.cpython-312.pyc,,
29
+ jinja2/__pycache__/sandbox.cpython-312.pyc,,
30
+ jinja2/__pycache__/tests.cpython-312.pyc,,
31
+ jinja2/__pycache__/utils.cpython-312.pyc,,
32
+ jinja2/__pycache__/visitor.cpython-312.pyc,,
33
+ jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958
34
+ jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834
35
+ jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061
36
+ jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131
37
+ jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433
38
+ jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297
39
+ jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267
40
+ jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513
41
+ jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071
42
+ jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875
43
+ jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212
44
+ jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555
45
+ jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786
46
+ jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055
47
+ jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397
48
+ jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210
49
+ jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579
50
+ jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651
51
+ jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383
52
+ jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249
54
+ jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009
55
+ jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926
56
+ jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129
57
+ jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557
lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: flit 3.11.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any