From 086a82464ce0cbe1246da6cf8b473b1c023f0774 Mon Sep 17 00:00:00 2001 From: Tim Hopper Date: Thu, 24 Mar 2016 10:18:22 -0400 Subject: [PATCH] BUG: Validate float_format setting as callable or None The `float_format` setting takes either None or a callable object that returns a formatted string given a float value. Currently, the setting isn't validated, so cryptic error messages are returned if, for example, a format non-callable format string is given (see \#12704). Add a standard validation method and use it to validate the `float_format` setting. --- doc/source/whatsnew/v0.18.1.txt | 1 + pandas/core/config.py | 18 ++++++++++++++++++ pandas/core/config_init.py | 6 ++++-- pandas/tests/test_config.py | 6 ++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.18.1.txt b/doc/source/whatsnew/v0.18.1.txt index 7843cb24b7686..714a0cf19d14a 100644 --- a/doc/source/whatsnew/v0.18.1.txt +++ b/doc/source/whatsnew/v0.18.1.txt @@ -124,6 +124,7 @@ Bug Fixes - Bug in numpy compatibility of ``np.round()`` on a ``Series`` (:issue:`12600`) - Bug in ``Series`` construction with ``Categorical`` and ``dtype='category'`` is specified (:issue:`12574`) - Bugs in concatenation with a coercable dtype was too aggressive. (:issue:`12411`, :issue:`12045`, :issue:`11594`, :issue:`10571`) +- Bug in ``float_format`` option with option not being validated as callable. (:issue:`12706`) diff --git a/pandas/core/config.py b/pandas/core/config.py index 7b1e5b29f1cbb..d489a3bc2f079 100644 --- a/pandas/core/config.py +++ b/pandas/core/config.py @@ -803,3 +803,21 @@ def inner(x): is_str = is_type_factory(str) is_unicode = is_type_factory(compat.text_type) is_text = is_instance_factory((str, bytes)) + + +def is_callable_or_none(obj): + """ + + Parameters + ---------- + `obj` - the object to be checked + + Returns + ------- + validator - returns True if object is callable or None, and + raises ValueError otherwise. + + """ + if not (callable(obj) or obj is None): + raise ValueError("Value must be an callable or None") + return True diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index f9b91db608093..5c2ef2191b031 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -13,7 +13,8 @@ import pandas.core.config as cf from pandas.core.config import (is_int, is_bool, is_text, is_instance_factory, - is_one_of_factory, get_default_val) + is_one_of_factory, get_default_val, + is_callable_or_none) from pandas.core.format import detect_console_encoding # @@ -279,7 +280,8 @@ def mpl_style_cb(key): with cf.config_prefix('display'): cf.register_option('precision', 6, pc_precision_doc, validator=is_int) - cf.register_option('float_format', None, float_format_doc) + cf.register_option('float_format', None, float_format_doc, + validator=is_callable_or_none) cf.register_option('column_space', 12, validator=is_int) cf.register_option('max_info_rows', 1690785, pc_max_info_rows_doc, validator=is_instance_factory((int, type(None)))) diff --git a/pandas/tests/test_config.py b/pandas/tests/test_config.py index 693b1d0ec71de..b0a06493ea83a 100644 --- a/pandas/tests/test_config.py +++ b/pandas/tests/test_config.py @@ -206,6 +206,12 @@ def test_validation(self): self.assertRaises(ValueError, self.cf.set_option, 'a', 'ab') self.assertRaises(ValueError, self.cf.set_option, 'b.c', 1) + self.cf.register_option('b', lambda: None, 'doc', + validator=self.cf.is_callable_or_none) + self.cf.set_option('b', '%.1f'.format) # Formatter is callable + self.cf.set_option('b', None) # Formatter is none (default) + self.assertRaises(ValueError, self.cf.set_option, 'b', '%.1f') + def test_reset_option(self): self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) self.cf.register_option('b.c', 'hullo', 'doc2',