Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/absl/flags/_flagvalues.py: 24%

592 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-05 06:32 +0000

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 

16Do NOT import this module directly. Import the flags package and use the 

17aliases defined at the package level instead. 

18""" 

19 

20import copy 

21import itertools 

22import logging 

23import os 

24import sys 

25from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Text, TextIO, Generic, TypeVar, Union, Tuple 

26from xml.dom import minidom 

27 

28from absl.flags import _exceptions 

29from absl.flags import _flag 

30from absl.flags import _helpers 

31from absl.flags import _validators_classes 

32from absl.flags._flag import Flag 

33 

34# Add flagvalues module to disclaimed module ids. 

35_helpers.disclaim_module_ids.add(id(sys.modules[__name__])) 

36 

37_T = TypeVar('_T') 

38 

39 

40class FlagValues: 

41 """Registry of :class:`~absl.flags.Flag` objects. 

42 

43 A :class:`FlagValues` can then scan command line arguments, passing flag 

44 arguments through to the 'Flag' objects that it owns. It also 

45 provides easy access to the flag values. Typically only one 

46 :class:`FlagValues` object is needed by an application: 

47 :const:`FLAGS`. 

48 

49 This class is heavily overloaded: 

50 

51 :class:`Flag` objects are registered via ``__setitem__``:: 

52 

53 FLAGS['longname'] = x # register a new flag 

54 

55 The ``.value`` attribute of the registered :class:`~absl.flags.Flag` objects 

56 can be accessed as attributes of this :class:`FlagValues` object, through 

57 ``__getattr__``. Both the long and short name of the original 

58 :class:`~absl.flags.Flag` objects can be used to access its value:: 

59 

60 FLAGS.longname # parsed flag value 

61 FLAGS.x # parsed flag value (short name) 

62 

63 Command line arguments are scanned and passed to the registered 

64 :class:`~absl.flags.Flag` objects through the ``__call__`` method. Unparsed 

65 arguments, including ``argv[0]`` (e.g. the program name) are returned:: 

66 

67 argv = FLAGS(sys.argv) # scan command line arguments 

68 

69 The original registered :class:`~absl.flags.Flag` objects can be retrieved 

70 through the use of the dictionary-like operator, ``__getitem__``:: 

71 

72 x = FLAGS['longname'] # access the registered Flag object 

73 

74 The ``str()`` operator of a :class:`absl.flags.FlagValues` object provides 

75 help for all of the registered :class:`~absl.flags.Flag` objects. 

76 """ 

77 

78 _HAS_DYNAMIC_ATTRIBUTES = True 

79 

80 # A note on collections.abc.Mapping: 

81 # FlagValues defines __getitem__, __iter__, and __len__. It makes perfect 

82 # sense to let it be a collections.abc.Mapping class. However, we are not 

83 # able to do so. The mixin methods, e.g. keys, values, are not uncommon flag 

84 # names. Those flag values would not be accessible via the FLAGS.xxx form. 

85 

86 __dict__: Dict[str, Any] 

87 

88 def __init__(self): 

89 # Since everything in this class is so heavily overloaded, the only 

90 # way of defining and using fields is to access __dict__ directly. 

91 

92 # Dictionary: flag name (string) -> Flag object. 

93 self.__dict__['__flags'] = {} 

94 

95 # Set: name of hidden flag (string). 

96 # Holds flags that should not be directly accessible from Python. 

97 self.__dict__['__hiddenflags'] = set() 

98 

99 # Dictionary: module name (string) -> list of Flag objects that are defined 

100 # by that module. 

101 self.__dict__['__flags_by_module'] = {} 

102 # Dictionary: module id (int) -> list of Flag objects that are defined by 

103 # that module. 

104 self.__dict__['__flags_by_module_id'] = {} 

105 # Dictionary: module name (string) -> list of Flag objects that are 

106 # key for that module. 

107 self.__dict__['__key_flags_by_module'] = {} 

108 

109 # Bool: True if flags were parsed. 

110 self.__dict__['__flags_parsed'] = False 

111 

112 # Bool: True if unparse_flags() was called. 

113 self.__dict__['__unparse_flags_called'] = False 

114 

115 # None or Method(name, value) to call from __setattr__ for an unknown flag. 

116 self.__dict__['__set_unknown'] = None 

117 

118 # A set of banned flag names. This is to prevent users from accidentally 

119 # defining a flag that has the same name as a method on this class. 

120 # Users can still allow defining the flag by passing 

121 # allow_using_method_names=True in DEFINE_xxx functions. 

122 self.__dict__['__banned_flag_names'] = frozenset(dir(FlagValues)) 

123 

124 # Bool: Whether to use GNU style scanning. 

125 self.__dict__['__use_gnu_getopt'] = True 

126 

127 # Bool: Whether use_gnu_getopt has been explicitly set by the user. 

128 self.__dict__['__use_gnu_getopt_explicitly_set'] = False 

129 

130 # Function: Takes a flag name as parameter, returns a tuple 

131 # (is_retired, type_is_bool). 

132 self.__dict__['__is_retired_flag_func'] = None 

133 

134 def set_gnu_getopt(self, gnu_getopt: bool = True) -> None: 

135 """Sets whether or not to use GNU style scanning. 

136 

137 GNU style allows mixing of flag and non-flag arguments. See 

138 http://docs.python.org/library/getopt.html#getopt.gnu_getopt 

139 

140 Args: 

141 gnu_getopt: bool, whether or not to use GNU style scanning. 

142 """ 

143 self.__dict__['__use_gnu_getopt'] = gnu_getopt 

144 self.__dict__['__use_gnu_getopt_explicitly_set'] = True 

145 

146 def is_gnu_getopt(self) -> bool: 

147 return self.__dict__['__use_gnu_getopt'] 

148 

149 def _flags(self) -> Dict[Text, Flag]: 

150 return self.__dict__['__flags'] 

151 

152 def flags_by_module_dict(self) -> Dict[Text, List[Flag]]: 

153 """Returns the dictionary of module_name -> list of defined flags. 

154 

155 Returns: 

156 A dictionary. Its keys are module names (strings). Its values 

157 are lists of Flag objects. 

158 """ 

159 return self.__dict__['__flags_by_module'] 

160 

161 def flags_by_module_id_dict(self) -> Dict[int, List[Flag]]: 

162 """Returns the dictionary of module_id -> list of defined flags. 

163 

164 Returns: 

165 A dictionary. Its keys are module IDs (ints). Its values 

166 are lists of Flag objects. 

167 """ 

168 return self.__dict__['__flags_by_module_id'] 

169 

170 def key_flags_by_module_dict(self) -> Dict[Text, List[Flag]]: 

171 """Returns the dictionary of module_name -> list of key flags. 

172 

173 Returns: 

174 A dictionary. Its keys are module names (strings). Its values 

175 are lists of Flag objects. 

176 """ 

177 return self.__dict__['__key_flags_by_module'] 

178 

179 def register_flag_by_module(self, module_name: Text, flag: Flag) -> None: 

180 """Records the module that defines a specific flag. 

181 

182 We keep track of which flag is defined by which module so that we 

183 can later sort the flags by module. 

184 

185 Args: 

186 module_name: str, the name of a Python module. 

187 flag: Flag, the Flag instance that is key to the module. 

188 """ 

189 flags_by_module = self.flags_by_module_dict() 

190 flags_by_module.setdefault(module_name, []).append(flag) 

191 

192 def register_flag_by_module_id(self, module_id: int, flag: Flag) -> None: 

193 """Records the module that defines a specific flag. 

194 

195 Args: 

196 module_id: int, the ID of the Python module. 

197 flag: Flag, the Flag instance that is key to the module. 

198 """ 

199 flags_by_module_id = self.flags_by_module_id_dict() 

200 flags_by_module_id.setdefault(module_id, []).append(flag) 

201 

202 def register_key_flag_for_module(self, module_name: Text, flag: Flag) -> None: 

203 """Specifies that a flag is a key flag for a module. 

204 

205 Args: 

206 module_name: str, the name of a Python module. 

207 flag: Flag, the Flag instance that is key to the module. 

208 """ 

209 key_flags_by_module = self.key_flags_by_module_dict() 

210 # The list of key flags for the module named module_name. 

211 key_flags = key_flags_by_module.setdefault(module_name, []) 

212 # Add flag, but avoid duplicates. 

213 if flag not in key_flags: 

214 key_flags.append(flag) 

215 

216 def _flag_is_registered(self, flag_obj: Flag) -> bool: 

217 """Checks whether a Flag object is registered under long name or short name. 

218 

219 Args: 

220 flag_obj: Flag, the Flag instance to check for. 

221 

222 Returns: 

223 bool, True iff flag_obj is registered under long name or short name. 

224 """ 

225 flag_dict = self._flags() 

226 # Check whether flag_obj is registered under its long name. 

227 name = flag_obj.name 

228 if flag_dict.get(name, None) == flag_obj: 

229 return True 

230 # Check whether flag_obj is registered under its short name. 

231 short_name = flag_obj.short_name 

232 if (short_name is not None and flag_dict.get(short_name, None) == flag_obj): 

233 return True 

234 return False 

235 

236 def _cleanup_unregistered_flag_from_module_dicts( 

237 self, flag_obj: Flag 

238 ) -> None: 

239 """Cleans up unregistered flags from all module -> [flags] dictionaries. 

240 

241 If flag_obj is registered under either its long name or short name, it 

242 won't be removed from the dictionaries. 

243 

244 Args: 

245 flag_obj: Flag, the Flag instance to clean up for. 

246 """ 

247 if self._flag_is_registered(flag_obj): 

248 return 

249 for flags_by_module_dict in (self.flags_by_module_dict(), 

250 self.flags_by_module_id_dict(), 

251 self.key_flags_by_module_dict()): 

252 for flags_in_module in flags_by_module_dict.values(): 

253 # While (as opposed to if) takes care of multiple occurrences of a 

254 # flag in the list for the same module. 

255 while flag_obj in flags_in_module: 

256 flags_in_module.remove(flag_obj) 

257 

258 def get_flags_for_module(self, module: Union[Text, Any]) -> List[Flag]: 

259 """Returns the list of flags defined by a module. 

260 

261 Args: 

262 module: module|str, the module to get flags from. 

263 

264 Returns: 

265 [Flag], a new list of Flag instances. Caller may update this list as 

266 desired: none of those changes will affect the internals of this 

267 FlagValue instance. 

268 """ 

269 if not isinstance(module, str): 

270 module = module.__name__ 

271 if module == '__main__': 

272 module = sys.argv[0] 

273 

274 return list(self.flags_by_module_dict().get(module, [])) 

275 

276 def get_key_flags_for_module(self, module: Union[Text, Any]) -> List[Flag]: 

277 """Returns the list of key flags for a module. 

278 

279 Args: 

280 module: module|str, the module to get key flags from. 

281 

282 Returns: 

283 [Flag], a new list of Flag instances. Caller may update this list as 

284 desired: none of those changes will affect the internals of this 

285 FlagValue instance. 

286 """ 

287 if not isinstance(module, str): 

288 module = module.__name__ 

289 if module == '__main__': 

290 module = sys.argv[0] 

291 

292 # Any flag is a key flag for the module that defined it. NOTE: 

293 # key_flags is a fresh list: we can update it without affecting the 

294 # internals of this FlagValues object. 

295 key_flags = self.get_flags_for_module(module) 

296 

297 # Take into account flags explicitly declared as key for a module. 

298 for flag in self.key_flags_by_module_dict().get(module, []): 

299 if flag not in key_flags: 

300 key_flags.append(flag) 

301 return key_flags 

302 

303 # TODO(yileiyang): Restrict default to Optional[Text]. 

304 def find_module_defining_flag( 

305 self, flagname: Text, default: Optional[_T] = None 

306 ) -> Union[str, Optional[_T]]: 

307 """Return the name of the module defining this flag, or default. 

308 

309 Args: 

310 flagname: str, name of the flag to lookup. 

311 default: Value to return if flagname is not defined. Defaults to None. 

312 

313 Returns: 

314 The name of the module which registered the flag with this name. 

315 If no such module exists (i.e. no flag with this name exists), 

316 we return default. 

317 """ 

318 registered_flag = self._flags().get(flagname) 

319 if registered_flag is None: 

320 return default 

321 for module, flags in self.flags_by_module_dict().items(): 

322 for flag in flags: 

323 # It must compare the flag with the one in _flags. This is because a 

324 # flag might be overridden only for its long name (or short name), 

325 # and only its short name (or long name) is considered registered. 

326 if (flag.name == registered_flag.name and 

327 flag.short_name == registered_flag.short_name): 

328 return module 

329 return default 

330 

331 # TODO(yileiyang): Restrict default to Optional[Text]. 

332 def find_module_id_defining_flag( 

333 self, flagname: Text, default: Optional[_T] = None 

334 ) -> Union[int, Optional[_T]]: 

335 """Return the ID of the module defining this flag, or default. 

336 

337 Args: 

338 flagname: str, name of the flag to lookup. 

339 default: Value to return if flagname is not defined. Defaults to None. 

340 

341 Returns: 

342 The ID of the module which registered the flag with this name. 

343 If no such module exists (i.e. no flag with this name exists), 

344 we return default. 

345 """ 

346 registered_flag = self._flags().get(flagname) 

347 if registered_flag is None: 

348 return default 

349 for module_id, flags in self.flags_by_module_id_dict().items(): 

350 for flag in flags: 

351 # It must compare the flag with the one in _flags. This is because a 

352 # flag might be overridden only for its long name (or short name), 

353 # and only its short name (or long name) is considered registered. 

354 if (flag.name == registered_flag.name and 

355 flag.short_name == registered_flag.short_name): 

356 return module_id 

357 return default 

358 

359 def _register_unknown_flag_setter( 

360 self, setter: Callable[[str, Any], None] 

361 ) -> None: 

362 """Allow set default values for undefined flags. 

363 

364 Args: 

365 setter: Method(name, value) to call to __setattr__ an unknown flag. Must 

366 raise NameError or ValueError for invalid name/value. 

367 """ 

368 self.__dict__['__set_unknown'] = setter 

369 

370 def _set_unknown_flag(self, name: str, value: _T) -> _T: 

371 """Returns value if setting flag |name| to |value| returned True. 

372 

373 Args: 

374 name: str, name of the flag to set. 

375 value: Value to set. 

376 

377 Returns: 

378 Flag value on successful call. 

379 

380 Raises: 

381 UnrecognizedFlagError 

382 IllegalFlagValueError 

383 """ 

384 setter = self.__dict__['__set_unknown'] 

385 if setter: 

386 try: 

387 setter(name, value) 

388 return value 

389 except (TypeError, ValueError): # Flag value is not valid. 

390 raise _exceptions.IllegalFlagValueError( 

391 '"{1}" is not valid for --{0}'.format(name, value)) 

392 except NameError: # Flag name is not valid. 

393 pass 

394 raise _exceptions.UnrecognizedFlagError(name, value) 

395 

396 def append_flag_values(self, flag_values: 'FlagValues') -> None: 

397 """Appends flags registered in another FlagValues instance. 

398 

399 Args: 

400 flag_values: FlagValues, the FlagValues instance from which to copy flags. 

401 """ 

402 for flag_name, flag in flag_values._flags().items(): # pylint: disable=protected-access 

403 # Each flags with short_name appears here twice (once under its 

404 # normal name, and again with its short name). To prevent 

405 # problems (DuplicateFlagError) with double flag registration, we 

406 # perform a check to make sure that the entry we're looking at is 

407 # for its normal name. 

408 if flag_name == flag.name: 

409 try: 

410 self[flag_name] = flag 

411 except _exceptions.DuplicateFlagError: 

412 raise _exceptions.DuplicateFlagError.from_flag( 

413 flag_name, self, other_flag_values=flag_values) 

414 

415 def remove_flag_values( 

416 self, flag_values: 'Union[FlagValues, Iterable[Text]]' 

417 ) -> None: 

418 """Remove flags that were previously appended from another FlagValues. 

419 

420 Args: 

421 flag_values: FlagValues, the FlagValues instance containing flags to 

422 remove. 

423 """ 

424 for flag_name in flag_values: 

425 self.__delattr__(flag_name) 

426 

427 def __setitem__(self, name: Text, flag: Flag) -> None: 

428 """Registers a new flag variable.""" 

429 fl = self._flags() 

430 if not isinstance(flag, _flag.Flag): 

431 raise _exceptions.IllegalFlagValueError( 

432 f'Expect Flag instances, found type {type(flag)}. ' 

433 "Maybe you didn't mean to use FlagValue.__setitem__?") 

434 if not isinstance(name, str): 

435 raise _exceptions.Error('Flag name must be a string') 

436 if not name: 

437 raise _exceptions.Error('Flag name cannot be empty') 

438 if ' ' in name: 

439 raise _exceptions.Error('Flag name cannot contain a space') 

440 self._check_method_name_conflicts(name, flag) 

441 if name in fl and not flag.allow_override and not fl[name].allow_override: 

442 module, module_name = _helpers.get_calling_module_object_and_name() 

443 if (self.find_module_defining_flag(name) == module_name and 

444 id(module) != self.find_module_id_defining_flag(name)): 

445 # If the flag has already been defined by a module with the same name, 

446 # but a different ID, we can stop here because it indicates that the 

447 # module is simply being imported a subsequent time. 

448 return 

449 raise _exceptions.DuplicateFlagError.from_flag(name, self) 

450 # If a new flag overrides an old one, we need to cleanup the old flag's 

451 # modules if it's not registered. 

452 flags_to_cleanup = set() 

453 short_name: str = flag.short_name # pytype: disable=annotation-type-mismatch 

454 if short_name is not None: 

455 if (short_name in fl and not flag.allow_override and 

456 not fl[short_name].allow_override): 

457 raise _exceptions.DuplicateFlagError.from_flag(short_name, self) 

458 if short_name in fl and fl[short_name] != flag: 

459 flags_to_cleanup.add(fl[short_name]) 

460 fl[short_name] = flag 

461 if (name not in fl # new flag 

462 or fl[name].using_default_value or not flag.using_default_value): 

463 if name in fl and fl[name] != flag: 

464 flags_to_cleanup.add(fl[name]) 

465 fl[name] = flag 

466 for f in flags_to_cleanup: 

467 self._cleanup_unregistered_flag_from_module_dicts(f) 

468 

469 def __dir__(self) -> List[Text]: 

470 """Returns list of names of all defined flags. 

471 

472 Useful for TAB-completion in ipython. 

473 

474 Returns: 

475 [str], a list of names of all defined flags. 

476 """ 

477 return sorted(self.__dict__['__flags']) 

478 

479 def __getitem__(self, name: Text) -> Flag: 

480 """Returns the Flag object for the flag --name.""" 

481 return self._flags()[name] 

482 

483 def _hide_flag(self, name): 

484 """Marks the flag --name as hidden.""" 

485 self.__dict__['__hiddenflags'].add(name) 

486 

487 def __getattr__(self, name: Text) -> Any: 

488 """Retrieves the 'value' attribute of the flag --name.""" 

489 fl = self._flags() 

490 if name not in fl: 

491 raise AttributeError(name) 

492 if name in self.__dict__['__hiddenflags']: 

493 raise AttributeError(name) 

494 

495 if self.__dict__['__flags_parsed'] or fl[name].present: 

496 return fl[name].value 

497 else: 

498 raise _exceptions.UnparsedFlagAccessError( 

499 'Trying to access flag --%s before flags were parsed.' % name) 

500 

501 def __setattr__(self, name: Text, value: _T) -> _T: 

502 """Sets the 'value' attribute of the flag --name.""" 

503 self._set_attributes(**{name: value}) 

504 return value 

505 

506 def _set_attributes(self, **attributes: Any) -> None: 

507 """Sets multiple flag values together, triggers validators afterwards.""" 

508 fl = self._flags() 

509 known_flags = set() 

510 for name, value in attributes.items(): 

511 if name in self.__dict__['__hiddenflags']: 

512 raise AttributeError(name) 

513 if name in fl: 

514 fl[name].value = value 

515 known_flags.add(name) 

516 else: 

517 self._set_unknown_flag(name, value) 

518 for name in known_flags: 

519 self._assert_validators(fl[name].validators) 

520 fl[name].using_default_value = False 

521 

522 def validate_all_flags(self) -> None: 

523 """Verifies whether all flags pass validation. 

524 

525 Raises: 

526 AttributeError: Raised if validators work with a non-existing flag. 

527 IllegalFlagValueError: Raised if validation fails for at least one 

528 validator. 

529 """ 

530 all_validators = set() 

531 for flag in self._flags().values(): 

532 all_validators.update(flag.validators) 

533 self._assert_validators(all_validators) 

534 

535 def _assert_validators( 

536 self, validators: Iterable[_validators_classes.Validator] 

537 ) -> None: 

538 """Asserts if all validators in the list are satisfied. 

539 

540 It asserts validators in the order they were created. 

541 

542 Args: 

543 validators: Iterable(validators.Validator), validators to be verified. 

544 

545 Raises: 

546 AttributeError: Raised if validators work with a non-existing flag. 

547 IllegalFlagValueError: Raised if validation fails for at least one 

548 validator. 

549 """ 

550 messages = [] 

551 bad_flags = set() 

552 for validator in sorted( 

553 validators, key=lambda validator: validator.insertion_index): 

554 try: 

555 if isinstance(validator, _validators_classes.SingleFlagValidator): 

556 if validator.flag_name in bad_flags: 

557 continue 

558 elif isinstance(validator, _validators_classes.MultiFlagsValidator): 

559 if bad_flags & set(validator.flag_names): 

560 continue 

561 validator.verify(self) 

562 except _exceptions.ValidationError as e: 

563 if isinstance(validator, _validators_classes.SingleFlagValidator): 

564 bad_flags.add(validator.flag_name) 

565 elif isinstance(validator, _validators_classes.MultiFlagsValidator): 

566 bad_flags.update(set(validator.flag_names)) 

567 message = validator.print_flags_with_values(self) 

568 messages.append('%s: %s' % (message, str(e))) 

569 if messages: 

570 raise _exceptions.IllegalFlagValueError('\n'.join(messages)) 

571 

572 def __delattr__(self, flag_name: Text) -> None: 

573 """Deletes a previously-defined flag from a flag object. 

574 

575 This method makes sure we can delete a flag by using 

576 

577 del FLAGS.<flag_name> 

578 

579 E.g., 

580 

581 flags.DEFINE_integer('foo', 1, 'Integer flag.') 

582 del flags.FLAGS.foo 

583 

584 If a flag is also registered by its the other name (long name or short 

585 name), the other name won't be deleted. 

586 

587 Args: 

588 flag_name: str, the name of the flag to be deleted. 

589 

590 Raises: 

591 AttributeError: Raised when there is no registered flag named flag_name. 

592 """ 

593 fl = self._flags() 

594 if flag_name not in fl: 

595 raise AttributeError(flag_name) 

596 

597 flag_obj = fl[flag_name] 

598 del fl[flag_name] 

599 

600 self._cleanup_unregistered_flag_from_module_dicts(flag_obj) 

601 

602 def set_default(self, name: Text, value: Any) -> None: 

603 """Changes the default value of the named flag object. 

604 

605 The flag's current value is also updated if the flag is currently using 

606 the default value, i.e. not specified in the command line, and not set 

607 by FLAGS.name = value. 

608 

609 Args: 

610 name: str, the name of the flag to modify. 

611 value: The new default value. 

612 

613 Raises: 

614 UnrecognizedFlagError: Raised when there is no registered flag named name. 

615 IllegalFlagValueError: Raised when value is not valid. 

616 """ 

617 fl = self._flags() 

618 if name not in fl: 

619 self._set_unknown_flag(name, value) 

620 return 

621 fl[name]._set_default(value) # pylint: disable=protected-access 

622 self._assert_validators(fl[name].validators) 

623 

624 def __contains__(self, name: Text) -> bool: 

625 """Returns True if name is a value (flag) in the dict.""" 

626 return name in self._flags() 

627 

628 def __len__(self) -> int: 

629 return len(self.__dict__['__flags']) 

630 

631 def __iter__(self) -> Iterator[Text]: 

632 return iter(self._flags()) 

633 

634 def __call__( 

635 self, argv: Sequence[Text], known_only: bool = False 

636 ) -> List[Text]: 

637 """Parses flags from argv; stores parsed flags into this FlagValues object. 

638 

639 All unparsed arguments are returned. 

640 

641 Args: 

642 argv: a tuple/list of strings. 

643 known_only: bool, if True, parse and remove known flags; return the rest 

644 untouched. Unknown flags specified by --undefok are not returned. 

645 

646 Returns: 

647 The list of arguments not parsed as options, including argv[0]. 

648 

649 Raises: 

650 Error: Raised on any parsing error. 

651 TypeError: Raised on passing wrong type of arguments. 

652 ValueError: Raised on flag value parsing error. 

653 """ 

654 if isinstance(argv, (str, bytes)): 

655 raise TypeError( 

656 'argv should be a tuple/list of strings, not bytes or string.') 

657 if not argv: 

658 raise ValueError( 

659 'argv cannot be an empty list, and must contain the program name as ' 

660 'the first element.') 

661 

662 # This pre parses the argv list for --flagfile=<> options. 

663 program_name = argv[0] 

664 args = self.read_flags_from_files(argv[1:], force_gnu=False) 

665 

666 # Parse the arguments. 

667 unknown_flags, unparsed_args = self._parse_args(args, known_only) 

668 

669 # Handle unknown flags by raising UnrecognizedFlagError. 

670 # Note some users depend on us raising this particular error. 

671 for name, value in unknown_flags: 

672 suggestions = _helpers.get_flag_suggestions(name, list(self)) 

673 raise _exceptions.UnrecognizedFlagError( 

674 name, value, suggestions=suggestions) 

675 

676 self.mark_as_parsed() 

677 self.validate_all_flags() 

678 return [program_name] + unparsed_args 

679 

680 def __getstate__(self) -> Any: 

681 raise TypeError("can't pickle FlagValues") 

682 

683 def __copy__(self) -> Any: 

684 raise TypeError('FlagValues does not support shallow copies. ' 

685 'Use absl.testing.flagsaver or copy.deepcopy instead.') 

686 

687 def __deepcopy__(self, memo) -> Any: 

688 result = object.__new__(type(self)) 

689 result.__dict__.update(copy.deepcopy(self.__dict__, memo)) 

690 return result 

691 

692 def _set_is_retired_flag_func(self, is_retired_flag_func): 

693 """Sets a function for checking retired flags. 

694 

695 Do not use it. This is a private absl API used to check retired flags 

696 registered by the absl C++ flags library. 

697 

698 Args: 

699 is_retired_flag_func: Callable(str) -> (bool, bool), a function takes flag 

700 name as parameter, returns a tuple (is_retired, type_is_bool). 

701 """ 

702 self.__dict__['__is_retired_flag_func'] = is_retired_flag_func 

703 

704 def _parse_args( 

705 self, args: List[str], known_only: bool 

706 ) -> Tuple[List[Tuple[Optional[str], Any]], List[str]]: 

707 """Helper function to do the main argument parsing. 

708 

709 This function goes through args and does the bulk of the flag parsing. 

710 It will find the corresponding flag in our flag dictionary, and call its 

711 .parse() method on the flag value. 

712 

713 Args: 

714 args: [str], a list of strings with the arguments to parse. 

715 known_only: bool, if True, parse and remove known flags; return the rest 

716 untouched. Unknown flags specified by --undefok are not returned. 

717 

718 Returns: 

719 A tuple with the following: 

720 unknown_flags: List of (flag name, arg) for flags we don't know about. 

721 unparsed_args: List of arguments we did not parse. 

722 

723 Raises: 

724 Error: Raised on any parsing error. 

725 ValueError: Raised on flag value parsing error. 

726 """ 

727 unparsed_names_and_args = [] # A list of (flag name or None, arg). 

728 undefok = set() 

729 retired_flag_func = self.__dict__['__is_retired_flag_func'] 

730 

731 flag_dict = self._flags() 

732 args = iter(args) 

733 for arg in args: 

734 value = None 

735 

736 def get_value(): 

737 # pylint: disable=cell-var-from-loop 

738 try: 

739 return next(args) if value is None else value 

740 except StopIteration: 

741 raise _exceptions.Error('Missing value for flag ' + arg) # pylint: disable=undefined-loop-variable 

742 

743 if not arg.startswith('-'): 

744 # A non-argument: default is break, GNU is skip. 

745 unparsed_names_and_args.append((None, arg)) 

746 if self.is_gnu_getopt(): 

747 continue 

748 else: 

749 break 

750 

751 if arg == '--': 

752 if known_only: 

753 unparsed_names_and_args.append((None, arg)) 

754 break 

755 

756 # At this point, arg must start with '-'. 

757 if arg.startswith('--'): 

758 arg_without_dashes = arg[2:] 

759 else: 

760 arg_without_dashes = arg[1:] 

761 

762 if '=' in arg_without_dashes: 

763 name, value = arg_without_dashes.split('=', 1) 

764 else: 

765 name, value = arg_without_dashes, None 

766 

767 if not name: 

768 # The argument is all dashes (including one dash). 

769 unparsed_names_and_args.append((None, arg)) 

770 if self.is_gnu_getopt(): 

771 continue 

772 else: 

773 break 

774 

775 # --undefok is a special case. 

776 if name == 'undefok': 

777 value = get_value() 

778 undefok.update(v.strip() for v in value.split(',')) 

779 undefok.update('no' + v.strip() for v in value.split(',')) 

780 continue 

781 

782 flag = flag_dict.get(name) 

783 if flag is not None: 

784 if flag.boolean and value is None: 

785 value = 'true' 

786 else: 

787 value = get_value() 

788 elif name.startswith('no') and len(name) > 2: 

789 # Boolean flags can take the form of --noflag, with no value. 

790 noflag = flag_dict.get(name[2:]) 

791 if noflag is not None and noflag.boolean: 

792 if value is not None: 

793 raise ValueError(arg + ' does not take an argument') 

794 flag = noflag 

795 value = 'false' 

796 

797 if retired_flag_func and flag is None: 

798 is_retired, is_bool = retired_flag_func(name) 

799 

800 # If we didn't recognize that flag, but it starts with 

801 # "no" then maybe it was a boolean flag specified in the 

802 # --nofoo form. 

803 if not is_retired and name.startswith('no'): 

804 is_retired, is_bool = retired_flag_func(name[2:]) 

805 is_retired = is_retired and is_bool 

806 

807 if is_retired: 

808 if not is_bool and value is None: 

809 # This happens when a non-bool retired flag is specified 

810 # in format of "--flag value". 

811 get_value() 

812 logging.error( 

813 'Flag "%s" is retired and should no longer be specified. See ' 

814 'https://abseil.io/tips/90.', 

815 name, 

816 ) 

817 continue 

818 

819 if flag is not None: 

820 # LINT.IfChange 

821 flag.parse(value) 

822 flag.using_default_value = False 

823 # LINT.ThenChange(../testing/flagsaver.py:flag_override_parsing) 

824 else: 

825 unparsed_names_and_args.append((name, arg)) 

826 

827 unknown_flags = [] 

828 unparsed_args = [] 

829 for name, arg in unparsed_names_and_args: 

830 if name is None: 

831 # Positional arguments. 

832 unparsed_args.append(arg) 

833 elif name in undefok: 

834 # Remove undefok flags. 

835 continue 

836 else: 

837 # This is an unknown flag. 

838 if known_only: 

839 unparsed_args.append(arg) 

840 else: 

841 unknown_flags.append((name, arg)) 

842 

843 unparsed_args.extend(list(args)) 

844 return unknown_flags, unparsed_args 

845 

846 def is_parsed(self) -> bool: 

847 """Returns whether flags were parsed.""" 

848 return self.__dict__['__flags_parsed'] 

849 

850 def mark_as_parsed(self) -> None: 

851 """Explicitly marks flags as parsed. 

852 

853 Use this when the caller knows that this FlagValues has been parsed as if 

854 a ``__call__()`` invocation has happened. This is only a public method for 

855 use by things like appcommands which do additional command like parsing. 

856 """ 

857 self.__dict__['__flags_parsed'] = True 

858 

859 def unparse_flags(self) -> None: 

860 """Unparses all flags to the point before any FLAGS(argv) was called.""" 

861 for f in self._flags().values(): 

862 f.unparse() 

863 # We log this message before marking flags as unparsed to avoid a 

864 # problem when the logging library causes flags access. 

865 logging.info('unparse_flags() called; flags access will now raise errors.') 

866 self.__dict__['__flags_parsed'] = False 

867 self.__dict__['__unparse_flags_called'] = True 

868 

869 def flag_values_dict(self) -> Dict[Text, Any]: 

870 """Returns a dictionary that maps flag names to flag values.""" 

871 return {name: flag.value for name, flag in self._flags().items()} 

872 

873 def __str__(self): 

874 """Returns a help string for all known flags.""" 

875 return self.get_help() 

876 

877 def get_help( 

878 self, prefix: Text = '', include_special_flags: bool = True 

879 ) -> Text: 

880 """Returns a help string for all known flags. 

881 

882 Args: 

883 prefix: str, per-line output prefix. 

884 include_special_flags: bool, whether to include description of 

885 SPECIAL_FLAGS, i.e. --flagfile and --undefok. 

886 

887 Returns: 

888 str, formatted help message. 

889 """ 

890 flags_by_module = self.flags_by_module_dict() 

891 if flags_by_module: 

892 modules = sorted(flags_by_module) 

893 # Print the help for the main module first, if possible. 

894 main_module = sys.argv[0] 

895 if main_module in modules: 

896 modules.remove(main_module) 

897 modules = [main_module] + modules 

898 return self._get_help_for_modules(modules, prefix, include_special_flags) 

899 else: 

900 output_lines = [] 

901 # Just print one long list of flags. 

902 values = self._flags().values() 

903 if include_special_flags: 

904 values = itertools.chain( 

905 values, _helpers.SPECIAL_FLAGS._flags().values() # pylint: disable=protected-access # pytype: disable=attribute-error 

906 ) 

907 self._render_flag_list(values, output_lines, prefix) 

908 return '\n'.join(output_lines) 

909 

910 def _get_help_for_modules(self, modules, prefix, include_special_flags): 

911 """Returns the help string for a list of modules. 

912 

913 Private to absl.flags package. 

914 

915 Args: 

916 modules: List[str], a list of modules to get the help string for. 

917 prefix: str, a string that is prepended to each generated help line. 

918 include_special_flags: bool, whether to include description of 

919 SPECIAL_FLAGS, i.e. --flagfile and --undefok. 

920 """ 

921 output_lines = [] 

922 for module in modules: 

923 self._render_our_module_flags(module, output_lines, prefix) 

924 if include_special_flags: 

925 self._render_module_flags( 

926 'absl.flags', 

927 _helpers.SPECIAL_FLAGS._flags().values(), # pylint: disable=protected-access # pytype: disable=attribute-error 

928 output_lines, 

929 prefix, 

930 ) 

931 return '\n'.join(output_lines) 

932 

933 def _render_module_flags(self, module, flags, output_lines, prefix=''): 

934 """Returns a help string for a given module.""" 

935 if not isinstance(module, str): 

936 module = module.__name__ 

937 output_lines.append('\n%s%s:' % (prefix, module)) 

938 self._render_flag_list(flags, output_lines, prefix + ' ') 

939 

940 def _render_our_module_flags(self, module, output_lines, prefix=''): 

941 """Returns a help string for a given module.""" 

942 flags = self.get_flags_for_module(module) 

943 if flags: 

944 self._render_module_flags(module, flags, output_lines, prefix) 

945 

946 def _render_our_module_key_flags(self, module, output_lines, prefix=''): 

947 """Returns a help string for the key flags of a given module. 

948 

949 Args: 

950 module: module|str, the module to render key flags for. 

951 output_lines: [str], a list of strings. The generated help message lines 

952 will be appended to this list. 

953 prefix: str, a string that is prepended to each generated help line. 

954 """ 

955 key_flags = self.get_key_flags_for_module(module) 

956 if key_flags: 

957 self._render_module_flags(module, key_flags, output_lines, prefix) 

958 

959 def module_help(self, module: Any) -> Text: 

960 """Describes the key flags of a module. 

961 

962 Args: 

963 module: module|str, the module to describe the key flags for. 

964 

965 Returns: 

966 str, describing the key flags of a module. 

967 """ 

968 helplist = [] 

969 self._render_our_module_key_flags(module, helplist) 

970 return '\n'.join(helplist) 

971 

972 def main_module_help(self) -> Text: 

973 """Describes the key flags of the main module. 

974 

975 Returns: 

976 str, describing the key flags of the main module. 

977 """ 

978 return self.module_help(sys.argv[0]) 

979 

980 def _render_flag_list(self, flaglist, output_lines, prefix=' '): 

981 fl = self._flags() 

982 special_fl = _helpers.SPECIAL_FLAGS._flags() # pylint: disable=protected-access # pytype: disable=attribute-error 

983 flaglist = [(flag.name, flag) for flag in flaglist] 

984 flaglist.sort() 

985 flagset = {} 

986 for (name, flag) in flaglist: 

987 # It's possible this flag got deleted or overridden since being 

988 # registered in the per-module flaglist. Check now against the 

989 # canonical source of current flag information, the _flags. 

990 if fl.get(name, None) != flag and special_fl.get(name, None) != flag: 

991 # a different flag is using this name now 

992 continue 

993 # only print help once 

994 if flag in flagset: 

995 continue 

996 flagset[flag] = 1 

997 flaghelp = '' 

998 if flag.short_name: 

999 flaghelp += '-%s,' % flag.short_name 

1000 if flag.boolean: 

1001 flaghelp += '--[no]%s:' % flag.name 

1002 else: 

1003 flaghelp += '--%s:' % flag.name 

1004 flaghelp += ' ' 

1005 if flag.help: 

1006 flaghelp += flag.help 

1007 flaghelp = _helpers.text_wrap( 

1008 flaghelp, indent=prefix + ' ', firstline_indent=prefix) 

1009 if flag.default_as_str: 

1010 flaghelp += '\n' 

1011 flaghelp += _helpers.text_wrap( 

1012 '(default: %s)' % flag.default_as_str, indent=prefix + ' ') 

1013 if flag.parser.syntactic_help: 

1014 flaghelp += '\n' 

1015 flaghelp += _helpers.text_wrap( 

1016 '(%s)' % flag.parser.syntactic_help, indent=prefix + ' ') 

1017 output_lines.append(flaghelp) 

1018 

1019 def get_flag_value(self, name: Text, default: Any) -> Any: # pylint: disable=invalid-name 

1020 """Returns the value of a flag (if not None) or a default value. 

1021 

1022 Args: 

1023 name: str, the name of a flag. 

1024 default: Default value to use if the flag value is None. 

1025 

1026 Returns: 

1027 Requested flag value or default. 

1028 """ 

1029 

1030 value = self.__getattr__(name) 

1031 if value is not None: # Can't do if not value, b/c value might be '0' or "" 

1032 return value 

1033 else: 

1034 return default 

1035 

1036 def _is_flag_file_directive(self, flag_string): 

1037 """Checks whether flag_string contain a --flagfile=<foo> directive.""" 

1038 if isinstance(flag_string, str): 

1039 if flag_string.startswith('--flagfile='): 

1040 return 1 

1041 elif flag_string == '--flagfile': 

1042 return 1 

1043 elif flag_string.startswith('-flagfile='): 

1044 return 1 

1045 elif flag_string == '-flagfile': 

1046 return 1 

1047 else: 

1048 return 0 

1049 return 0 

1050 

1051 def _extract_filename(self, flagfile_str): 

1052 """Returns filename from a flagfile_str of form -[-]flagfile=filename. 

1053 

1054 The cases of --flagfile foo and -flagfile foo shouldn't be hitting 

1055 this function, as they are dealt with in the level above this 

1056 function. 

1057 

1058 Args: 

1059 flagfile_str: str, the flagfile string. 

1060 

1061 Returns: 

1062 str, the filename from a flagfile_str of form -[-]flagfile=filename. 

1063 

1064 Raises: 

1065 Error: Raised when illegal --flagfile is provided. 

1066 """ 

1067 if flagfile_str.startswith('--flagfile='): 

1068 return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip()) 

1069 elif flagfile_str.startswith('-flagfile='): 

1070 return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip()) 

1071 else: 

1072 raise _exceptions.Error('Hit illegal --flagfile type: %s' % flagfile_str) 

1073 

1074 def _get_flag_file_lines(self, filename, parsed_file_stack=None): 

1075 """Returns the useful (!=comments, etc) lines from a file with flags. 

1076 

1077 Args: 

1078 filename: str, the name of the flag file. 

1079 parsed_file_stack: [str], a list of the names of the files that we have 

1080 recursively encountered at the current depth. MUTATED BY THIS FUNCTION 

1081 (but the original value is preserved upon successfully returning from 

1082 function call). 

1083 

1084 Returns: 

1085 List of strings. See the note below. 

1086 

1087 NOTE(springer): This function checks for a nested --flagfile=<foo> 

1088 tag and handles the lower file recursively. It returns a list of 

1089 all the lines that _could_ contain command flags. This is 

1090 EVERYTHING except whitespace lines and comments (lines starting 

1091 with '#' or '//'). 

1092 """ 

1093 # For consistency with the cpp version, ignore empty values. 

1094 if not filename: 

1095 return [] 

1096 if parsed_file_stack is None: 

1097 parsed_file_stack = [] 

1098 # We do a little safety check for reparsing a file we've already encountered 

1099 # at a previous depth. 

1100 if filename in parsed_file_stack: 

1101 sys.stderr.write('Warning: Hit circular flagfile dependency. Ignoring' 

1102 ' flagfile: %s\n' % (filename,)) 

1103 return [] 

1104 else: 

1105 parsed_file_stack.append(filename) 

1106 

1107 line_list = [] # All line from flagfile. 

1108 flag_line_list = [] # Subset of lines w/o comments, blanks, flagfile= tags. 

1109 try: 

1110 file_obj = open(filename, 'r') 

1111 except IOError as e_msg: 

1112 raise _exceptions.CantOpenFlagFileError( 

1113 'ERROR:: Unable to open flagfile: %s' % e_msg) 

1114 

1115 with file_obj: 

1116 line_list = file_obj.readlines() 

1117 

1118 # This is where we check each line in the file we just read. 

1119 for line in line_list: 

1120 if line.isspace(): 

1121 pass 

1122 # Checks for comment (a line that starts with '#'). 

1123 elif line.startswith('#') or line.startswith('//'): 

1124 pass 

1125 # Checks for a nested "--flagfile=<bar>" flag in the current file. 

1126 # If we find one, recursively parse down into that file. 

1127 elif self._is_flag_file_directive(line): 

1128 sub_filename = self._extract_filename(line) 

1129 included_flags = self._get_flag_file_lines( 

1130 sub_filename, parsed_file_stack=parsed_file_stack) 

1131 flag_line_list.extend(included_flags) 

1132 else: 

1133 # Any line that's not a comment or a nested flagfile should get 

1134 # copied into 2nd position. This leaves earlier arguments 

1135 # further back in the list, thus giving them higher priority. 

1136 flag_line_list.append(line.strip()) 

1137 

1138 parsed_file_stack.pop() 

1139 return flag_line_list 

1140 

1141 def read_flags_from_files( 

1142 self, argv: Sequence[Text], force_gnu: bool = True 

1143 ) -> List[Text]: 

1144 """Processes command line args, but also allow args to be read from file. 

1145 

1146 Args: 

1147 argv: [str], a list of strings, usually sys.argv[1:], which may contain 

1148 one or more flagfile directives of the form --flagfile="./filename". 

1149 Note that the name of the program (sys.argv[0]) should be omitted. 

1150 force_gnu: bool, if False, --flagfile parsing obeys the 

1151 FLAGS.is_gnu_getopt() value. If True, ignore the value and always follow 

1152 gnu_getopt semantics. 

1153 

1154 Returns: 

1155 A new list which has the original list combined with what we read 

1156 from any flagfile(s). 

1157 

1158 Raises: 

1159 IllegalFlagValueError: Raised when --flagfile is provided with no 

1160 argument. 

1161 

1162 This function is called by FLAGS(argv). 

1163 It scans the input list for a flag that looks like: 

1164 --flagfile=<somefile>. Then it opens <somefile>, reads all valid key 

1165 and value pairs and inserts them into the input list in exactly the 

1166 place where the --flagfile arg is found. 

1167 

1168 Note that your application's flags are still defined the usual way 

1169 using absl.flags DEFINE_flag() type functions. 

1170 

1171 Notes (assuming we're getting a commandline of some sort as our input): 

1172 

1173 * For duplicate flags, the last one we hit should "win". 

1174 * Since flags that appear later win, a flagfile's settings can be "weak" 

1175 if the --flagfile comes at the beginning of the argument sequence, 

1176 and it can be "strong" if the --flagfile comes at the end. 

1177 * A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile. 

1178 It will be expanded in exactly the spot where it is found. 

1179 * In a flagfile, a line beginning with # or // is a comment. 

1180 * Entirely blank lines _should_ be ignored. 

1181 """ 

1182 rest_of_args = argv 

1183 new_argv = [] 

1184 while rest_of_args: 

1185 current_arg = rest_of_args[0] 

1186 rest_of_args = rest_of_args[1:] 

1187 if self._is_flag_file_directive(current_arg): 

1188 # This handles the case of -(-)flagfile foo. In this case the 

1189 # next arg really is part of this one. 

1190 if current_arg == '--flagfile' or current_arg == '-flagfile': 

1191 if not rest_of_args: 

1192 raise _exceptions.IllegalFlagValueError( 

1193 '--flagfile with no argument') 

1194 flag_filename = os.path.expanduser(rest_of_args[0]) 

1195 rest_of_args = rest_of_args[1:] 

1196 else: 

1197 # This handles the case of (-)-flagfile=foo. 

1198 flag_filename = self._extract_filename(current_arg) 

1199 new_argv.extend(self._get_flag_file_lines(flag_filename)) 

1200 else: 

1201 new_argv.append(current_arg) 

1202 # Stop parsing after '--', like getopt and gnu_getopt. 

1203 if current_arg == '--': 

1204 break 

1205 # Stop parsing after a non-flag, like getopt. 

1206 if not current_arg.startswith('-'): 

1207 if not force_gnu and not self.__dict__['__use_gnu_getopt']: 

1208 break 

1209 else: 

1210 if ('=' not in current_arg and rest_of_args and 

1211 not rest_of_args[0].startswith('-')): 

1212 # If this is an occurrence of a legitimate --x y, skip the value 

1213 # so that it won't be mistaken for a standalone arg. 

1214 fl = self._flags() 

1215 name = current_arg.lstrip('-') 

1216 if name in fl and not fl[name].boolean: 

1217 current_arg = rest_of_args[0] 

1218 rest_of_args = rest_of_args[1:] 

1219 new_argv.append(current_arg) 

1220 

1221 if rest_of_args: 

1222 new_argv.extend(rest_of_args) 

1223 

1224 return new_argv 

1225 

1226 def flags_into_string(self) -> Text: 

1227 """Returns a string with the flags assignments from this FlagValues object. 

1228 

1229 This function ignores flags whose value is None. Each flag 

1230 assignment is separated by a newline. 

1231 

1232 NOTE: MUST mirror the behavior of the C++ CommandlineFlagsIntoString 

1233 from https://github.com/gflags/gflags. 

1234 

1235 Returns: 

1236 str, the string with the flags assignments from this FlagValues object. 

1237 The flags are ordered by (module_name, flag_name). 

1238 """ 

1239 module_flags = sorted(self.flags_by_module_dict().items()) 

1240 s = '' 

1241 for unused_module_name, flags in module_flags: 

1242 flags = sorted(flags, key=lambda f: f.name) 

1243 for flag in flags: 

1244 if flag.value is not None: 

1245 s += flag.serialize() + '\n' 

1246 return s 

1247 

1248 def append_flags_into_file(self, filename: Text) -> None: 

1249 """Appends all flags assignments from this FlagInfo object to a file. 

1250 

1251 Output will be in the format of a flagfile. 

1252 

1253 NOTE: MUST mirror the behavior of the C++ AppendFlagsIntoFile 

1254 from https://github.com/gflags/gflags. 

1255 

1256 Args: 

1257 filename: str, name of the file. 

1258 """ 

1259 with open(filename, 'a') as out_file: 

1260 out_file.write(self.flags_into_string()) 

1261 

1262 def write_help_in_xml_format(self, outfile: Optional[TextIO] = None) -> None: 

1263 """Outputs flag documentation in XML format. 

1264 

1265 NOTE: We use element names that are consistent with those used by 

1266 the C++ command-line flag library, from 

1267 https://github.com/gflags/gflags. 

1268 We also use a few new elements (e.g., <key>), but we do not 

1269 interfere / overlap with existing XML elements used by the C++ 

1270 library. Please maintain this consistency. 

1271 

1272 Args: 

1273 outfile: File object we write to. Default None means sys.stdout. 

1274 """ 

1275 doc = minidom.Document() 

1276 all_flag = doc.createElement('AllFlags') 

1277 doc.appendChild(all_flag) 

1278 

1279 all_flag.appendChild( 

1280 _helpers.create_xml_dom_element(doc, 'program', 

1281 os.path.basename(sys.argv[0]))) 

1282 

1283 usage_doc = sys.modules['__main__'].__doc__ 

1284 if not usage_doc: 

1285 usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0] 

1286 else: 

1287 usage_doc = usage_doc.replace('%s', sys.argv[0]) 

1288 all_flag.appendChild( 

1289 _helpers.create_xml_dom_element(doc, 'usage', usage_doc)) 

1290 

1291 # Get list of key flags for the main module. 

1292 key_flags = self.get_key_flags_for_module(sys.argv[0]) 

1293 

1294 # Sort flags by declaring module name and next by flag name. 

1295 flags_by_module = self.flags_by_module_dict() 

1296 all_module_names = list(flags_by_module.keys()) 

1297 all_module_names.sort() 

1298 for module_name in all_module_names: 

1299 flag_list = [(f.name, f) for f in flags_by_module[module_name]] 

1300 flag_list.sort() 

1301 for unused_flag_name, flag in flag_list: 

1302 is_key = flag in key_flags 

1303 all_flag.appendChild( 

1304 flag._create_xml_dom_element( # pylint: disable=protected-access 

1305 doc, 

1306 module_name, 

1307 is_key=is_key)) 

1308 

1309 outfile = outfile or sys.stdout 

1310 outfile.write( 

1311 doc.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')) 

1312 outfile.flush() 

1313 

1314 def _check_method_name_conflicts(self, name: str, flag: Flag): 

1315 if flag.allow_using_method_names: 

1316 return 

1317 short_name = flag.short_name 

1318 flag_names = {name} if short_name is None else {name, short_name} 

1319 for flag_name in flag_names: 

1320 if flag_name in self.__dict__['__banned_flag_names']: 

1321 raise _exceptions.FlagNameConflictsWithMethodError( 

1322 'Cannot define a flag named "{name}". It conflicts with a method ' 

1323 'on class "{class_name}". To allow defining it, use ' 

1324 'allow_using_method_names and access the flag value with ' 

1325 "FLAGS['{name}'].value. FLAGS.{name} returns the method, " 

1326 'not the flag value.'.format( 

1327 name=flag_name, class_name=type(self).__name__)) 

1328 

1329 

1330FLAGS = FlagValues() 

1331 

1332 

1333class FlagHolder(Generic[_T]): 

1334 """Holds a defined flag. 

1335 

1336 This facilitates a cleaner api around global state. Instead of:: 

1337 

1338 flags.DEFINE_integer('foo', ...) 

1339 flags.DEFINE_integer('bar', ...) 

1340 

1341 def method(): 

1342 # prints parsed value of 'bar' flag 

1343 print(flags.FLAGS.foo) 

1344 # runtime error due to typo or possibly bad coding style. 

1345 print(flags.FLAGS.baz) 

1346 

1347 it encourages code like:: 

1348 

1349 _FOO_FLAG = flags.DEFINE_integer('foo', ...) 

1350 _BAR_FLAG = flags.DEFINE_integer('bar', ...) 

1351 

1352 def method(): 

1353 print(_FOO_FLAG.value) 

1354 print(_BAR_FLAG.value) 

1355 

1356 since the name of the flag appears only once in the source code. 

1357 """ 

1358 

1359 value: _T 

1360 

1361 def __init__( 

1362 self, 

1363 flag_values: FlagValues, 

1364 flag: Flag[_T], 

1365 ensure_non_none_value: bool = False, 

1366 ): 

1367 """Constructs a FlagHolder instance providing typesafe access to flag. 

1368 

1369 Args: 

1370 flag_values: The container the flag is registered to. 

1371 flag: The flag object for this flag. 

1372 ensure_non_none_value: Is the value of the flag allowed to be None. 

1373 """ 

1374 self._flagvalues = flag_values 

1375 # We take the entire flag object, but only keep the name. Why? 

1376 # - We want FlagHolder[T] to be generic container 

1377 # - flag_values contains all flags, so has no reference to T. 

1378 # - typecheckers don't like to see a generic class where none of the ctor 

1379 # arguments refer to the generic type. 

1380 self._name = flag.name 

1381 # We intentionally do NOT check if the default value is None. 

1382 # This allows future use of this for "required flags with None default" 

1383 self._ensure_non_none_value = ensure_non_none_value 

1384 

1385 def __eq__(self, other): 

1386 raise TypeError( 

1387 "unsupported operand type(s) for ==: '{0}' and '{1}' " 

1388 "(did you mean to use '{0}.value' instead?)".format( 

1389 type(self).__name__, type(other).__name__)) 

1390 

1391 def __bool__(self): 

1392 raise TypeError( 

1393 "bool() not supported for instances of type '{0}' " 

1394 "(did you mean to use '{0}.value' instead?)".format( 

1395 type(self).__name__)) 

1396 

1397 __nonzero__ = __bool__ 

1398 

1399 @property 

1400 def name(self) -> Text: 

1401 return self._name 

1402 

1403 @property 

1404 def value(self) -> _T: 

1405 """Returns the value of the flag. 

1406 

1407 If ``_ensure_non_none_value`` is ``True``, then return value is not 

1408 ``None``. 

1409 

1410 Raises: 

1411 UnparsedFlagAccessError: if flag parsing has not finished. 

1412 IllegalFlagValueError: if value is None unexpectedly. 

1413 """ 

1414 val = getattr(self._flagvalues, self._name) 

1415 if self._ensure_non_none_value and val is None: 

1416 raise _exceptions.IllegalFlagValueError( 

1417 'Unexpected None value for flag %s' % self._name) 

1418 return val 

1419 

1420 @property 

1421 def default(self) -> _T: 

1422 """Returns the default value of the flag.""" 

1423 return self._flagvalues[self._name].default 

1424 

1425 @property 

1426 def present(self) -> bool: 

1427 """Returns True if the flag was parsed from command-line flags.""" 

1428 return bool(self._flagvalues[self._name].present) 

1429 

1430 

1431def resolve_flag_ref( 

1432 flag_ref: Union[str, FlagHolder], flag_values: FlagValues 

1433) -> Tuple[str, FlagValues]: 

1434 """Helper to validate and resolve a flag reference argument.""" 

1435 if isinstance(flag_ref, FlagHolder): 

1436 new_flag_values = flag_ref._flagvalues # pylint: disable=protected-access 

1437 if flag_values != FLAGS and flag_values != new_flag_values: 

1438 raise ValueError( 

1439 'flag_values must not be customized when operating on a FlagHolder') 

1440 return flag_ref.name, new_flag_values 

1441 return flag_ref, flag_values 

1442 

1443 

1444def resolve_flag_refs( 

1445 flag_refs: Sequence[Union[str, FlagHolder]], flag_values: FlagValues 

1446) -> Tuple[List[str], FlagValues]: 

1447 """Helper to validate and resolve flag reference list arguments.""" 

1448 fv = None 

1449 names = [] 

1450 for ref in flag_refs: 

1451 if isinstance(ref, FlagHolder): 

1452 newfv = ref._flagvalues # pylint: disable=protected-access 

1453 name = ref.name 

1454 else: 

1455 newfv = flag_values 

1456 name = ref 

1457 if fv and fv != newfv: 

1458 raise ValueError( 

1459 'multiple FlagValues instances used in invocation. ' 

1460 'FlagHolders must be registered to the same FlagValues instance as ' 

1461 'do flag names, if provided.') 

1462 fv = newfv 

1463 names.append(name) 

1464 return names, fv