config.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. """
  2. The config module holds package-wide configurables and provides
  3. a uniform API for working with them.
  4. Overview
  5. ========
  6. This module supports the following requirements:
  7. - options are referenced using keys in dot.notation, e.g. "x.y.option - z".
  8. - keys are case-insensitive.
  9. - functions should accept partial/regex keys, when unambiguous.
  10. - options can be registered by modules at import time.
  11. - options can be registered at init-time (via core.config_init)
  12. - options have a default value, and (optionally) a description and
  13. validation function associated with them.
  14. - options can be deprecated, in which case referencing them
  15. should produce a warning.
  16. - deprecated options can optionally be rerouted to a replacement
  17. so that accessing a deprecated option reroutes to a differently
  18. named option.
  19. - options can be reset to their default value.
  20. - all option can be reset to their default value at once.
  21. - all options in a certain sub - namespace can be reset at once.
  22. - the user can set / get / reset or ask for the description of an option.
  23. - a developer can register and mark an option as deprecated.
  24. - you can register a callback to be invoked when the option value
  25. is set or reset. Changing the stored value is considered misuse, but
  26. is not verboten.
  27. Implementation
  28. ==============
  29. - Data is stored using nested dictionaries, and should be accessed
  30. through the provided API.
  31. - "Registered options" and "Deprecated options" have metadata associated
  32. with them, which are stored in auxiliary dictionaries keyed on the
  33. fully-qualified key, e.g. "x.y.z.option".
  34. - the config_init module is imported by the package's __init__.py file.
  35. placing any register_option() calls there will ensure those options
  36. are available as soon as pandas is loaded. If you use register_option
  37. in a module, it will only be available after that module is imported,
  38. which you should be aware of.
  39. - `config_prefix` is a context_manager (for use with the `with` keyword)
  40. which can save developers some typing, see the docstring.
  41. """
  42. from collections import namedtuple
  43. from contextlib import contextmanager
  44. import re
  45. import warnings
  46. import pandas.compat as compat
  47. from pandas.compat import lmap, map, u
  48. DeprecatedOption = namedtuple('DeprecatedOption', 'key msg rkey removal_ver')
  49. RegisteredOption = namedtuple('RegisteredOption',
  50. 'key defval doc validator cb')
  51. _deprecated_options = {} # holds deprecated option metdata
  52. _registered_options = {} # holds registered option metdata
  53. _global_config = {} # holds the current values for registered options
  54. _reserved_keys = ['all'] # keys which have a special meaning
  55. class OptionError(AttributeError, KeyError):
  56. """Exception for pandas.options, backwards compatible with KeyError
  57. checks
  58. """
  59. #
  60. # User API
  61. def _get_single_key(pat, silent):
  62. keys = _select_options(pat)
  63. if len(keys) == 0:
  64. if not silent:
  65. _warn_if_deprecated(pat)
  66. raise OptionError('No such keys(s): {pat!r}'.format(pat=pat))
  67. if len(keys) > 1:
  68. raise OptionError('Pattern matched multiple keys')
  69. key = keys[0]
  70. if not silent:
  71. _warn_if_deprecated(key)
  72. key = _translate_key(key)
  73. return key
  74. def _get_option(pat, silent=False):
  75. key = _get_single_key(pat, silent)
  76. # walk the nested dict
  77. root, k = _get_root(key)
  78. return root[k]
  79. def _set_option(*args, **kwargs):
  80. # must at least 1 arg deal with constraints later
  81. nargs = len(args)
  82. if not nargs or nargs % 2 != 0:
  83. raise ValueError("Must provide an even number of non-keyword "
  84. "arguments")
  85. # default to false
  86. silent = kwargs.pop('silent', False)
  87. if kwargs:
  88. msg = '_set_option() got an unexpected keyword argument "{kwarg}"'
  89. raise TypeError(msg.format(list(kwargs.keys())[0]))
  90. for k, v in zip(args[::2], args[1::2]):
  91. key = _get_single_key(k, silent)
  92. o = _get_registered_option(key)
  93. if o and o.validator:
  94. o.validator(v)
  95. # walk the nested dict
  96. root, k = _get_root(key)
  97. root[k] = v
  98. if o.cb:
  99. if silent:
  100. with warnings.catch_warnings(record=True):
  101. o.cb(key)
  102. else:
  103. o.cb(key)
  104. def _describe_option(pat='', _print_desc=True):
  105. keys = _select_options(pat)
  106. if len(keys) == 0:
  107. raise OptionError('No such keys(s)')
  108. s = u('')
  109. for k in keys: # filter by pat
  110. s += _build_option_description(k)
  111. if _print_desc:
  112. print(s)
  113. else:
  114. return s
  115. def _reset_option(pat, silent=False):
  116. keys = _select_options(pat)
  117. if len(keys) == 0:
  118. raise OptionError('No such keys(s)')
  119. if len(keys) > 1 and len(pat) < 4 and pat != 'all':
  120. raise ValueError('You must specify at least 4 characters when '
  121. 'resetting multiple keys, use the special keyword '
  122. '"all" to reset all the options to their default '
  123. 'value')
  124. for k in keys:
  125. _set_option(k, _registered_options[k].defval, silent=silent)
  126. def get_default_val(pat):
  127. key = _get_single_key(pat, silent=True)
  128. return _get_registered_option(key).defval
  129. class DictWrapper(object):
  130. """ provide attribute-style access to a nested dict"""
  131. def __init__(self, d, prefix=""):
  132. object.__setattr__(self, "d", d)
  133. object.__setattr__(self, "prefix", prefix)
  134. def __setattr__(self, key, val):
  135. prefix = object.__getattribute__(self, "prefix")
  136. if prefix:
  137. prefix += "."
  138. prefix += key
  139. # you can't set new keys
  140. # can you can't overwrite subtrees
  141. if key in self.d and not isinstance(self.d[key], dict):
  142. _set_option(prefix, val)
  143. else:
  144. raise OptionError("You can only set the value of existing options")
  145. def __getattr__(self, key):
  146. prefix = object.__getattribute__(self, "prefix")
  147. if prefix:
  148. prefix += "."
  149. prefix += key
  150. try:
  151. v = object.__getattribute__(self, "d")[key]
  152. except KeyError:
  153. raise OptionError("No such option")
  154. if isinstance(v, dict):
  155. return DictWrapper(v, prefix)
  156. else:
  157. return _get_option(prefix)
  158. def __dir__(self):
  159. return list(self.d.keys())
  160. # For user convenience, we'd like to have the available options described
  161. # in the docstring. For dev convenience we'd like to generate the docstrings
  162. # dynamically instead of maintaining them by hand. To this, we use the
  163. # class below which wraps functions inside a callable, and converts
  164. # __doc__ into a property function. The doctsrings below are templates
  165. # using the py2.6+ advanced formatting syntax to plug in a concise list
  166. # of options, and option descriptions.
  167. class CallableDynamicDoc(object):
  168. def __init__(self, func, doc_tmpl):
  169. self.__doc_tmpl__ = doc_tmpl
  170. self.__func__ = func
  171. def __call__(self, *args, **kwds):
  172. return self.__func__(*args, **kwds)
  173. @property
  174. def __doc__(self):
  175. opts_desc = _describe_option('all', _print_desc=False)
  176. opts_list = pp_options_list(list(_registered_options.keys()))
  177. return self.__doc_tmpl__.format(opts_desc=opts_desc,
  178. opts_list=opts_list)
  179. _get_option_tmpl = """
  180. get_option(pat)
  181. Retrieves the value of the specified option.
  182. Available options:
  183. {opts_list}
  184. Parameters
  185. ----------
  186. pat : str
  187. Regexp which should match a single option.
  188. Note: partial matches are supported for convenience, but unless you use the
  189. full option name (e.g. x.y.z.option_name), your code may break in future
  190. versions if new options with similar names are introduced.
  191. Returns
  192. -------
  193. result : the value of the option
  194. Raises
  195. ------
  196. OptionError : if no such option exists
  197. Notes
  198. -----
  199. The available options with its descriptions:
  200. {opts_desc}
  201. """
  202. _set_option_tmpl = """
  203. set_option(pat, value)
  204. Sets the value of the specified option.
  205. Available options:
  206. {opts_list}
  207. Parameters
  208. ----------
  209. pat : str
  210. Regexp which should match a single option.
  211. Note: partial matches are supported for convenience, but unless you use the
  212. full option name (e.g. x.y.z.option_name), your code may break in future
  213. versions if new options with similar names are introduced.
  214. value :
  215. new value of option.
  216. Returns
  217. -------
  218. None
  219. Raises
  220. ------
  221. OptionError if no such option exists
  222. Notes
  223. -----
  224. The available options with its descriptions:
  225. {opts_desc}
  226. """
  227. _describe_option_tmpl = """
  228. describe_option(pat, _print_desc=False)
  229. Prints the description for one or more registered options.
  230. Call with not arguments to get a listing for all registered options.
  231. Available options:
  232. {opts_list}
  233. Parameters
  234. ----------
  235. pat : str
  236. Regexp pattern. All matching keys will have their description displayed.
  237. _print_desc : bool, default True
  238. If True (default) the description(s) will be printed to stdout.
  239. Otherwise, the description(s) will be returned as a unicode string
  240. (for testing).
  241. Returns
  242. -------
  243. None by default, the description(s) as a unicode string if _print_desc
  244. is False
  245. Notes
  246. -----
  247. The available options with its descriptions:
  248. {opts_desc}
  249. """
  250. _reset_option_tmpl = """
  251. reset_option(pat)
  252. Reset one or more options to their default value.
  253. Pass "all" as argument to reset all options.
  254. Available options:
  255. {opts_list}
  256. Parameters
  257. ----------
  258. pat : str/regex
  259. If specified only options matching `prefix*` will be reset.
  260. Note: partial matches are supported for convenience, but unless you
  261. use the full option name (e.g. x.y.z.option_name), your code may break
  262. in future versions if new options with similar names are introduced.
  263. Returns
  264. -------
  265. None
  266. Notes
  267. -----
  268. The available options with its descriptions:
  269. {opts_desc}
  270. """
  271. # bind the functions with their docstrings into a Callable
  272. # and use that as the functions exposed in pd.api
  273. get_option = CallableDynamicDoc(_get_option, _get_option_tmpl)
  274. set_option = CallableDynamicDoc(_set_option, _set_option_tmpl)
  275. reset_option = CallableDynamicDoc(_reset_option, _reset_option_tmpl)
  276. describe_option = CallableDynamicDoc(_describe_option, _describe_option_tmpl)
  277. options = DictWrapper(_global_config)
  278. #
  279. # Functions for use by pandas developers, in addition to User - api
  280. class option_context(object):
  281. """
  282. Context manager to temporarily set options in the `with` statement context.
  283. You need to invoke as ``option_context(pat, val, [(pat, val), ...])``.
  284. Examples
  285. --------
  286. >>> with option_context('display.max_rows', 10, 'display.max_columns', 5):
  287. ... ...
  288. """
  289. def __init__(self, *args):
  290. if not (len(args) % 2 == 0 and len(args) >= 2):
  291. raise ValueError('Need to invoke as'
  292. ' option_context(pat, val, [(pat, val), ...]).')
  293. self.ops = list(zip(args[::2], args[1::2]))
  294. def __enter__(self):
  295. self.undo = [(pat, _get_option(pat, silent=True))
  296. for pat, val in self.ops]
  297. for pat, val in self.ops:
  298. _set_option(pat, val, silent=True)
  299. def __exit__(self, *args):
  300. if self.undo:
  301. for pat, val in self.undo:
  302. _set_option(pat, val, silent=True)
  303. def register_option(key, defval, doc='', validator=None, cb=None):
  304. """Register an option in the package-wide pandas config object
  305. Parameters
  306. ----------
  307. key - a fully-qualified key, e.g. "x.y.option - z".
  308. defval - the default value of the option
  309. doc - a string description of the option
  310. validator - a function of a single argument, should raise `ValueError` if
  311. called with a value which is not a legal value for the option.
  312. cb - a function of a single argument "key", which is called
  313. immediately after an option value is set/reset. key is
  314. the full name of the option.
  315. Returns
  316. -------
  317. Nothing.
  318. Raises
  319. ------
  320. ValueError if `validator` is specified and `defval` is not a valid value.
  321. """
  322. import tokenize
  323. import keyword
  324. key = key.lower()
  325. if key in _registered_options:
  326. msg = "Option '{key}' has already been registered"
  327. raise OptionError(msg.format(key=key))
  328. if key in _reserved_keys:
  329. msg = "Option '{key}' is a reserved key"
  330. raise OptionError(msg.format(key=key))
  331. # the default value should be legal
  332. if validator:
  333. validator(defval)
  334. # walk the nested dict, creating dicts as needed along the path
  335. path = key.split('.')
  336. for k in path:
  337. if not bool(re.match('^' + tokenize.Name + '$', k)):
  338. raise ValueError("{k} is not a valid identifier".format(k=k))
  339. if keyword.iskeyword(k):
  340. raise ValueError("{k} is a python keyword".format(k=k))
  341. cursor = _global_config
  342. msg = "Path prefix to option '{option}' is already an option"
  343. for i, p in enumerate(path[:-1]):
  344. if not isinstance(cursor, dict):
  345. raise OptionError(msg.format(option='.'.join(path[:i])))
  346. if p not in cursor:
  347. cursor[p] = {}
  348. cursor = cursor[p]
  349. if not isinstance(cursor, dict):
  350. raise OptionError(msg.format(option='.'.join(path[:-1])))
  351. cursor[path[-1]] = defval # initialize
  352. # save the option metadata
  353. _registered_options[key] = RegisteredOption(key=key, defval=defval,
  354. doc=doc, validator=validator,
  355. cb=cb)
  356. def deprecate_option(key, msg=None, rkey=None, removal_ver=None):
  357. """
  358. Mark option `key` as deprecated, if code attempts to access this option,
  359. a warning will be produced, using `msg` if given, or a default message
  360. if not.
  361. if `rkey` is given, any access to the key will be re-routed to `rkey`.
  362. Neither the existence of `key` nor that if `rkey` is checked. If they
  363. do not exist, any subsequence access will fail as usual, after the
  364. deprecation warning is given.
  365. Parameters
  366. ----------
  367. key - the name of the option to be deprecated. must be a fully-qualified
  368. option name (e.g "x.y.z.rkey").
  369. msg - (Optional) a warning message to output when the key is referenced.
  370. if no message is given a default message will be emitted.
  371. rkey - (Optional) the name of an option to reroute access to.
  372. If specified, any referenced `key` will be re-routed to `rkey`
  373. including set/get/reset.
  374. rkey must be a fully-qualified option name (e.g "x.y.z.rkey").
  375. used by the default message if no `msg` is specified.
  376. removal_ver - (Optional) specifies the version in which this option will
  377. be removed. used by the default message if no `msg`
  378. is specified.
  379. Returns
  380. -------
  381. Nothing
  382. Raises
  383. ------
  384. OptionError - if key has already been deprecated.
  385. """
  386. key = key.lower()
  387. if key in _deprecated_options:
  388. msg = "Option '{key}' has already been defined as deprecated."
  389. raise OptionError(msg.format(key=key))
  390. _deprecated_options[key] = DeprecatedOption(key, msg, rkey, removal_ver)
  391. #
  392. # functions internal to the module
  393. def _select_options(pat):
  394. """returns a list of keys matching `pat`
  395. if pat=="all", returns all registered options
  396. """
  397. # short-circuit for exact key
  398. if pat in _registered_options:
  399. return [pat]
  400. # else look through all of them
  401. keys = sorted(_registered_options.keys())
  402. if pat == 'all': # reserved key
  403. return keys
  404. return [k for k in keys if re.search(pat, k, re.I)]
  405. def _get_root(key):
  406. path = key.split('.')
  407. cursor = _global_config
  408. for p in path[:-1]:
  409. cursor = cursor[p]
  410. return cursor, path[-1]
  411. def _is_deprecated(key):
  412. """ Returns True if the given option has been deprecated """
  413. key = key.lower()
  414. return key in _deprecated_options
  415. def _get_deprecated_option(key):
  416. """
  417. Retrieves the metadata for a deprecated option, if `key` is deprecated.
  418. Returns
  419. -------
  420. DeprecatedOption (namedtuple) if key is deprecated, None otherwise
  421. """
  422. try:
  423. d = _deprecated_options[key]
  424. except KeyError:
  425. return None
  426. else:
  427. return d
  428. def _get_registered_option(key):
  429. """
  430. Retrieves the option metadata if `key` is a registered option.
  431. Returns
  432. -------
  433. RegisteredOption (namedtuple) if key is deprecated, None otherwise
  434. """
  435. return _registered_options.get(key)
  436. def _translate_key(key):
  437. """
  438. if key id deprecated and a replacement key defined, will return the
  439. replacement key, otherwise returns `key` as - is
  440. """
  441. d = _get_deprecated_option(key)
  442. if d:
  443. return d.rkey or key
  444. else:
  445. return key
  446. def _warn_if_deprecated(key):
  447. """
  448. Checks if `key` is a deprecated option and if so, prints a warning.
  449. Returns
  450. -------
  451. bool - True if `key` is deprecated, False otherwise.
  452. """
  453. d = _get_deprecated_option(key)
  454. if d:
  455. if d.msg:
  456. print(d.msg)
  457. warnings.warn(d.msg, FutureWarning)
  458. else:
  459. msg = "'{key}' is deprecated".format(key=key)
  460. if d.removal_ver:
  461. msg += (' and will be removed in {version}'
  462. .format(version=d.removal_ver))
  463. if d.rkey:
  464. msg += ", please use '{rkey}' instead.".format(rkey=d.rkey)
  465. else:
  466. msg += ', please refrain from using it.'
  467. warnings.warn(msg, FutureWarning)
  468. return True
  469. return False
  470. def _build_option_description(k):
  471. """ Builds a formatted description of a registered option and prints it """
  472. o = _get_registered_option(k)
  473. d = _get_deprecated_option(k)
  474. s = u('{k} ').format(k=k)
  475. if o.doc:
  476. s += '\n'.join(o.doc.strip().split('\n'))
  477. else:
  478. s += 'No description available.'
  479. if o:
  480. s += (u('\n [default: {default}] [currently: {current}]')
  481. .format(default=o.defval, current=_get_option(k, True)))
  482. if d:
  483. s += u('\n (Deprecated')
  484. s += (u(', use `{rkey}` instead.')
  485. .format(rkey=d.rkey if d.rkey else ''))
  486. s += u(')')
  487. s += '\n\n'
  488. return s
  489. def pp_options_list(keys, width=80, _print=False):
  490. """ Builds a concise listing of available options, grouped by prefix """
  491. from textwrap import wrap
  492. from itertools import groupby
  493. def pp(name, ks):
  494. pfx = ('- ' + name + '.[' if name else '')
  495. ls = wrap(', '.join(ks), width, initial_indent=pfx,
  496. subsequent_indent=' ', break_long_words=False)
  497. if ls and ls[-1] and name:
  498. ls[-1] = ls[-1] + ']'
  499. return ls
  500. ls = []
  501. singles = [x for x in sorted(keys) if x.find('.') < 0]
  502. if singles:
  503. ls += pp('', singles)
  504. keys = [x for x in keys if x.find('.') >= 0]
  505. for k, g in groupby(sorted(keys), lambda x: x[:x.rfind('.')]):
  506. ks = [x[len(k) + 1:] for x in list(g)]
  507. ls += pp(k, ks)
  508. s = '\n'.join(ls)
  509. if _print:
  510. print(s)
  511. else:
  512. return s
  513. #
  514. # helpers
  515. @contextmanager
  516. def config_prefix(prefix):
  517. """contextmanager for multiple invocations of API with a common prefix
  518. supported API functions: (register / get / set )__option
  519. Warning: This is not thread - safe, and won't work properly if you import
  520. the API functions into your module using the "from x import y" construct.
  521. Example:
  522. import pandas.core.config as cf
  523. with cf.config_prefix("display.font"):
  524. cf.register_option("color", "red")
  525. cf.register_option("size", " 5 pt")
  526. cf.set_option(size, " 6 pt")
  527. cf.get_option(size)
  528. ...
  529. etc'
  530. will register options "display.font.color", "display.font.size", set the
  531. value of "display.font.size"... and so on.
  532. """
  533. # Note: reset_option relies on set_option, and on key directly
  534. # it does not fit in to this monkey-patching scheme
  535. global register_option, get_option, set_option, reset_option
  536. def wrap(func):
  537. def inner(key, *args, **kwds):
  538. pkey = '{prefix}.{key}'.format(prefix=prefix, key=key)
  539. return func(pkey, *args, **kwds)
  540. return inner
  541. _register_option = register_option
  542. _get_option = get_option
  543. _set_option = set_option
  544. set_option = wrap(set_option)
  545. get_option = wrap(get_option)
  546. register_option = wrap(register_option)
  547. yield None
  548. set_option = _set_option
  549. get_option = _get_option
  550. register_option = _register_option
  551. # These factories and methods are handy for use as the validator
  552. # arg in register_option
  553. def is_type_factory(_type):
  554. """
  555. Parameters
  556. ----------
  557. `_type` - a type to be compared against (e.g. type(x) == `_type`)
  558. Returns
  559. -------
  560. validator - a function of a single argument x , which raises
  561. ValueError if type(x) is not equal to `_type`
  562. """
  563. def inner(x):
  564. if type(x) != _type:
  565. msg = "Value must have type '{typ!s}'"
  566. raise ValueError(msg.format(typ=_type))
  567. return inner
  568. def is_instance_factory(_type):
  569. """
  570. Parameters
  571. ----------
  572. `_type` - the type to be checked against
  573. Returns
  574. -------
  575. validator - a function of a single argument x , which raises
  576. ValueError if x is not an instance of `_type`
  577. """
  578. if isinstance(_type, (tuple, list)):
  579. _type = tuple(_type)
  580. from pandas.io.formats.printing import pprint_thing
  581. type_repr = "|".join(map(pprint_thing, _type))
  582. else:
  583. type_repr = "'{typ}'".format(typ=_type)
  584. def inner(x):
  585. if not isinstance(x, _type):
  586. msg = "Value must be an instance of {type_repr}"
  587. raise ValueError(msg.format(type_repr=type_repr))
  588. return inner
  589. def is_one_of_factory(legal_values):
  590. callables = [c for c in legal_values if callable(c)]
  591. legal_values = [c for c in legal_values if not callable(c)]
  592. def inner(x):
  593. from pandas.io.formats.printing import pprint_thing as pp
  594. if x not in legal_values:
  595. if not any(c(x) for c in callables):
  596. pp_values = pp("|".join(lmap(pp, legal_values)))
  597. msg = "Value must be one of {pp_values}"
  598. if len(callables):
  599. msg += " or a callable"
  600. raise ValueError(msg.format(pp_values=pp_values))
  601. return inner
  602. # common type validators, for convenience
  603. # usage: register_option(... , validator = is_int)
  604. is_int = is_type_factory(int)
  605. is_bool = is_type_factory(bool)
  606. is_float = is_type_factory(float)
  607. is_str = is_type_factory(str)
  608. is_unicode = is_type_factory(compat.text_type)
  609. is_text = is_instance_factory((str, bytes))
  610. def is_callable(obj):
  611. """
  612. Parameters
  613. ----------
  614. `obj` - the object to be checked
  615. Returns
  616. -------
  617. validator - returns True if object is callable
  618. raises ValueError otherwise.
  619. """
  620. if not callable(obj):
  621. raise ValueError("Value must be a callable")
  622. return True