Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 collections import namedtuple 

52from contextlib import contextmanager 

53import re 

54from typing import Any, Dict, Iterable, List 

55import warnings 

56 

57DeprecatedOption = namedtuple("DeprecatedOption", "key msg rkey removal_ver") 

58RegisteredOption = namedtuple("RegisteredOption", "key defval doc validator cb") 

59 

60# holds deprecated option metadata 

61_deprecated_options: Dict[str, DeprecatedOption] = {} 

62 

63# holds registered option metadata 

64_registered_options: Dict[str, RegisteredOption] = {} 

65 

66# holds the current values for registered options 

67_global_config: Dict[str, Any] = {} 

68 

69# keys which have a special meaning 

70_reserved_keys: List[str] = ["all"] 

71 

72 

73class OptionError(AttributeError, KeyError): 

74 """Exception for pandas.options, backwards compatible with KeyError 

75 checks 

76 """ 

77 

78 

79# 

80# User API 

81 

82 

83def _get_single_key(pat, silent): 

84 keys = _select_options(pat) 

85 if len(keys) == 0: 

86 if not silent: 

87 _warn_if_deprecated(pat) 

88 raise OptionError(f"No such keys(s): {repr(pat)}") 

89 if len(keys) > 1: 

90 raise OptionError("Pattern matched multiple keys") 

91 key = keys[0] 

92 

93 if not silent: 

94 _warn_if_deprecated(key) 

95 

96 key = _translate_key(key) 

97 

98 return key 

99 

100 

101def _get_option(pat, silent=False): 

102 key = _get_single_key(pat, silent) 

103 

104 # walk the nested dict 

105 root, k = _get_root(key) 

106 return root[k] 

107 

108 

109def _set_option(*args, **kwargs): 

110 # must at least 1 arg deal with constraints later 

111 nargs = len(args) 

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

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

114 

115 # default to false 

116 silent = kwargs.pop("silent", False) 

117 

118 if kwargs: 

119 kwarg = list(kwargs.keys())[0] 

120 raise TypeError(f'_set_option() got an unexpected keyword argument "{kwarg}"') 

121 

122 for k, v in zip(args[::2], args[1::2]): 

123 key = _get_single_key(k, silent) 

124 

125 o = _get_registered_option(key) 

126 if o and o.validator: 

127 o.validator(v) 

128 

129 # walk the nested dict 

130 root, k = _get_root(key) 

131 root[k] = v 

132 

133 if o.cb: 

134 if silent: 

135 with warnings.catch_warnings(record=True): 

136 o.cb(key) 

137 else: 

138 o.cb(key) 

139 

140 

141def _describe_option(pat="", _print_desc=True): 

142 

143 keys = _select_options(pat) 

144 if len(keys) == 0: 

145 raise OptionError("No such keys(s)") 

146 

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

148 

149 if _print_desc: 

150 print(s) 

151 else: 

152 return s 

153 

154 

155def _reset_option(pat, silent=False): 

156 

157 keys = _select_options(pat) 

158 

159 if len(keys) == 0: 

160 raise OptionError("No such keys(s)") 

161 

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

163 raise ValueError( 

164 "You must specify at least 4 characters when " 

165 "resetting multiple keys, use the special keyword " 

166 '"all" to reset all the options to their default ' 

167 "value" 

168 ) 

169 

170 for k in keys: 

171 _set_option(k, _registered_options[k].defval, silent=silent) 

172 

173 

174def get_default_val(pat): 

175 key = _get_single_key(pat, silent=True) 

176 return _get_registered_option(key).defval 

177 

178 

179class DictWrapper: 

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

181 

182 def __init__(self, d, prefix=""): 

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

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

185 

186 def __setattr__(self, key, val): 

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

188 if prefix: 

189 prefix += "." 

190 prefix += key 

191 # you can't set new keys 

192 # can you can't overwrite subtrees 

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

194 _set_option(prefix, val) 

195 else: 

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

197 

198 def __getattr__(self, key: str): 

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

200 if prefix: 

201 prefix += "." 

202 prefix += key 

203 try: 

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

205 except KeyError: 

206 raise OptionError("No such option") 

207 if isinstance(v, dict): 

208 return DictWrapper(v, prefix) 

209 else: 

210 return _get_option(prefix) 

211 

212 def __dir__(self): 

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

214 

215 

216# For user convenience, we'd like to have the available options described 

217# in the docstring. For dev convenience we'd like to generate the docstrings 

218# dynamically instead of maintaining them by hand. To this, we use the 

219# class below which wraps functions inside a callable, and converts 

220# __doc__ into a property function. The doctsrings below are templates 

221# using the py2.6+ advanced formatting syntax to plug in a concise list 

222# of options, and option descriptions. 

223 

224 

225class CallableDynamicDoc: 

226 def __init__(self, func, doc_tmpl): 

227 self.__doc_tmpl__ = doc_tmpl 

228 self.__func__ = func 

229 

230 def __call__(self, *args, **kwds): 

231 return self.__func__(*args, **kwds) 

232 

233 @property 

234 def __doc__(self): 

235 opts_desc = _describe_option("all", _print_desc=False) 

236 opts_list = pp_options_list(list(_registered_options.keys())) 

237 return self.__doc_tmpl__.format(opts_desc=opts_desc, opts_list=opts_list) 

238 

239 

240_get_option_tmpl = """ 

241get_option(pat) 

242 

243Retrieves the value of the specified option. 

244 

245Available options: 

246 

247{opts_list} 

248 

249Parameters 

250---------- 

251pat : str 

252 Regexp which should match a single option. 

253 Note: partial matches are supported for convenience, but unless you use the 

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

255 versions if new options with similar names are introduced. 

256 

257Returns 

258------- 

259result : the value of the option 

260 

261Raises 

262------ 

263OptionError : if no such option exists 

264 

265Notes 

266----- 

267The available options with its descriptions: 

268 

269{opts_desc} 

270""" 

271 

272_set_option_tmpl = """ 

273set_option(pat, value) 

274 

275Sets the value of the specified option. 

276 

277Available options: 

278 

279{opts_list} 

280 

281Parameters 

282---------- 

283pat : str 

284 Regexp which should match a single option. 

285 Note: partial matches are supported for convenience, but unless you use the 

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

287 versions if new options with similar names are introduced. 

288value : object 

289 New value of option. 

290 

291Returns 

292------- 

293None 

294 

295Raises 

296------ 

297OptionError if no such option exists 

298 

299Notes 

300----- 

301The available options with its descriptions: 

302 

303{opts_desc} 

304""" 

305 

306_describe_option_tmpl = """ 

307describe_option(pat, _print_desc=False) 

308 

309Prints the description for one or more registered options. 

310 

311Call with not arguments to get a listing for all registered options. 

312 

313Available options: 

314 

315{opts_list} 

316 

317Parameters 

318---------- 

319pat : str 

320 Regexp pattern. All matching keys will have their description displayed. 

321_print_desc : bool, default True 

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

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

324 (for testing). 

325 

326Returns 

327------- 

328None by default, the description(s) as a unicode string if _print_desc 

329is False 

330 

331Notes 

332----- 

333The available options with its descriptions: 

334 

335{opts_desc} 

336""" 

337 

338_reset_option_tmpl = """ 

339reset_option(pat) 

340 

341Reset one or more options to their default value. 

342 

343Pass "all" as argument to reset all options. 

344 

345Available options: 

346 

347{opts_list} 

348 

349Parameters 

350---------- 

351pat : str/regex 

352 If specified only options matching `prefix*` will be reset. 

353 Note: partial matches are supported for convenience, but unless you 

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

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

356 

357Returns 

358------- 

359None 

360 

361Notes 

362----- 

363The available options with its descriptions: 

364 

365{opts_desc} 

366""" 

367 

368# bind the functions with their docstrings into a Callable 

369# and use that as the functions exposed in pd.api 

370get_option = CallableDynamicDoc(_get_option, _get_option_tmpl) 

371set_option = CallableDynamicDoc(_set_option, _set_option_tmpl) 

372reset_option = CallableDynamicDoc(_reset_option, _reset_option_tmpl) 

373describe_option = CallableDynamicDoc(_describe_option, _describe_option_tmpl) 

374options = DictWrapper(_global_config) 

375 

376# 

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

378 

379 

380class option_context: 

381 """ 

382 Context manager to temporarily set options in the `with` statement context. 

383 

384 You need to invoke as ``option_context(pat, val, [(pat, val), ...])``. 

385 

386 Examples 

387 -------- 

388 

389 >>> with option_context('display.max_rows', 10, 'display.max_columns', 5): 

390 ... ... 

391 """ 

392 

393 def __init__(self, *args): 

394 if not (len(args) % 2 == 0 and len(args) >= 2): 

395 raise ValueError( 

396 "Need to invoke as option_context(pat, val, [(pat, val), ...])." 

397 ) 

398 

399 self.ops = list(zip(args[::2], args[1::2])) 

400 

401 def __enter__(self): 

402 self.undo = [(pat, _get_option(pat, silent=True)) for pat, val in self.ops] 

403 

404 for pat, val in self.ops: 

405 _set_option(pat, val, silent=True) 

406 

407 def __exit__(self, *args): 

408 if self.undo: 

409 for pat, val in self.undo: 

410 _set_option(pat, val, silent=True) 

411 

412 

413def register_option(key: str, defval: object, doc="", validator=None, cb=None): 

414 """Register an option in the package-wide pandas config object 

415 

416 Parameters 

417 ---------- 

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

419 defval - the default value of the option 

420 doc - a string description of the option 

421 validator - a function of a single argument, should raise `ValueError` if 

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

423 cb - a function of a single argument "key", which is called 

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

425 the full name of the option. 

426 

427 Returns 

428 ------- 

429 Nothing. 

430 

431 Raises 

432 ------ 

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

434 

435 """ 

436 import tokenize 

437 import keyword 

438 

439 key = key.lower() 

440 

441 if key in _registered_options: 

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

443 if key in _reserved_keys: 

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

445 

446 # the default value should be legal 

447 if validator: 

448 validator(defval) 

449 

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

451 path = key.split(".") 

452 

453 for k in path: 

454 # NOTE: tokenize.Name is not a public constant 

455 # error: Module has no attribute "Name" [attr-defined] 

456 if not re.match("^" + tokenize.Name + "$", k): # type: ignore 

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

458 if keyword.iskeyword(k): 

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

460 

461 cursor = _global_config 

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

463 

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

465 if not isinstance(cursor, dict): 

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

467 if p not in cursor: 

468 cursor[p] = {} 

469 cursor = cursor[p] 

470 

471 if not isinstance(cursor, dict): 

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

473 

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

475 

476 # save the option metadata 

477 _registered_options[key] = RegisteredOption( 

478 key=key, defval=defval, doc=doc, validator=validator, cb=cb 

479 ) 

480 

481 

482def deprecate_option(key, msg=None, rkey=None, removal_ver=None): 

483 """ 

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

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

486 if not. 

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

488 

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

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

491 deprecation warning is given. 

492 

493 Parameters 

494 ---------- 

495 key - the name of the option to be deprecated. must be a fully-qualified 

496 option name (e.g "x.y.z.rkey"). 

497 

498 msg - (Optional) a warning message to output when the key is referenced. 

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

500 

501 rkey - (Optional) the name of an option to reroute access to. 

502 If specified, any referenced `key` will be re-routed to `rkey` 

503 including set/get/reset. 

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

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

506 

507 removal_ver - (Optional) specifies the version in which this option will 

508 be removed. used by the default message if no `msg` 

509 is specified. 

510 

511 Returns 

512 ------- 

513 Nothing 

514 

515 Raises 

516 ------ 

517 OptionError - if key has already been deprecated. 

518 

519 """ 

520 

521 key = key.lower() 

522 

523 if key in _deprecated_options: 

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

525 

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

527 

528 

529# 

530# functions internal to the module 

531 

532 

533def _select_options(pat): 

534 """returns a list of keys matching `pat` 

535 

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

537 """ 

538 

539 # short-circuit for exact key 

540 if pat in _registered_options: 

541 return [pat] 

542 

543 # else look through all of them 

544 keys = sorted(_registered_options.keys()) 

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

546 return keys 

547 

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

549 

550 

551def _get_root(key): 

552 path = key.split(".") 

553 cursor = _global_config 

554 for p in path[:-1]: 

555 cursor = cursor[p] 

556 return cursor, path[-1] 

557 

558 

559def _is_deprecated(key): 

560 """ Returns True if the given option has been deprecated """ 

561 

562 key = key.lower() 

563 return key in _deprecated_options 

564 

565 

566def _get_deprecated_option(key): 

567 """ 

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

569 

570 Returns 

571 ------- 

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

573 """ 

574 

575 try: 

576 d = _deprecated_options[key] 

577 except KeyError: 

578 return None 

579 else: 

580 return d 

581 

582 

583def _get_registered_option(key): 

584 """ 

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

586 

587 Returns 

588 ------- 

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

590 """ 

591 return _registered_options.get(key) 

592 

593 

594def _translate_key(key): 

595 """ 

596 if key id deprecated and a replacement key defined, will return the 

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

598 """ 

599 

600 d = _get_deprecated_option(key) 

601 if d: 

602 return d.rkey or key 

603 else: 

604 return key 

605 

606 

607def _warn_if_deprecated(key): 

608 """ 

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

610 

611 Returns 

612 ------- 

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

614 """ 

615 

616 d = _get_deprecated_option(key) 

617 if d: 

618 if d.msg: 

619 print(d.msg) 

620 warnings.warn(d.msg, FutureWarning) 

621 else: 

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

623 if d.removal_ver: 

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

625 if d.rkey: 

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

627 else: 

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

629 

630 warnings.warn(msg, FutureWarning) 

631 return True 

632 return False 

633 

634 

635def _build_option_description(k): 

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

637 

638 o = _get_registered_option(k) 

639 d = _get_deprecated_option(k) 

640 

641 s = f"{k} " 

642 

643 if o.doc: 

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

645 else: 

646 s += "No description available." 

647 

648 if o: 

649 s += f"\n [default: {o.defval}] [currently: {_get_option(k, True)}]" 

650 

651 if d: 

652 rkey = d.rkey if d.rkey else "" 

653 s += "\n (Deprecated" 

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

655 s += ")" 

656 

657 return s 

658 

659 

660def pp_options_list(keys, width=80, _print=False): 

661 """ Builds a concise listing of available options, grouped by prefix """ 

662 

663 from textwrap import wrap 

664 from itertools import groupby 

665 

666 def pp(name: str, ks: Iterable[str]) -> List[str]: 

667 pfx = "- " + name + ".[" if name else "" 

668 ls = wrap( 

669 ", ".join(ks), 

670 width, 

671 initial_indent=pfx, 

672 subsequent_indent=" ", 

673 break_long_words=False, 

674 ) 

675 if ls and ls[-1] and name: 

676 ls[-1] = ls[-1] + "]" 

677 return ls 

678 

679 ls: List[str] = [] 

680 singles = [x for x in sorted(keys) if x.find(".") < 0] 

681 if singles: 

682 ls += pp("", singles) 

683 keys = [x for x in keys if x.find(".") >= 0] 

684 

685 for k, g in groupby(sorted(keys), lambda x: x[: x.rfind(".")]): 

686 ks = [x[len(k) + 1 :] for x in list(g)] 

687 ls += pp(k, ks) 

688 s = "\n".join(ls) 

689 if _print: 

690 print(s) 

691 else: 

692 return s 

693 

694 

695# 

696# helpers 

697 

698 

699@contextmanager 

700def config_prefix(prefix): 

701 """contextmanager for multiple invocations of API with a common prefix 

702 

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

704 

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

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

707 

708 Example: 

709 

710 import pandas._config.config as cf 

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

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

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

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

715 cf.get_option(size) 

716 ... 

717 

718 etc' 

719 

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

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

722 """ 

723 

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

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

726 

727 global register_option, get_option, set_option, reset_option 

728 

729 def wrap(func): 

730 def inner(key, *args, **kwds): 

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

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

733 

734 return inner 

735 

736 _register_option = register_option 

737 _get_option = get_option 

738 _set_option = set_option 

739 set_option = wrap(set_option) 

740 get_option = wrap(get_option) 

741 register_option = wrap(register_option) 

742 yield None 

743 set_option = _set_option 

744 get_option = _get_option 

745 register_option = _register_option 

746 

747 

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

749# arg in register_option 

750 

751 

752def is_type_factory(_type): 

753 """ 

754 

755 Parameters 

756 ---------- 

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

758 

759 Returns 

760 ------- 

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

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

763 

764 """ 

765 

766 def inner(x): 

767 if type(x) != _type: 

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

769 

770 return inner 

771 

772 

773def is_instance_factory(_type): 

774 """ 

775 

776 Parameters 

777 ---------- 

778 `_type` - the type to be checked against 

779 

780 Returns 

781 ------- 

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

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

784 

785 """ 

786 

787 if isinstance(_type, (tuple, list)): 

788 _type = tuple(_type) 

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

790 else: 

791 type_repr = f"'{_type}'" 

792 

793 def inner(x): 

794 if not isinstance(x, _type): 

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

796 

797 return inner 

798 

799 

800def is_one_of_factory(legal_values): 

801 

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

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

804 

805 def inner(x): 

806 if x not in legal_values: 

807 

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

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

810 pp_values = "|".join(uvals) 

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

812 if len(callables): 

813 msg += " or a callable" 

814 raise ValueError(msg) 

815 

816 return inner 

817 

818 

819def is_nonnegative_int(value): 

820 """ 

821 Verify that value is None or a positive int. 

822 

823 Parameters 

824 ---------- 

825 value : None or int 

826 The `value` to be checked. 

827 

828 Raises 

829 ------ 

830 ValueError 

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

832 """ 

833 

834 if value is None: 

835 return 

836 

837 elif isinstance(value, int): 

838 if value >= 0: 

839 return 

840 

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

842 raise ValueError(msg) 

843 

844 

845# common type validators, for convenience 

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

847is_int = is_type_factory(int) 

848is_bool = is_type_factory(bool) 

849is_float = is_type_factory(float) 

850is_str = is_type_factory(str) 

851is_text = is_instance_factory((str, bytes)) 

852 

853 

854def is_callable(obj): 

855 """ 

856 

857 Parameters 

858 ---------- 

859 `obj` - the object to be checked 

860 

861 Returns 

862 ------- 

863 validator - returns True if object is callable 

864 raises ValueError otherwise. 

865 

866 """ 

867 if not callable(obj): 

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

869 return True