Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/_config/config.py: 56%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

279 statements  

1""" 

2The config module holds package-wide configurables and provides 

3a uniform API for working with them. 

4 

5Overview 

6======== 

7 

8This module supports the following requirements: 

9- options are referenced using keys in dot.notation, e.g. "x.y.option - z". 

10- keys are case-insensitive. 

11- functions should accept partial/regex keys, when unambiguous. 

12- options can be registered by modules at import time. 

13- options can be registered at init-time (via core.config_init) 

14- options have a default value, and (optionally) a description and 

15 validation function associated with them. 

16- options can be deprecated, in which case referencing them 

17 should produce a warning. 

18- deprecated options can optionally be rerouted to a replacement 

19 so that accessing a deprecated option reroutes to a differently 

20 named option. 

21- options can be reset to their default value. 

22- all option can be reset to their default value at once. 

23- all options in a certain sub - namespace can be reset at once. 

24- the user can set / get / reset or ask for the description of an option. 

25- a developer can register and mark an option as deprecated. 

26- you can register a callback to be invoked when the option value 

27 is set or reset. Changing the stored value is considered misuse, but 

28 is not verboten. 

29 

30Implementation 

31============== 

32 

33- Data is stored using nested dictionaries, and should be accessed 

34 through the provided API. 

35 

36- "Registered options" and "Deprecated options" have metadata associated 

37 with them, which are stored in auxiliary dictionaries keyed on the 

38 fully-qualified key, e.g. "x.y.z.option". 

39 

40- the config_init module is imported by the package's __init__.py file. 

41 placing any register_option() calls there will ensure those options 

42 are available as soon as pandas is loaded. If you use register_option 

43 in a module, it will only be available after that module is imported, 

44 which you should be aware of. 

45 

46- `config_prefix` is a context_manager (for use with the `with` keyword) 

47 which can save developers some typing, see the docstring. 

48 

49""" 

50 

51from __future__ import annotations 

52 

53from contextlib import contextmanager 

54import re 

55from typing import ( 

56 TYPE_CHECKING, 

57 Any, 

58 NamedTuple, 

59 cast, 

60) 

61import warnings 

62 

63from pandas._typing import F 

64from pandas.util._exceptions import find_stack_level 

65 

66if TYPE_CHECKING: 

67 from collections.abc import ( 

68 Callable, 

69 Generator, 

70 Sequence, 

71 ) 

72 

73 

74class DeprecatedOption(NamedTuple): 

75 key: str 

76 category: type[Warning] 

77 msg: str | None 

78 rkey: str | None 

79 removal_ver: str | None 

80 

81 

82class RegisteredOption(NamedTuple): 

83 key: str 

84 defval: Any 

85 doc: str 

86 validator: Callable[[object], Any] | None 

87 cb: Callable[[str], Any] | None 

88 

89 

90# holds deprecated option metadata 

91_deprecated_options: dict[str, DeprecatedOption] = {} 

92 

93# holds registered option metadata 

94_registered_options: dict[str, RegisteredOption] = {} 

95 

96# holds the current values for registered options 

97_global_config: dict[str, Any] = {} 

98 

99# keys which have a special meaning 

100_reserved_keys: list[str] = ["all"] 

101 

102 

103class OptionError(AttributeError, KeyError): 

104 """ 

105 Exception raised for pandas.options. 

106 

107 Backwards compatible with KeyError checks. 

108 

109 See Also 

110 -------- 

111 options : Access and modify global pandas settings. 

112 

113 Examples 

114 -------- 

115 >>> pd.options.context 

116 Traceback (most recent call last): 

117 OptionError: No such option 

118 """ 

119 

120 __module__ = "pandas.errors" 

121 

122 

123# 

124# User API 

125 

126 

127def _get_single_key(pat: str) -> str: 

128 keys = _select_options(pat) 

129 if len(keys) == 0: 

130 _warn_if_deprecated(pat) 

131 raise OptionError(f"No such keys(s): {pat!r}") 

132 if len(keys) > 1: 

133 raise OptionError("Pattern matched multiple keys") 

134 key = keys[0] 

135 

136 _warn_if_deprecated(key) 

137 

138 key = _translate_key(key) 

139 

140 return key 

141 

142 

143def get_option(pat: str) -> Any: 

144 """ 

145 Retrieve the value of the specified option. 

146 

147 This method allows users to query the current value of a given option 

148 in the pandas configuration system. Options control various display, 

149 performance, and behavior-related settings within pandas. 

150 

151 Parameters 

152 ---------- 

153 pat : str 

154 Regexp which should match a single option. 

155 

156 .. warning:: 

157 

158 Partial matches are supported for convenience, but unless you use the 

159 full option name (e.g. x.y.z.option_name), your code may break in future 

160 versions if new options with similar names are introduced. 

161 

162 Returns 

163 ------- 

164 Any 

165 The value of the option. 

166 

167 Raises 

168 ------ 

169 OptionError : if no such option exists 

170 

171 See Also 

172 -------- 

173 set_option : Set the value of the specified option or options. 

174 reset_option : Reset one or more options to their default value. 

175 describe_option : Print the description for one or more registered options. 

176 

177 Notes 

178 ----- 

179 For all available options, please view the :ref:`User Guide <options.available>` 

180 or use ``pandas.describe_option()``. 

181 

182 Examples 

183 -------- 

184 >>> pd.get_option("display.max_columns") # doctest: +SKIP 

185 4 

186 """ 

187 key = _get_single_key(pat) 

188 

189 # walk the nested dict 

190 root, k = _get_root(key) 

191 return root[k] 

192 

193 

194def set_option(*args) -> None: 

195 """ 

196 Set the value of the specified option or options. 

197 

198 This method allows fine-grained control over the behavior and display settings 

199 of pandas. Options affect various functionalities such as output formatting, 

200 display limits, and operational behavior. Settings can be modified at runtime 

201 without requiring changes to global configurations or environment variables. 

202 

203 Parameters 

204 ---------- 

205 *args : str | object | dict 

206 Arguments provided in pairs, which will be interpreted as (pattern, value), 

207 or as a single dictionary containing multiple option-value pairs. 

208 pattern: str 

209 Regexp which should match a single option 

210 value: object 

211 New value of option 

212 

213 .. warning:: 

214 

215 Partial pattern matches are supported for convenience, but unless you 

216 use the full option name (e.g. x.y.z.option_name), your code may break in 

217 future versions if new options with similar names are introduced. 

218 

219 Returns 

220 ------- 

221 None 

222 No return value. 

223 

224 Raises 

225 ------ 

226 ValueError if odd numbers of non-keyword arguments are provided 

227 TypeError if keyword arguments are provided 

228 OptionError if no such option exists 

229 

230 See Also 

231 -------- 

232 get_option : Retrieve the value of the specified option. 

233 reset_option : Reset one or more options to their default value. 

234 describe_option : Print the description for one or more registered options. 

235 option_context : Context manager to temporarily set options in a ``with`` 

236 statement. 

237 

238 Notes 

239 ----- 

240 For all available options, please view the :ref:`User Guide <options.available>` 

241 or use ``pandas.describe_option()``. 

242 

243 Examples 

244 -------- 

245 Option-Value Pair Input: 

246 

247 >>> pd.set_option("display.max_columns", 4) 

248 >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) 

249 >>> df 

250 0 1 ... 3 4 

251 0 1 2 ... 4 5 

252 1 6 7 ... 9 10 

253 [2 rows x 5 columns] 

254 >>> pd.reset_option("display.max_columns") 

255 

256 Dictionary Input: 

257 

258 >>> pd.set_option({"display.max_columns": 4, "display.precision": 1}) 

259 >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) 

260 >>> df 

261 0 1 ... 3 4 

262 0 1 2 ... 4 5 

263 1 6 7 ... 9 10 

264 [2 rows x 5 columns] 

265 >>> pd.reset_option("display.max_columns") 

266 >>> pd.reset_option("display.precision") 

267 """ 

268 # Handle dictionary input 

269 if len(args) == 1 and isinstance(args[0], dict): 

270 args = tuple(kv for item in args[0].items() for kv in item) 

271 

272 nargs = len(args) 

273 if not nargs or nargs % 2 != 0: 

274 raise ValueError("Must provide an even number of non-keyword arguments") 

275 

276 for k, v in zip(args[::2], args[1::2], strict=True): 

277 key = _get_single_key(k) 

278 

279 opt = _get_registered_option(key) 

280 if opt and opt.validator: 

281 opt.validator(v) 

282 

283 # walk the nested dict 

284 root, k_root = _get_root(key) 

285 root[k_root] = v 

286 

287 if opt.cb: 

288 opt.cb(key) 

289 

290 

291def describe_option(pat: str = "", _print_desc: bool = True) -> str | None: 

292 """ 

293 Print the description for one or more registered options. 

294 

295 Call with no arguments to get a listing for all registered options. 

296 

297 Parameters 

298 ---------- 

299 pat : str, default "" 

300 String or string regexp pattern. 

301 Empty string will return all options. 

302 For regexp strings, all matching keys will have their description displayed. 

303 _print_desc : bool, default True 

304 If True (default) the description(s) will be printed to stdout. 

305 Otherwise, the description(s) will be returned as a string 

306 (for testing). 

307 

308 Returns 

309 ------- 

310 None 

311 If ``_print_desc=True``. 

312 str 

313 If the description(s) as a string if ``_print_desc=False``. 

314 

315 See Also 

316 -------- 

317 get_option : Retrieve the value of the specified option. 

318 set_option : Set the value of the specified option or options. 

319 reset_option : Reset one or more options to their default value. 

320 

321 Notes 

322 ----- 

323 For all available options, please view the 

324 :ref:`User Guide <options.available>`. 

325 

326 Examples 

327 -------- 

328 >>> pd.describe_option("display.max_columns") # doctest: +SKIP 

329 display.max_columns : int 

330 If max_cols is exceeded, switch to truncate view... 

331 """ 

332 keys = _select_options(pat) 

333 if len(keys) == 0: 

334 raise OptionError(f"No such keys(s) for {pat=}") 

335 

336 s = "\n".join([_build_option_description(k) for k in keys]) 

337 

338 if _print_desc: 

339 print(s) 

340 return None 

341 return s 

342 

343 

344def reset_option(pat: str) -> None: 

345 """ 

346 Reset one or more options to their default value. 

347 

348 This method resets the specified pandas option(s) back to their default 

349 values. It allows partial string matching for convenience, but users should 

350 exercise caution to avoid unintended resets due to changes in option names 

351 in future versions. 

352 

353 Parameters 

354 ---------- 

355 pat : str/regex 

356 If specified only options matching ``pat*`` will be reset. 

357 Pass ``"all"`` as argument to reset all options. 

358 

359 .. warning:: 

360 

361 Partial matches are supported for convenience, but unless you 

362 use the full option name (e.g. x.y.z.option_name), your code may break 

363 in future versions if new options with similar names are introduced. 

364 

365 Returns 

366 ------- 

367 None 

368 No return value. 

369 

370 See Also 

371 -------- 

372 get_option : Retrieve the value of the specified option. 

373 set_option : Set the value of the specified option or options. 

374 describe_option : Print the description for one or more registered options. 

375 

376 Notes 

377 ----- 

378 For all available options, please view the 

379 :ref:`User Guide <options.available>`. 

380 

381 Examples 

382 -------- 

383 >>> pd.reset_option("display.max_columns") # doctest: +SKIP 

384 """ 

385 keys = _select_options(pat) 

386 

387 if len(keys) == 0: 

388 raise OptionError(f"No such keys(s) for {pat=}") 

389 

390 if len(keys) > 1 and len(pat) < 4 and pat != "all": 

391 raise ValueError( 

392 "You must specify at least 4 characters when " 

393 "resetting multiple keys, use the special keyword " 

394 '"all" to reset all the options to their default value' 

395 ) 

396 

397 for k in keys: 

398 set_option(k, _registered_options[k].defval) 

399 

400 

401def get_default_val(pat: str): 

402 key = _get_single_key(pat) 

403 return _get_registered_option(key).defval 

404 

405 

406class DictWrapper: 

407 """provide attribute-style access to a nested dict""" 

408 

409 d: dict[str, Any] 

410 

411 def __init__(self, d: dict[str, Any], prefix: str = "") -> None: 

412 object.__setattr__(self, "d", d) 

413 object.__setattr__(self, "prefix", prefix) 

414 

415 def __setattr__(self, key: str, val: Any) -> None: 

416 prefix = object.__getattribute__(self, "prefix") 

417 if prefix: 

418 prefix += "." 

419 prefix += key 

420 # you can't set new keys 

421 # can you can't overwrite subtrees 

422 if key in self.d and not isinstance(self.d[key], dict): 

423 set_option(prefix, val) 

424 else: 

425 raise OptionError("You can only set the value of existing options") 

426 

427 def __getattr__(self, key: str): 

428 prefix = object.__getattribute__(self, "prefix") 

429 if prefix: 

430 prefix += "." 

431 prefix += key 

432 try: 

433 v = object.__getattribute__(self, "d")[key] 

434 except KeyError as err: 

435 raise OptionError("No such option") from err 

436 if isinstance(v, dict): 

437 return DictWrapper(v, prefix) 

438 else: 

439 return get_option(prefix) 

440 

441 def __dir__(self) -> list[str]: 

442 return list(self.d.keys()) 

443 

444 

445options = DictWrapper(_global_config) 

446# DictWrapper defines a custom setattr 

447object.__setattr__(options, "__module__", "pandas") 

448 

449# 

450# Functions for use by pandas developers, in addition to User - api 

451 

452 

453@contextmanager 

454def option_context(*args) -> Generator[None]: 

455 """ 

456 Context manager to temporarily set options in a ``with`` statement. 

457 

458 This method allows users to set one or more pandas options temporarily 

459 within a controlled block. The previous options' values are restored 

460 once the block is exited. This is useful when making temporary adjustments 

461 to pandas' behavior without affecting the global state. 

462 

463 Parameters 

464 ---------- 

465 *args : str | object | dict 

466 An even amount of arguments provided in pairs which will be 

467 interpreted as (pattern, value) pairs. Alternatively, a single 

468 dictionary of {pattern: value} may be provided. 

469 

470 Returns 

471 ------- 

472 None 

473 No return value. 

474 

475 Yields 

476 ------ 

477 None 

478 No yield value. 

479 

480 See Also 

481 -------- 

482 get_option : Retrieve the value of the specified option. 

483 set_option : Set the value of the specified option. 

484 reset_option : Reset one or more options to their default value. 

485 describe_option : Print the description for one or more registered options. 

486 

487 Notes 

488 ----- 

489 For all available options, please view the :ref:`User Guide <options.available>` 

490 or use ``pandas.describe_option()``. 

491 

492 Examples 

493 -------- 

494 >>> from pandas import option_context 

495 >>> with option_context("display.max_rows", 10, "display.max_columns", 5): 

496 ... pass 

497 >>> with option_context({"display.max_rows": 10, "display.max_columns": 5}): 

498 ... pass 

499 """ 

500 if len(args) == 1 and isinstance(args[0], dict): 

501 args = tuple(kv for item in args[0].items() for kv in item) 

502 

503 if len(args) % 2 != 0 or len(args) < 2: 

504 raise ValueError( 

505 "Provide an even amount of arguments as " 

506 "option_context(pat, val, pat, val...)." 

507 ) 

508 

509 ops = tuple(zip(args[::2], args[1::2], strict=True)) 

510 undo: tuple[tuple[Any, Any], ...] = () 

511 try: 

512 undo = tuple((pat, get_option(pat)) for pat, val in ops) 

513 for pat, val in ops: 

514 set_option(pat, val) 

515 yield 

516 finally: 

517 for pat, val in undo: 

518 set_option(pat, val) 

519 

520 

521def register_option( 

522 key: str, 

523 defval: object, 

524 doc: str = "", 

525 validator: Callable[[object], Any] | None = None, 

526 cb: Callable[[str], Any] | None = None, 

527) -> None: 

528 """ 

529 Register an option in the package-wide pandas config object 

530 

531 Parameters 

532 ---------- 

533 key : str 

534 Fully-qualified key, e.g. "x.y.option - z". 

535 defval : object 

536 Default value of the option. 

537 doc : str 

538 Description of the option. 

539 validator : Callable, optional 

540 Function of a single argument, should raise `ValueError` if 

541 called with a value which is not a legal value for the option. 

542 cb 

543 a function of a single argument "key", which is called 

544 immediately after an option value is set/reset. key is 

545 the full name of the option. 

546 

547 Raises 

548 ------ 

549 ValueError if `validator` is specified and `defval` is not a valid value. 

550 

551 """ 

552 import keyword 

553 import tokenize 

554 

555 key = key.lower() 

556 

557 if key in _registered_options: 

558 raise OptionError(f"Option '{key}' has already been registered") 

559 if key in _reserved_keys: 

560 raise OptionError(f"Option '{key}' is a reserved key") 

561 

562 # the default value should be legal 

563 if validator: 

564 validator(defval) 

565 

566 # walk the nested dict, creating dicts as needed along the path 

567 path = key.split(".") 

568 

569 for k in path: 

570 if not re.match("^" + tokenize.Name + "$", k): 

571 raise ValueError(f"{k} is not a valid identifier") 

572 if keyword.iskeyword(k): 

573 raise ValueError(f"{k} is a python keyword") 

574 

575 cursor = _global_config 

576 msg = "Path prefix to option '{option}' is already an option" 

577 

578 for i, p in enumerate(path[:-1]): 

579 if not isinstance(cursor, dict): 

580 raise OptionError(msg.format(option=".".join(path[:i]))) 

581 if p not in cursor: 

582 cursor[p] = {} 

583 cursor = cursor[p] 

584 

585 if not isinstance(cursor, dict): 

586 raise OptionError(msg.format(option=".".join(path[:-1]))) 

587 

588 cursor[path[-1]] = defval # initialize 

589 

590 # save the option metadata 

591 _registered_options[key] = RegisteredOption( 

592 key=key, defval=defval, doc=doc, validator=validator, cb=cb 

593 ) 

594 

595 

596def deprecate_option( 

597 key: str, 

598 category: type[Warning], 

599 msg: str | None = None, 

600 rkey: str | None = None, 

601 removal_ver: str | None = None, 

602) -> None: 

603 """ 

604 Mark option `key` as deprecated, if code attempts to access this option, 

605 a warning will be produced, using `msg` if given, or a default message 

606 if not. 

607 if `rkey` is given, any access to the key will be re-routed to `rkey`. 

608 

609 Neither the existence of `key` nor that if `rkey` is checked. If they 

610 do not exist, any subsequence access will fail as usual, after the 

611 deprecation warning is given. 

612 

613 Parameters 

614 ---------- 

615 key : str 

616 Name of the option to be deprecated. 

617 must be a fully-qualified option name (e.g "x.y.z.rkey"). 

618 category : Warning 

619 Warning class for the deprecation. 

620 msg : str, optional 

621 Warning message to output when the key is referenced. 

622 if no message is given a default message will be emitted. 

623 rkey : str, optional 

624 Name of an option to reroute access to. 

625 If specified, any referenced `key` will be 

626 re-routed to `rkey` including set/get/reset. 

627 rkey must be a fully-qualified option name (e.g "x.y.z.rkey"). 

628 used by the default message if no `msg` is specified. 

629 removal_ver : str, optional 

630 Specifies the version in which this option will 

631 be removed. used by the default message if no `msg` is specified. 

632 

633 Raises 

634 ------ 

635 OptionError 

636 If the specified key has already been deprecated. 

637 """ 

638 key = key.lower() 

639 

640 if key in _deprecated_options: 

641 raise OptionError(f"Option '{key}' has already been defined as deprecated.") 

642 

643 _deprecated_options[key] = DeprecatedOption(key, category, msg, rkey, removal_ver) 

644 

645 

646# 

647# functions internal to the module 

648 

649 

650def _select_options(pat: str) -> list[str]: 

651 """ 

652 returns a list of keys matching `pat` 

653 

654 if pat=="all", returns all registered options 

655 """ 

656 # short-circuit for exact key 

657 if pat in _registered_options: 

658 return [pat] 

659 

660 # else look through all of them 

661 keys = sorted(_registered_options.keys()) 

662 if pat == "all": # reserved key 

663 return keys 

664 

665 return [k for k in keys if re.search(pat, k, re.I)] 

666 

667 

668def _get_root(key: str) -> tuple[dict[str, Any], str]: 

669 path = key.split(".") 

670 cursor = _global_config 

671 for p in path[:-1]: 

672 cursor = cursor[p] 

673 return cursor, path[-1] 

674 

675 

676def _get_deprecated_option(key: str): 

677 """ 

678 Retrieves the metadata for a deprecated option, if `key` is deprecated. 

679 

680 Returns 

681 ------- 

682 DeprecatedOption (namedtuple) if key is deprecated, None otherwise 

683 """ 

684 try: 

685 d = _deprecated_options[key] 

686 except KeyError: 

687 return None 

688 else: 

689 return d 

690 

691 

692def _get_registered_option(key: str): 

693 """ 

694 Retrieves the option metadata if `key` is a registered option. 

695 

696 Returns 

697 ------- 

698 RegisteredOption (namedtuple) if key is deprecated, None otherwise 

699 """ 

700 return _registered_options.get(key) 

701 

702 

703def _translate_key(key: str) -> str: 

704 """ 

705 if `key` is deprecated and a replacement key defined, will return the 

706 replacement key, otherwise returns `key` as-is 

707 """ 

708 d = _get_deprecated_option(key) 

709 if d: 

710 return d.rkey or key 

711 else: 

712 return key 

713 

714 

715def _warn_if_deprecated(key: str) -> bool: 

716 """ 

717 Checks if `key` is a deprecated option and if so, prints a warning. 

718 

719 Returns 

720 ------- 

721 bool - True if `key` is deprecated, False otherwise. 

722 """ 

723 d = _get_deprecated_option(key) 

724 if d: 

725 if d.msg: 

726 warnings.warn( 

727 d.msg, 

728 d.category, 

729 stacklevel=find_stack_level(), 

730 ) 

731 else: 

732 msg = f"'{key}' is deprecated" 

733 if d.removal_ver: 

734 msg += f" and will be removed in {d.removal_ver}" 

735 if d.rkey: 

736 msg += f", please use '{d.rkey}' instead." 

737 else: 

738 msg += ", please refrain from using it." 

739 

740 warnings.warn( 

741 msg, 

742 d.category, 

743 stacklevel=find_stack_level(), 

744 ) 

745 return True 

746 return False 

747 

748 

749def _build_option_description(k: str) -> str: 

750 """Builds a formatted description of a registered option and prints it""" 

751 o = _get_registered_option(k) 

752 d = _get_deprecated_option(k) 

753 

754 s = f"{k} " 

755 

756 if o.doc: 

757 s += "\n".join(o.doc.strip().split("\n")) 

758 else: 

759 s += "No description available." 

760 

761 if o: 

762 with warnings.catch_warnings(): 

763 warnings.simplefilter("ignore", FutureWarning) 

764 warnings.simplefilter("ignore", DeprecationWarning) 

765 s += f"\n [default: {o.defval}] [currently: {get_option(k)}]" 

766 

767 if d: 

768 rkey = d.rkey or "" 

769 s += "\n (Deprecated" 

770 s += f", use `{rkey}` instead." 

771 s += ")" 

772 

773 return s 

774 

775 

776# helpers 

777 

778 

779@contextmanager 

780def config_prefix(prefix: str) -> Generator[None]: 

781 """ 

782 contextmanager for multiple invocations of API with a common prefix 

783 

784 supported API functions: (register / get / set )__option 

785 

786 Warning: This is not thread - safe, and won't work properly if you import 

787 the API functions into your module using the "from x import y" construct. 

788 

789 Example 

790 ------- 

791 import pandas._config.config as cf 

792 with cf.config_prefix("display.font"): 

793 cf.register_option("color", "red") 

794 cf.register_option("size", " 5 pt") 

795 cf.set_option(size, " 6 pt") 

796 cf.get_option(size) 

797 ... 

798 

799 etc' 

800 

801 will register options "display.font.color", "display.font.size", set the 

802 value of "display.font.size"... and so on. 

803 """ 

804 # Note: reset_option relies on set_option, and on key directly 

805 # it does not fit in to this monkey-patching scheme 

806 

807 global register_option, get_option, set_option 

808 

809 def wrap(func: F) -> F: 

810 def inner(key: str, *args, **kwds): 

811 pkey = f"{prefix}.{key}" 

812 return func(pkey, *args, **kwds) 

813 

814 return cast(F, inner) 

815 

816 _register_option = register_option 

817 _get_option = get_option 

818 _set_option = set_option 

819 set_option = wrap(set_option) 

820 get_option = wrap(get_option) 

821 register_option = wrap(register_option) 

822 try: 

823 yield 

824 finally: 

825 set_option = _set_option 

826 get_option = _get_option 

827 register_option = _register_option 

828 

829 

830# These factories and methods are handy for use as the validator 

831# arg in register_option 

832 

833 

834def is_type_factory(_type: type[Any]) -> Callable[[Any], None]: 

835 """ 

836 

837 Parameters 

838 ---------- 

839 `_type` - a type to be compared against (e.g. type(x) == `_type`) 

840 

841 Returns 

842 ------- 

843 validator - a function of a single argument x , which raises 

844 ValueError if type(x) is not equal to `_type` 

845 

846 """ 

847 

848 def inner(x) -> None: 

849 if type(x) != _type: 

850 raise ValueError(f"Value must have type '{_type}'") 

851 

852 return inner 

853 

854 

855def is_instance_factory(_type: type | tuple[type, ...]) -> Callable[[Any], None]: 

856 """ 

857 

858 Parameters 

859 ---------- 

860 `_type` - the type to be checked against 

861 

862 Returns 

863 ------- 

864 validator - a function of a single argument x , which raises 

865 ValueError if x is not an instance of `_type` 

866 

867 """ 

868 if isinstance(_type, tuple): 

869 type_repr = "|".join(map(str, _type)) 

870 else: 

871 type_repr = f"'{_type}'" 

872 

873 def inner(x) -> None: 

874 if not isinstance(x, _type): 

875 raise ValueError(f"Value must be an instance of {type_repr}") 

876 

877 return inner 

878 

879 

880def is_one_of_factory(legal_values: Sequence) -> Callable[[Any], None]: 

881 callables = [c for c in legal_values if callable(c)] 

882 legal_values = [c for c in legal_values if not callable(c)] 

883 

884 def inner(x) -> None: 

885 if x not in legal_values: 

886 if not any(c(x) for c in callables): 

887 uvals = [str(lval) for lval in legal_values] 

888 pp_values = "|".join(uvals) 

889 msg = f"Value must be one of {pp_values}" 

890 if len(callables): 

891 msg += " or a callable" 

892 raise ValueError(msg) 

893 

894 return inner 

895 

896 

897def is_nonnegative_int(value: object) -> None: 

898 """ 

899 Verify that value is None or a positive int. 

900 

901 Parameters 

902 ---------- 

903 value : None or int 

904 The `value` to be checked. 

905 

906 Raises 

907 ------ 

908 ValueError 

909 When the value is not None or is a negative integer 

910 """ 

911 if value is None: 

912 return 

913 

914 elif isinstance(value, int): 

915 if value >= 0: 

916 return 

917 

918 msg = "Value must be a nonnegative integer or None" 

919 raise ValueError(msg) 

920 

921 

922# common type validators, for convenience 

923# usage: register_option(... , validator = is_int) 

924is_int = is_type_factory(int) 

925is_bool = is_type_factory(bool) 

926is_float = is_type_factory(float) 

927is_str = is_type_factory(str) 

928is_text = is_instance_factory((str, bytes)) 

929 

930 

931def is_callable(obj: object) -> bool: 

932 """ 

933 

934 Parameters 

935 ---------- 

936 `obj` - the object to be checked 

937 

938 Returns 

939 ------- 

940 validator - returns True if object is callable 

941 raises ValueError otherwise. 

942 

943 """ 

944 if not callable(obj): 

945 raise ValueError("Value must be a callable") 

946 return True 

947 

948 

949# import set_module here would cause circular import 

950get_option.__module__ = "pandas" 

951set_option.__module__ = "pandas" 

952describe_option.__module__ = "pandas" 

953reset_option.__module__ = "pandas" 

954option_context.__module__ = "pandas"