From 0cb981c4f3faa7e6474e038198124406a0d3b551 Mon Sep 17 00:00:00 2001 From: Phillip Cloud Date: Tue, 21 May 2013 18:16:32 -0400 Subject: [PATCH] ENH: add new option syntax --- RELEASE.rst | 4 +++ doc/source/v0.11.1.txt | 25 +++++++++++++++++ pandas/core/config.py | 54 ++++++++++++++++++++++++++++--------- pandas/tests/test_config.py | 38 ++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/RELEASE.rst b/RELEASE.rst index 9283bada2d720..35741f7eb008f 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -46,6 +46,9 @@ pandas 0.11.1 Note: The default value will change in 0.12 to make the default *to* write and read multi-index columns in the new format. (GH3571_, GH1651_, GH3141_) - Add iterator to ``Series.str`` (GH3638_) + - ``pd.set_option()`` now allows N option, value pairs (GH3667_). + + **Improvements to existing features** @@ -269,6 +272,7 @@ pandas 0.11.1 .. _GH3702: https://github.com/pydata/pandas/issues/3702 .. _GH3691: https://github.com/pydata/pandas/issues/3691 .. _GH3696: https://github.com/pydata/pandas/issues/3696 +.. _GH3667: https://github.com/pydata/pandas/issues/3667 pandas 0.11.0 ============= diff --git a/doc/source/v0.11.1.txt b/doc/source/v0.11.1.txt index 26069681552f0..5acd2aa365ea3 100644 --- a/doc/source/v0.11.1.txt +++ b/doc/source/v0.11.1.txt @@ -238,6 +238,30 @@ Enhancements GH3572_). This happens before any drawing takes place which elimnates any spurious plots from showing up. + - ``pd.set_option()`` now allows N option, value pairs (GH3667_). + + Let's say that we had an option ``'a.b'`` and another option ``'b.c'``. + We can set them at the same time: + + .. ipython:: python + :suppress: + + pd.core.config.register_option('a.b', 2, 'ay dot bee') + pd.core.config.register_option('b.c', 3, 'bee dot cee') + + .. ipython:: python + + pd.get_option('a.b') + pd.get_option('b.c') + pd.set_option('a.b', 1, 'b.c', 4) + pd.get_option('a.b') + pd.get_option('b.c') + + You can of course still do it sequentially if you want. You can use up to + N arguments here, the only stipulation is that the number of arguments + must be even (since if they weren't then that would mean you provided an + argument name with no value). + Bug Fixes ~~~~~~~~~ @@ -305,3 +329,4 @@ on GitHub for a complete list. .. _GH3702: https://github.com/pydata/pandas/issues/3702 .. _GH3691: https://github.com/pydata/pandas/issues/3691 .. _GH3696: https://github.com/pydata/pandas/issues/3696 +.. _GH3667: https://github.com/pydata/pandas/issues/3667 diff --git a/pandas/core/config.py b/pandas/core/config.py index 2d62b807cf203..e8403164ac1b9 100644 --- a/pandas/core/config.py +++ b/pandas/core/config.py @@ -94,7 +94,7 @@ def _get_option(pat, silent=False): return root[k] -def _set_option(pat, value, silent=False): +def _set_single_option(pat, value, silent): key = _get_single_key(pat, silent) o = _get_registered_option(key) @@ -109,6 +109,40 @@ def _set_option(pat, value, silent=False): o.cb(key) +def _set_multiple_options(args, silent): + for k, v in zip(args[::2], args[1::2]): + _set_single_option(k, v, silent) + + +def _set_option(*args, **kwargs): + # must at least 1 arg deal with constraints later + nargs = len(args) + if not nargs or nargs % 2 != 0: + raise AssertionError("Must provide an even number of non-keyword " + "arguments") + + # must be 0 or 1 kwargs + nkwargs = len(kwargs) + if nkwargs not in (0, 1): + raise AssertionError("The can only be 0 or 1 keyword arguments") + + # if 1 kwarg then it must be silent=True or silent=False + if nkwargs: + k, = kwargs.keys() + v, = kwargs.values() + + if k != 'silent': + raise ValueError("the only allowed keyword argument is 'silent', " + "you passed '{0}'".format(k)) + if not isinstance(v, bool): + raise TypeError("the type of the keyword argument passed must be " + "bool, you passed a {0}".format(v.__class__)) + + # default to false + silent = kwargs.get('silent', False) + _set_multiple_options(args, silent) + + def _describe_option(pat='', _print_desc=True): keys = _select_options(pat) @@ -186,7 +220,7 @@ def __dir__(self): # of options, and option descriptions. -class CallableDyanmicDoc(object): +class CallableDynamicDoc(object): def __init__(self, func, doc_tmpl): self.__doc_tmpl__ = doc_tmpl @@ -301,10 +335,10 @@ def __doc__(self): # bind the functions with their docstrings into a Callable # and use that as the functions exposed in pd.api -get_option = CallableDyanmicDoc(_get_option, _get_option_tmpl) -set_option = CallableDyanmicDoc(_set_option, _set_option_tmpl) -reset_option = CallableDyanmicDoc(_reset_option, _reset_option_tmpl) -describe_option = CallableDyanmicDoc(_describe_option, _describe_option_tmpl) +get_option = CallableDynamicDoc(_get_option, _get_option_tmpl) +set_option = CallableDynamicDoc(_set_option, _set_option_tmpl) +reset_option = CallableDynamicDoc(_reset_option, _reset_option_tmpl) +describe_option = CallableDynamicDoc(_describe_option, _describe_option_tmpl) options = DictWrapper(_global_config) ###################################################### @@ -505,13 +539,7 @@ def _get_registered_option(key): ------- RegisteredOption (namedtuple) if key is deprecated, None otherwise """ - - try: - d = _registered_options[key] - except KeyError: - return None - else: - return d + return _registered_options.get(key) def _translate_key(key): diff --git a/pandas/tests/test_config.py b/pandas/tests/test_config.py index c1231df026853..a2b1ea43717cf 100644 --- a/pandas/tests/test_config.py +++ b/pandas/tests/test_config.py @@ -169,6 +169,44 @@ def test_set_option(self): self.assertRaises(KeyError, self.cf.set_option, 'no.such.key', None) + + def test_set_option_empty_args(self): + self.assertRaises(AssertionError, self.cf.set_option) + + def test_set_option_uneven_args(self): + self.assertRaises(AssertionError, self.cf.set_option, 'a.b', 2, 'b.c') + + + def test_set_option_2_kwargs(self): + self.assertRaises(AssertionError, self.cf.set_option, 'a.b', 2, + silenadf=2, asdf=2) + + def test_set_option_invalid_kwargs_key(self): + self.assertRaises(ValueError, self.cf.set_option, 'a.b', 2, + silenadf=2) + + def test_set_option_invalid_kwargs_value_type(self): + self.assertRaises(TypeError, self.cf.set_option, 'a.b', 2, + silent=2) + + def test_set_option_invalid_single_argument_type(self): + self.assertRaises(AssertionError, self.cf.set_option, 2) + + def test_set_option_multiple(self): + self.cf.register_option('a', 1, 'doc') + self.cf.register_option('b.c', 'hullo', 'doc2') + self.cf.register_option('b.b', None, 'doc2') + + self.assertEqual(self.cf.get_option('a'), 1) + self.assertEqual(self.cf.get_option('b.c'), 'hullo') + self.assertTrue(self.cf.get_option('b.b') is None) + + self.cf.set_option('a', '2', 'b.c', None, 'b.b', 10.0) + + self.assertEqual(self.cf.get_option('a'), '2') + self.assertTrue(self.cf.get_option('b.c') is None) + self.assertEqual(self.cf.get_option('b.b'), 10.0) + def test_validation(self): self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) self.cf.register_option('b.c', 'hullo', 'doc2',