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.util._exceptions import find_stack_level
64
65if TYPE_CHECKING:
66 from collections.abc import (
67 Callable,
68 Generator,
69 Sequence,
70 )
71
72 from pandas._typing import F
73
74
75class DeprecatedOption(NamedTuple):
76 key: str
77 category: type[Warning]
78 msg: str | None
79 rkey: str | None
80 removal_ver: str | None
81
82
83class RegisteredOption(NamedTuple):
84 key: str
85 defval: Any
86 doc: str
87 validator: Callable[[object], Any] | None
88 cb: Callable[[str], Any] | None
89
90
91# holds deprecated option metadata
92_deprecated_options: dict[str, DeprecatedOption] = {}
93
94# holds registered option metadata
95_registered_options: dict[str, RegisteredOption] = {}
96
97# holds the current values for registered options
98_global_config: dict[str, Any] = {}
99
100# keys which have a special meaning
101_reserved_keys: list[str] = ["all"]
102
103
104class OptionError(AttributeError, KeyError):
105 """
106 Exception raised for pandas.options.
107
108 Backwards compatible with KeyError checks.
109
110 See Also
111 --------
112 options : Access and modify global pandas settings.
113
114 Examples
115 --------
116 >>> pd.options.context
117 Traceback (most recent call last):
118 OptionError: No such option
119 """
120
121 __module__ = "pandas.errors"
122
123
124#
125# User API
126
127
128def _get_single_key(pat: str, warn: bool = True, extra_stacklevel: int = 0) -> str:
129 keys = _select_options(pat)
130 if len(keys) == 0:
131 if warn:
132 _warn_if_deprecated(pat, extra_stacklevel=extra_stacklevel)
133 raise OptionError(f"No such keys(s): {pat!r}")
134 if len(keys) > 1:
135 raise OptionError("Pattern matched multiple keys")
136 key = keys[0]
137
138 if warn:
139 _warn_if_deprecated(key, extra_stacklevel=extra_stacklevel)
140
141 key = _translate_key(key)
142
143 return key
144
145
146def get_option(pat: str) -> Any:
147 """
148 Retrieve the value of the specified option.
149
150 This method allows users to query the current value of a given option
151 in the pandas configuration system. Options control various display,
152 performance, and behavior-related settings within pandas.
153
154 Parameters
155 ----------
156 pat : str
157 Regexp which should match a single option.
158
159 .. warning::
160
161 Partial matches are supported for convenience, but unless you use the
162 full option name (e.g. x.y.z.option_name), your code may break in future
163 versions if new options with similar names are introduced.
164
165 Returns
166 -------
167 Any
168 The value of the option.
169
170 Raises
171 ------
172 OptionError : if no such option exists
173
174 See Also
175 --------
176 set_option : Set the value of the specified option or options.
177 reset_option : Reset one or more options to their default value.
178 describe_option : Print the description for one or more registered options.
179
180 Notes
181 -----
182 For all available options, please view the :ref:`User Guide <options.available>`
183 or use ``pandas.describe_option()``.
184
185 Examples
186 --------
187 >>> pd.get_option("display.max_columns") # doctest: +SKIP
188 4
189 """
190 return _get_option_impl(pat)
191
192
193def _get_option_impl(pat: str, warn: bool = True) -> Any:
194 key = _get_single_key(pat, warn=warn)
195
196 # walk the nested dict
197 root, k = _get_root(key)
198 return root[k]
199
200
201def _set_option_impl(*args: Any, warn: bool = True, extra_stacklevel: int = 0) -> None:
202 # GH#63235: `warn` and `extra_stacklevel` let option_context warn exactly
203 # once, aimed at the caller rather than at contextlib.
204 # Handle dictionary input
205 if len(args) == 1 and isinstance(args[0], dict):
206 args = tuple(kv for item in args[0].items() for kv in item)
207
208 nargs = len(args)
209 if not nargs or nargs % 2 != 0:
210 raise ValueError("Must provide an even number of non-keyword arguments")
211
212 for k, v in zip(args[::2], args[1::2], strict=True):
213 key = _get_single_key(k, warn=warn, extra_stacklevel=extra_stacklevel)
214
215 opt = _get_registered_option(key)
216 if opt and opt.validator:
217 opt.validator(v)
218
219 # walk the nested dict
220 root, k_root = _get_root(key)
221 root[k_root] = v
222
223 if opt is not None and opt.cb:
224 opt.cb(key)
225
226
227def set_option(*args: Any) -> None:
228 """
229 Set the value of the specified option or options.
230
231 This method allows fine-grained control over the behavior and display settings
232 of pandas. Options affect various functionalities such as output formatting,
233 display limits, and operational behavior. Settings can be modified at runtime
234 without requiring changes to global configurations or environment variables.
235
236 Parameters
237 ----------
238 *args : str | object | dict
239 Arguments provided in pairs, which will be interpreted as (pattern, value),
240 or as a single dictionary containing multiple option-value pairs.
241 pattern: str
242 Regexp which should match a single option
243 value: object
244 New value of option
245
246 .. warning::
247
248 Partial pattern matches are supported for convenience, but unless you
249 use the full option name (e.g. x.y.z.option_name), your code may break in
250 future versions if new options with similar names are introduced.
251
252 Returns
253 -------
254 None
255 No return value.
256
257 Raises
258 ------
259 ValueError if odd numbers of non-keyword arguments are provided
260 TypeError if keyword arguments are provided
261 OptionError if no such option exists
262
263 See Also
264 --------
265 get_option : Retrieve the value of the specified option.
266 reset_option : Reset one or more options to their default value.
267 describe_option : Print the description for one or more registered options.
268 option_context : Context manager to temporarily set options in a ``with``
269 statement.
270
271 Notes
272 -----
273 For all available options, please view the :ref:`User Guide <options.available>`
274 or use ``pandas.describe_option()``.
275
276 Examples
277 --------
278 Option-Value Pair Input:
279
280 >>> pd.set_option("display.max_columns", 4)
281 >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
282 >>> df
283 0 1 ... 3 4
284 0 1 2 ... 4 5
285 1 6 7 ... 9 10
286 [2 rows x 5 columns]
287 >>> pd.reset_option("display.max_columns")
288
289 Dictionary Input:
290
291 >>> pd.set_option({"display.max_columns": 4, "display.precision": 1})
292 >>> df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
293 >>> df
294 0 1 ... 3 4
295 0 1 2 ... 4 5
296 1 6 7 ... 9 10
297 [2 rows x 5 columns]
298 >>> pd.reset_option("display.max_columns")
299 >>> pd.reset_option("display.precision")
300 """
301 _set_option_impl(*args)
302
303
304def describe_option(pat: str = "", _print_desc: bool = True) -> str | None:
305 """
306 Print the description for one or more registered options.
307
308 Call with no arguments to get a listing for all registered options.
309
310 Parameters
311 ----------
312 pat : str, default ""
313 String or string regexp pattern.
314 Empty string will return all options.
315 For regexp strings, all matching keys will have their description displayed.
316 _print_desc : bool, default True
317 If True (default) the description(s) will be printed to stdout.
318 Otherwise, the description(s) will be returned as a string
319 (for testing).
320
321 Returns
322 -------
323 None
324 If ``_print_desc=True``.
325 str
326 If the description(s) as a string if ``_print_desc=False``.
327
328 See Also
329 --------
330 get_option : Retrieve the value of the specified option.
331 set_option : Set the value of the specified option or options.
332 reset_option : Reset one or more options to their default value.
333
334 Notes
335 -----
336 For all available options, please view the
337 :ref:`User Guide <options.available>`.
338
339 Examples
340 --------
341 >>> pd.describe_option("display.max_columns") # doctest: +SKIP
342 display.max_columns : int
343 If max_cols is exceeded, switch to truncate view...
344 """
345 keys = _select_options(pat)
346 if len(keys) == 0:
347 raise OptionError(f"No such keys(s) for {pat=}")
348
349 s = "\n".join([_build_option_description(k) for k in keys])
350
351 if _print_desc:
352 print(s)
353 return None
354 return s
355
356
357def reset_option(pat: str) -> None:
358 """
359 Reset one or more options to their default value.
360
361 This method resets the specified pandas option(s) back to their default
362 values. It allows partial string matching for convenience, but users should
363 exercise caution to avoid unintended resets due to changes in option names
364 in future versions.
365
366 Parameters
367 ----------
368 pat : str/regex
369 If specified only options matching ``pat*`` will be reset.
370 Pass ``"all"`` as argument to reset all options.
371
372 .. warning::
373
374 Partial matches are supported for convenience, but unless you
375 use the full option name (e.g. x.y.z.option_name), your code may break
376 in future versions if new options with similar names are introduced.
377
378 Returns
379 -------
380 None
381 No return value.
382
383 See Also
384 --------
385 get_option : Retrieve the value of the specified option.
386 set_option : Set the value of the specified option or options.
387 describe_option : Print the description for one or more registered options.
388
389 Notes
390 -----
391 For all available options, please view the
392 :ref:`User Guide <options.available>`.
393
394 Examples
395 --------
396 >>> pd.reset_option("display.max_columns") # doctest: +SKIP
397 """
398 keys = _select_options(pat)
399
400 if len(keys) == 0:
401 raise OptionError(f"No such keys(s) for {pat=}")
402
403 if len(keys) > 1 and len(pat) < 4 and pat != "all":
404 raise ValueError(
405 "You must specify at least 4 characters when "
406 "resetting multiple keys, use the special keyword "
407 '"all" to reset all the options to their default value'
408 )
409
410 for k in keys:
411 set_option(k, _registered_options[k].defval)
412
413
414def get_default_val(pat: str) -> Any:
415 key = _get_single_key(pat)
416 opt = _get_registered_option(key)
417 assert opt is not None
418 return opt.defval
419
420
421class DictWrapper:
422 """provide attribute-style access to a nested dict"""
423
424 d: dict[str, Any]
425
426 def __init__(self, d: dict[str, Any], prefix: str = "") -> None:
427 object.__setattr__(self, "d", d)
428 object.__setattr__(self, "prefix", prefix)
429
430 def __setattr__(self, key: str, val: Any) -> None:
431 prefix = object.__getattribute__(self, "prefix")
432 if prefix:
433 prefix += "."
434 prefix += key
435 # you can't set new keys
436 # can you can't overwrite subtrees
437 if key in self.d and not isinstance(self.d[key], dict):
438 set_option(prefix, val)
439 else:
440 raise OptionError("You can only set the value of existing options")
441
442 def __getattr__(self, key: str) -> Any:
443 prefix = object.__getattribute__(self, "prefix")
444 if prefix:
445 prefix += "."
446 prefix += key
447 try:
448 v = object.__getattribute__(self, "d")[key]
449 except KeyError as err:
450 raise OptionError("No such option") from err
451 if isinstance(v, dict):
452 return DictWrapper(v, prefix)
453 else:
454 return get_option(prefix)
455
456 def __dir__(self) -> list[str]:
457 return list(self.d.keys())
458
459
460options = DictWrapper(_global_config)
461# DictWrapper defines a custom setattr
462object.__setattr__(options, "__module__", "pandas")
463
464#
465# Functions for use by pandas developers, in addition to User - api
466
467
468@contextmanager
469def option_context(*args: Any) -> Generator[None]:
470 """
471 Context manager to temporarily set options in a ``with`` statement.
472
473 This method allows users to set one or more pandas options temporarily
474 within a controlled block. The previous options' values are restored
475 once the block is exited. This is useful when making temporary adjustments
476 to pandas' behavior without affecting the global state.
477
478 Parameters
479 ----------
480 *args : str | object | dict
481 An even amount of arguments provided in pairs which will be
482 interpreted as (pattern, value) pairs. Alternatively, a single
483 dictionary of {pattern: value} may be provided.
484
485 Returns
486 -------
487 None
488 No return value.
489
490 Yields
491 ------
492 None
493 No yield value.
494
495 See Also
496 --------
497 get_option : Retrieve the value of the specified option.
498 set_option : Set the value of the specified option.
499 reset_option : Reset one or more options to their default value.
500 describe_option : Print the description for one or more registered options.
501
502 Notes
503 -----
504 For all available options, please view the :ref:`User Guide <options.available>`
505 or use ``pandas.describe_option()``.
506
507 Examples
508 --------
509 >>> from pandas import option_context
510 >>> with option_context("display.max_rows", 10, "display.max_columns", 5):
511 ... pass
512 >>> with option_context({"display.max_rows": 10, "display.max_columns": 5}):
513 ... pass
514 """
515 if len(args) == 1 and isinstance(args[0], dict):
516 args = tuple(kv for item in args[0].items() for kv in item)
517
518 if len(args) % 2 != 0 or len(args) < 2:
519 raise ValueError(
520 "Provide an even amount of arguments as "
521 "option_context(pat, val, pat, val...)."
522 )
523
524 ops = tuple(zip(args[::2], args[1::2], strict=True))
525 undo: tuple[tuple[Any, Any], ...] = ()
526 try:
527 # GH#63235: a deprecated option must warn exactly once per option_context,
528 # so only the entry below warns; reading the old value and restoring it are
529 # bookkeeping the user did not ask for. extra_stacklevel=1 skips the
530 # contextlib.__enter__ frame so the warning lands on the ``with`` statement.
531 undo = tuple((pat, _get_option_impl(pat, warn=False)) for pat, val in ops)
532 for pat, val in ops:
533 _set_option_impl(pat, val, extra_stacklevel=1)
534 yield
535 finally:
536 for pat, val in undo:
537 _set_option_impl(pat, val, warn=False)
538
539
540def register_option(
541 key: str,
542 defval: object,
543 doc: str = "",
544 validator: Callable[[object], Any] | None = None,
545 cb: Callable[[str], Any] | None = None,
546) -> None:
547 """
548 Register an option in the package-wide pandas config object
549
550 Parameters
551 ----------
552 key : str
553 Fully-qualified key, e.g. "x.y.option - z".
554 defval : object
555 Default value of the option.
556 doc : str
557 Description of the option.
558 validator : Callable, optional
559 Function of a single argument, should raise `ValueError` if
560 called with a value which is not a legal value for the option.
561 cb
562 a function of a single argument "key", which is called
563 immediately after an option value is set/reset. key is
564 the full name of the option.
565
566 Raises
567 ------
568 ValueError if `validator` is specified and `defval` is not a valid value.
569
570 """
571 import keyword
572 import tokenize
573
574 key = key.lower()
575
576 if key in _registered_options:
577 raise OptionError(f"Option '{key}' has already been registered")
578 if key in _reserved_keys:
579 raise OptionError(f"Option '{key}' is a reserved key")
580
581 # the default value should be legal
582 if validator:
583 validator(defval)
584
585 # walk the nested dict, creating dicts as needed along the path
586 path = key.split(".")
587
588 for k in path:
589 if not re.match("^" + tokenize.Name + "$", k):
590 raise ValueError(f"{k} is not a valid identifier")
591 if keyword.iskeyword(k):
592 raise ValueError(f"{k} is a python keyword")
593
594 cursor = _global_config
595 msg = "Path prefix to option '{option}' is already an option"
596
597 for i, p in enumerate(path[:-1]):
598 if not isinstance(cursor, dict):
599 raise OptionError(msg.format(option=".".join(path[:i])))
600 if p not in cursor:
601 cursor[p] = {}
602 cursor = cursor[p]
603
604 if not isinstance(cursor, dict):
605 raise OptionError(msg.format(option=".".join(path[:-1])))
606
607 # a namespace already lives here, i.e. `key` is a path prefix to one or
608 # more already-registered options; registering it would clobber them
609 # (GH#29242)
610 if isinstance(cursor.get(path[-1]), dict):
611 raise OptionError(f"Option '{key}' is a prefix of an already-registered option")
612
613 cursor[path[-1]] = defval # initialize
614
615 # save the option metadata
616 _registered_options[key] = RegisteredOption(
617 key=key, defval=defval, doc=doc, validator=validator, cb=cb
618 )
619
620
621def deprecate_option(
622 key: str,
623 category: type[Warning],
624 msg: str | None = None,
625 rkey: str | None = None,
626 removal_ver: str | None = None,
627) -> None:
628 """
629 Mark option `key` as deprecated, if code attempts to access this option,
630 a warning will be produced, using `msg` if given, or a default message
631 if not.
632 if `rkey` is given, any access to the key will be re-routed to `rkey`.
633
634 Neither the existence of `key` nor that if `rkey` is checked. If they
635 do not exist, any subsequence access will fail as usual, after the
636 deprecation warning is given.
637
638 Parameters
639 ----------
640 key : str
641 Name of the option to be deprecated.
642 must be a fully-qualified option name (e.g "x.y.z.rkey").
643 category : Warning
644 Warning class for the deprecation.
645 msg : str, optional
646 Warning message to output when the key is referenced.
647 if no message is given a default message will be emitted.
648 rkey : str, optional
649 Name of an option to reroute access to.
650 If specified, any referenced `key` will be
651 re-routed to `rkey` including set/get/reset.
652 rkey must be a fully-qualified option name (e.g "x.y.z.rkey").
653 used by the default message if no `msg` is specified.
654 removal_ver : str, optional
655 Specifies the version in which this option will
656 be removed. used by the default message if no `msg` is specified.
657
658 Raises
659 ------
660 OptionError
661 If the specified key has already been deprecated.
662 """
663 key = key.lower()
664
665 if key in _deprecated_options:
666 raise OptionError(f"Option '{key}' has already been defined as deprecated.")
667
668 _deprecated_options[key] = DeprecatedOption(key, category, msg, rkey, removal_ver)
669
670
671#
672# functions internal to the module
673
674
675def _select_options(pat: str) -> list[str]:
676 """
677 returns a list of keys matching `pat`
678
679 if pat=="all", returns all registered options
680 """
681 # short-circuit for exact key
682 if pat in _registered_options:
683 return [pat]
684
685 # else look through all of them
686 keys = sorted(_registered_options.keys())
687 if pat == "all": # reserved key
688 return keys
689
690 return [k for k in keys if re.search(pat, k, re.I)]
691
692
693def _get_root(key: str) -> tuple[dict[str, Any], str]:
694 path = key.split(".")
695 cursor = _global_config
696 for p in path[:-1]:
697 cursor = cursor[p]
698 return cursor, path[-1]
699
700
701def _get_deprecated_option(key: str) -> DeprecatedOption | None:
702 """
703 Retrieves the metadata for a deprecated option, if `key` is deprecated.
704
705 Returns
706 -------
707 DeprecatedOption (namedtuple) if key is deprecated, None otherwise
708 """
709 try:
710 d = _deprecated_options[key]
711 except KeyError:
712 return None
713 else:
714 return d
715
716
717def _get_registered_option(key: str) -> RegisteredOption | None:
718 """
719 Retrieves the option metadata if `key` is a registered option.
720
721 Returns
722 -------
723 RegisteredOption (namedtuple) if key is deprecated, None otherwise
724 """
725 return _registered_options.get(key)
726
727
728def _translate_key(key: str) -> str:
729 """
730 if `key` is deprecated and a replacement key defined, will return the
731 replacement key, otherwise returns `key` as-is
732 """
733 d = _get_deprecated_option(key)
734 if d:
735 return d.rkey or key
736 else:
737 return key
738
739
740def _warn_if_deprecated(key: str, extra_stacklevel: int = 0) -> bool:
741 """
742 Checks if `key` is a deprecated option and if so, prints a warning.
743
744 Returns
745 -------
746 bool - True if `key` is deprecated, False otherwise.
747 """
748 d = _get_deprecated_option(key)
749 if d:
750 stacklevel = find_stack_level() + extra_stacklevel
751 if d.msg:
752 warnings.warn(
753 d.msg,
754 d.category,
755 stacklevel=stacklevel,
756 )
757 else:
758 msg = f"'{key}' is deprecated"
759 if d.removal_ver:
760 msg += f" and will be removed in {d.removal_ver}"
761 if d.rkey:
762 msg += f", please use '{d.rkey}' instead."
763 else:
764 msg += ", please refrain from using it."
765
766 warnings.warn(
767 msg,
768 d.category,
769 stacklevel=stacklevel,
770 )
771 return True
772 return False
773
774
775def _build_option_description(k: str) -> str:
776 """Builds a formatted description of a registered option and prints it"""
777 o = _get_registered_option(k)
778 d = _get_deprecated_option(k)
779 assert o is not None
780
781 s = f"{k} "
782
783 if o.doc:
784 s += "\n".join(o.doc.strip().split("\n"))
785 else:
786 s += "No description available."
787
788 if o:
789 with warnings.catch_warnings():
790 warnings.simplefilter("ignore", FutureWarning)
791 warnings.simplefilter("ignore", DeprecationWarning)
792 s += f"\n [default: {o.defval}] [currently: {get_option(k)}]"
793
794 if d:
795 rkey = d.rkey or ""
796 s += "\n (Deprecated"
797 s += f", use `{rkey}` instead."
798 s += ")"
799
800 return s
801
802
803# helpers
804
805
806@contextmanager
807def config_prefix(prefix: str) -> Generator[None]:
808 """
809 contextmanager for multiple invocations of API with a common prefix
810
811 supported API functions: (register / get / set )__option
812
813 Warning: This is not thread - safe, and won't work properly if you import
814 the API functions into your module using the "from x import y" construct.
815
816 Example
817 -------
818 import pandas._config.config as cf
819 with cf.config_prefix("display.font"):
820 cf.register_option("color", "red")
821 cf.register_option("size", " 5 pt")
822 cf.set_option(size, " 6 pt")
823 cf.get_option(size)
824 ...
825
826 etc'
827
828 will register options "display.font.color", "display.font.size", set the
829 value of "display.font.size"... and so on.
830 """
831 # Note: reset_option relies on set_option, and on key directly
832 # it does not fit in to this monkey-patching scheme
833
834 global register_option, get_option, set_option
835
836 def wrap(func: F) -> F:
837 def inner(key: str, *args: object, **kwds: object) -> object:
838 pkey = f"{prefix}.{key}"
839 return func(pkey, *args, **kwds)
840
841 return cast("F", inner)
842
843 _register_option = register_option
844 _get_option = get_option
845 _set_option = set_option
846 set_option = wrap(set_option)
847 get_option = wrap(get_option)
848 register_option = wrap(register_option)
849 try:
850 yield
851 finally:
852 set_option = _set_option
853 get_option = _get_option
854 register_option = _register_option
855
856
857# These factories and methods are handy for use as the validator
858# arg in register_option
859
860
861def is_type_factory(_type: type[Any]) -> Callable[[Any], None]:
862 """
863
864 Parameters
865 ----------
866 `_type` - a type to be compared against (e.g. type(x) == `_type`)
867
868 Returns
869 -------
870 validator - a function of a single argument x , which raises
871 ValueError if type(x) is not equal to `_type`
872
873 """
874
875 def inner(x: object) -> None:
876 if type(x) != _type:
877 raise ValueError(f"Value must have type '{_type}'")
878
879 return inner
880
881
882def is_instance_factory(_type: type | tuple[type, ...]) -> Callable[[Any], None]:
883 """
884
885 Parameters
886 ----------
887 `_type` - the type to be checked against
888
889 Returns
890 -------
891 validator - a function of a single argument x , which raises
892 ValueError if x is not an instance of `_type`
893
894 """
895 if isinstance(_type, tuple):
896 type_repr = "|".join(map(str, _type))
897 else:
898 type_repr = f"'{_type}'"
899
900 def inner(x: object) -> None:
901 if not isinstance(x, _type):
902 raise ValueError(f"Value must be an instance of {type_repr}")
903
904 return inner
905
906
907def is_one_of_factory(legal_values: Sequence[Any]) -> Callable[[Any], None]:
908 callables = [c for c in legal_values if callable(c)]
909 legal_values = [c for c in legal_values if not callable(c)]
910
911 def inner(x: object) -> None:
912 if x not in legal_values:
913 if not any(c(x) for c in callables):
914 uvals = [str(lval) for lval in legal_values]
915 pp_values = "|".join(uvals)
916 msg = f"Value must be one of {pp_values}"
917 if len(callables):
918 msg += " or a callable"
919 raise ValueError(msg)
920
921 return inner
922
923
924def is_nonnegative_int(value: object) -> None:
925 """
926 Verify that value is None or a positive int.
927
928 Parameters
929 ----------
930 value : None or int
931 The `value` to be checked.
932
933 Raises
934 ------
935 ValueError
936 When the value is not None or is a negative integer
937 """
938 if value is None:
939 return
940
941 elif isinstance(value, int):
942 if value >= 0:
943 return
944
945 msg = "Value must be a nonnegative integer or None"
946 raise ValueError(msg)
947
948
949# common type validators, for convenience
950# usage: register_option(... , validator = is_int)
951is_int = is_type_factory(int)
952is_bool = is_type_factory(bool)
953is_float = is_type_factory(float)
954is_str = is_type_factory(str)
955is_text = is_instance_factory((str, bytes))
956
957
958def is_callable(obj: object) -> bool:
959 """
960
961 Parameters
962 ----------
963 `obj` - the object to be checked
964
965 Returns
966 -------
967 validator - returns True if object is callable
968 raises ValueError otherwise.
969
970 """
971 if not callable(obj):
972 raise ValueError("Value must be a callable")
973 return True
974
975
976# import set_module here would cause circular import
977get_option.__module__ = "pandas"
978set_option.__module__ = "pandas"
979describe_option.__module__ = "pandas"
980reset_option.__module__ = "pandas"
981option_context.__module__ = "pandas"