JustinTX commited on
Commit
127c4ec
·
verified ·
1 Parent(s): 834d2d4

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. .venv/lib/python3.13/site-packages/absl/__pycache__/__init__.cpython-313.pyc +0 -0
  2. .venv/lib/python3.13/site-packages/absl/__pycache__/app.cpython-313.pyc +0 -0
  3. .venv/lib/python3.13/site-packages/absl/__pycache__/command_name.cpython-313.pyc +0 -0
  4. .venv/lib/python3.13/site-packages/absl/flags/__init__.py +220 -0
  5. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/__init__.cpython-313.pyc +0 -0
  6. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_argument_parser.cpython-313.pyc +0 -0
  7. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_defines.cpython-313.pyc +0 -0
  8. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_exceptions.cpython-313.pyc +0 -0
  9. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_flag.cpython-313.pyc +0 -0
  10. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_flagvalues.cpython-313.pyc +0 -0
  11. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_helpers.cpython-313.pyc +0 -0
  12. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_validators.cpython-313.pyc +0 -0
  13. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/_validators_classes.cpython-313.pyc +0 -0
  14. .venv/lib/python3.13/site-packages/absl/flags/__pycache__/argparse_flags.cpython-313.pyc +0 -0
  15. .venv/lib/python3.13/site-packages/absl/flags/_argument_parser.py +632 -0
  16. .venv/lib/python3.13/site-packages/absl/flags/_defines.py +1702 -0
  17. .venv/lib/python3.13/site-packages/absl/flags/_exceptions.py +107 -0
  18. .venv/lib/python3.13/site-packages/absl/flags/_flag.py +566 -0
  19. .venv/lib/python3.13/site-packages/absl/flags/_flagvalues.py +1552 -0
  20. .venv/lib/python3.13/site-packages/absl/flags/_helpers.py +403 -0
  21. .venv/lib/python3.13/site-packages/absl/flags/_validators.py +353 -0
  22. .venv/lib/python3.13/site-packages/absl/flags/_validators_classes.py +172 -0
  23. .venv/lib/python3.13/site-packages/absl/flags/argparse_flags.py +390 -0
  24. .venv/lib/python3.13/site-packages/absl/logging/__init__.py +1335 -0
  25. .venv/lib/python3.13/site-packages/absl/logging/__init__.pyi +278 -0
  26. .venv/lib/python3.13/site-packages/absl/logging/__pycache__/__init__.cpython-313.pyc +0 -0
  27. .venv/lib/python3.13/site-packages/absl/logging/__pycache__/converter.cpython-313.pyc +0 -0
  28. .venv/lib/python3.13/site-packages/absl/logging/converter.py +214 -0
  29. .venv/lib/python3.13/site-packages/absl/testing/__init__.py +13 -0
  30. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/__init__.cpython-313.pyc +0 -0
  31. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-313.pyc +0 -0
  32. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-313.pyc +0 -0
  33. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/flagsaver.cpython-313.pyc +0 -0
  34. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/parameterized.cpython-313.pyc +0 -0
  35. .venv/lib/python3.13/site-packages/absl/testing/__pycache__/xml_reporter.cpython-313.pyc +0 -0
  36. .venv/lib/python3.13/site-packages/absl/testing/_bazelize_command.py +68 -0
  37. .venv/lib/python3.13/site-packages/absl/testing/_pretty_print_reporter.py +92 -0
  38. .venv/lib/python3.13/site-packages/absl/testing/absltest.py +0 -0
  39. .venv/lib/python3.13/site-packages/absl/testing/flagsaver.py +403 -0
  40. .venv/lib/python3.13/site-packages/absl/testing/parameterized.py +726 -0
  41. .venv/lib/python3.13/site-packages/absl/testing/xml_reporter.py +570 -0
  42. .venv/lib/python3.13/site-packages/attrs/__pycache__/__init__.cpython-313.pyc +0 -0
  43. .venv/lib/python3.13/site-packages/attrs/__pycache__/converters.cpython-313.pyc +0 -0
  44. .venv/lib/python3.13/site-packages/attrs/__pycache__/exceptions.cpython-313.pyc +0 -0
  45. .venv/lib/python3.13/site-packages/attrs/__pycache__/filters.cpython-313.pyc +0 -0
  46. .venv/lib/python3.13/site-packages/attrs/__pycache__/setters.cpython-313.pyc +0 -0
  47. .venv/lib/python3.13/site-packages/attrs/__pycache__/validators.cpython-313.pyc +0 -0
  48. .venv/lib/python3.13/site-packages/black/resources/__init__.cpython-313-x86_64-linux-gnu.so +0 -0
  49. .venv/lib/python3.13/site-packages/black/resources/__init__.py +0 -0
  50. .venv/lib/python3.13/site-packages/black/resources/black.schema.json +161 -0
.venv/lib/python3.13/site-packages/absl/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (207 Bytes). View file
 
.venv/lib/python3.13/site-packages/absl/__pycache__/app.cpython-313.pyc ADDED
Binary file (21.2 kB). View file
 
.venv/lib/python3.13/site-packages/absl/__pycache__/command_name.cpython-313.pyc ADDED
Binary file (2.34 kB). View file
 
.venv/lib/python3.13/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
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (5.75 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_argument_parser.cpython-313.pyc ADDED
Binary file (30.8 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_defines.cpython-313.pyc ADDED
Binary file (53.2 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_exceptions.cpython-313.pyc ADDED
Binary file (4.68 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_flag.cpython-313.pyc ADDED
Binary file (26.4 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_flagvalues.cpython-313.pyc ADDED
Binary file (62.8 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_helpers.cpython-313.pyc ADDED
Binary file (13.5 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_validators.cpython-313.pyc ADDED
Binary file (15.1 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/_validators_classes.cpython-313.pyc ADDED
Binary file (7.6 kB). View file
 
.venv/lib/python3.13/site-packages/absl/flags/__pycache__/argparse_flags.cpython-313.pyc ADDED
Binary file (15.1 kB). View file
 
.venv/lib/python3.13/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
.venv/lib/python3.13/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)
.venv/lib/python3.13/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."""
.venv/lib/python3.13/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
.venv/lib/python3.13/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
.venv/lib/python3.13/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
.venv/lib/python3.13/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)
.venv/lib/python3.13/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
.venv/lib/python3.13/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
.venv/lib/python3.13/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()
.venv/lib/python3.13/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
+ ...
.venv/lib/python3.13/site-packages/absl/logging/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (54.5 kB). View file
 
.venv/lib/python3.13/site-packages/absl/logging/__pycache__/converter.cpython-313.pyc ADDED
Binary file (6.15 kB). View file
 
.venv/lib/python3.13/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))
.venv/lib/python3.13/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.
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (187 Bytes). View file
 
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/_bazelize_command.cpython-313.pyc ADDED
Binary file (2.38 kB). View file
 
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/_pretty_print_reporter.cpython-313.pyc ADDED
Binary file (4.73 kB). View file
 
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/flagsaver.cpython-313.pyc ADDED
Binary file (16.1 kB). View file
 
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/parameterized.cpython-313.pyc ADDED
Binary file (30.2 kB). View file
 
.venv/lib/python3.13/site-packages/absl/testing/__pycache__/xml_reporter.cpython-313.pyc ADDED
Binary file (27.4 kB). View file
 
.venv/lib/python3.13/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)
.venv/lib/python3.13/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)
.venv/lib/python3.13/site-packages/absl/testing/absltest.py ADDED
The diff for this file is too large to render. See raw diff
 
.venv/lib/python3.13/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
.venv/lib/python3.13/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
.venv/lib/python3.13/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
.venv/lib/python3.13/site-packages/attrs/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (1.21 kB). View file
 
.venv/lib/python3.13/site-packages/attrs/__pycache__/converters.cpython-313.pyc ADDED
Binary file (221 Bytes). View file
 
.venv/lib/python3.13/site-packages/attrs/__pycache__/exceptions.cpython-313.pyc ADDED
Binary file (221 Bytes). View file
 
.venv/lib/python3.13/site-packages/attrs/__pycache__/filters.cpython-313.pyc ADDED
Binary file (215 Bytes). View file
 
.venv/lib/python3.13/site-packages/attrs/__pycache__/setters.cpython-313.pyc ADDED
Binary file (215 Bytes). View file
 
.venv/lib/python3.13/site-packages/attrs/__pycache__/validators.cpython-313.pyc ADDED
Binary file (221 Bytes). View file
 
.venv/lib/python3.13/site-packages/black/resources/__init__.cpython-313-x86_64-linux-gnu.so ADDED
Binary file (16 kB). View file
 
.venv/lib/python3.13/site-packages/black/resources/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/black/resources/black.schema.json ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/psf/black/blob/main/src/black/resources/black.schema.json",
4
+ "$comment": "tool.black table in pyproject.toml",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "code": {
9
+ "type": "string",
10
+ "description": "Format the code passed in as a string."
11
+ },
12
+ "line-length": {
13
+ "type": "integer",
14
+ "description": "How many characters per line to allow.",
15
+ "default": 88
16
+ },
17
+ "target-version": {
18
+ "type": "array",
19
+ "items": {
20
+ "enum": [
21
+ "py33",
22
+ "py34",
23
+ "py35",
24
+ "py36",
25
+ "py37",
26
+ "py38",
27
+ "py39",
28
+ "py310",
29
+ "py311",
30
+ "py312",
31
+ "py313",
32
+ "py314"
33
+ ]
34
+ },
35
+ "description": "Python versions that should be supported by Black's output. You should include all versions that your code supports. By default, Black will infer target versions from the project metadata in pyproject.toml. If this does not yield conclusive results, Black will use per-file auto-detection."
36
+ },
37
+ "pyi": {
38
+ "type": "boolean",
39
+ "description": "Format all input files like typing stubs regardless of file extension. This is useful when piping source on standard input.",
40
+ "default": false
41
+ },
42
+ "ipynb": {
43
+ "type": "boolean",
44
+ "description": "Format all input files like Jupyter Notebooks regardless of file extension. This is useful when piping source on standard input.",
45
+ "default": false
46
+ },
47
+ "python-cell-magics": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "string"
51
+ },
52
+ "description": "When processing Jupyter Notebooks, add the given magic to the list of known python-magics (capture, prun, pypy, python, python3, time, timeit). Useful for formatting cells with custom python magics."
53
+ },
54
+ "skip-source-first-line": {
55
+ "type": "boolean",
56
+ "description": "Skip the first line of the source code.",
57
+ "default": false
58
+ },
59
+ "skip-string-normalization": {
60
+ "type": "boolean",
61
+ "description": "Don't normalize string quotes or prefixes.",
62
+ "default": false
63
+ },
64
+ "skip-magic-trailing-comma": {
65
+ "type": "boolean",
66
+ "description": "Don't use trailing commas as a reason to split lines.",
67
+ "default": false
68
+ },
69
+ "preview": {
70
+ "type": "boolean",
71
+ "description": "Enable potentially disruptive style changes that may be added to Black's main functionality in the next major release.",
72
+ "default": false
73
+ },
74
+ "unstable": {
75
+ "type": "boolean",
76
+ "description": "Enable potentially disruptive style changes that have known bugs or are not currently expected to make it into the stable style Black's next major release. Implies --preview.",
77
+ "default": false
78
+ },
79
+ "enable-unstable-feature": {
80
+ "type": "array",
81
+ "items": {
82
+ "enum": [
83
+ "string_processing",
84
+ "hug_parens_with_braces_and_square_brackets",
85
+ "wrap_long_dict_values_in_parens",
86
+ "multiline_string_handling",
87
+ "always_one_newline_after_import",
88
+ "fix_fmt_skip_in_one_liners",
89
+ "standardize_type_comments",
90
+ "wrap_comprehension_in",
91
+ "remove_parens_around_except_types",
92
+ "normalize_cr_newlines",
93
+ "fix_module_docstring_detection",
94
+ "fix_type_expansion_split",
95
+ "remove_parens_from_assignment_lhs"
96
+ ]
97
+ },
98
+ "description": "Enable specific features included in the `--unstable` style. Requires `--preview`. No compatibility guarantees are provided on the behavior or existence of any unstable features."
99
+ },
100
+ "check": {
101
+ "type": "boolean",
102
+ "description": "Don't write the files back, just return the status. Return code 0 means nothing would change. Return code 1 means some files would be reformatted. Return code 123 means there was an internal error.",
103
+ "default": false
104
+ },
105
+ "diff": {
106
+ "type": "boolean",
107
+ "description": "Don't write the files back, just output a diff to indicate what changes Black would've made. They are printed to stdout so capturing them is simple.",
108
+ "default": false
109
+ },
110
+ "color": {
111
+ "type": "boolean",
112
+ "description": "Show (or do not show) colored diff. Only applies when --diff is given.",
113
+ "default": false
114
+ },
115
+ "fast": {
116
+ "type": "boolean",
117
+ "description": "By default, Black performs an AST safety check after formatting your code. The --fast flag turns off this check and the --safe flag explicitly enables it. [default: --safe]",
118
+ "default": false
119
+ },
120
+ "required-version": {
121
+ "type": "string",
122
+ "description": "Require a specific version of Black to be running. This is useful for ensuring that all contributors to your project are using the same version, because different versions of Black may format code a little differently. This option can be set in a configuration file for consistent results across environments."
123
+ },
124
+ "exclude": {
125
+ "type": "string",
126
+ "description": "A regular expression that matches files and directories that should be excluded on recursive searches. An empty value means no paths are excluded. Use forward slashes for directories on all platforms (Windows, too). By default, Black also ignores all paths listed in .gitignore. Changing this value will override all default exclusions. [default: /(\\.direnv|\\.eggs|\\.git|\\.hg|\\.ipynb_checkpoints|\\.mypy_cache|\\.nox|\\.pytest_cache|\\.ruff_cache|\\.tox|\\.svn|\\.venv|\\.vscode|__pypackages__|_build|buck-out|build|dist|venv)/]"
127
+ },
128
+ "extend-exclude": {
129
+ "type": "string",
130
+ "description": "Like --exclude, but adds additional files and directories on top of the default values instead of overriding them."
131
+ },
132
+ "force-exclude": {
133
+ "type": "string",
134
+ "description": "Like --exclude, but files and directories matching this regex will be excluded even when they are passed explicitly as arguments. This is useful when invoking Black programmatically on changed files, such as in a pre-commit hook or editor plugin."
135
+ },
136
+ "include": {
137
+ "type": "string",
138
+ "description": "A regular expression that matches files and directories that should be included on recursive searches. An empty value means all files are included regardless of the name. Use forward slashes for directories on all platforms (Windows, too). Overrides all exclusions, including from .gitignore and command line options.",
139
+ "default": "(\\.pyi?|\\.ipynb)$"
140
+ },
141
+ "workers": {
142
+ "type": "integer",
143
+ "description": "When Black formats multiple files, it may use a process pool to speed up formatting. This option controls the number of parallel workers. This can also be specified via the BLACK_NUM_WORKERS environment variable. Defaults to the number of CPUs in the system."
144
+ },
145
+ "quiet": {
146
+ "type": "boolean",
147
+ "description": "Stop emitting all non-critical output. Error messages will still be emitted (which can silenced by 2>/dev/null).",
148
+ "default": false
149
+ },
150
+ "verbose": {
151
+ "type": "boolean",
152
+ "description": "Emit messages about files that were not changed or were ignored due to exclusion patterns. If Black is using a configuration file, a message detailing which one it is using will be emitted.",
153
+ "default": false
154
+ },
155
+ "no-cache": {
156
+ "type": "boolean",
157
+ "description": "Skip reading and writing the cache, forcing Black to reformat all included files.",
158
+ "default": false
159
+ }
160
+ }
161
+ }