Repository: pandas-dev/pandas

Issue:
set_option as a context manager
It doesn't look like it's available yet as such, but it might be nice to have set_option usable as a context manager. E.g., I often find myself wanting to do something like

```
with pd.set_option('max_rows', 200):
    print df
```

As simple as adding `__enter__` and `__exit__` methods and preserving state?



Discussion:
you have to do something like:

```
with pd.option_context('max_rows',200):
      print df
```

IIRC

Oh nice. Is it new? I don't see it in the namespace. Probably deserves a mention in the set_option docstring too.

I think it's been around
mostly for internal use
let's make this issue a doc issue then

`config_prefix` according to #2097. And it's not exposed at the top-level namespace AFAICT.

`option_context`: https://github.com/pydata/pandas/pull/2516, and it was extended later to accept multiple key/value pairs, I think at cpcloud's suggestion.

so you can 

``` python
with option_context(foo,"is1", bar, "is2"):
    baz
```

Ah, great. Then consider this a request for importing it into the top-level namespace and adding some docs.

I have no objections to that. Can't remember the details but I think the reason set_option
itself isn't a context_manager was that it clashed with the nifty dynamic docstring
magic attached to it, which no one ever noticed or used. Should probably just ditch that
and make set_option a context manager instead. More intuitive.
Can just calculate the docstring once at the end of config_init and set it instead
of all that fuffing around in config.py.


Resolving patch:
diff --git a/pandas/core/api.py b/pandas/core/api.py
--- a/pandas/core/api.py
+++ b/pandas/core/api.py
@@ -31,4 +31,4 @@
 import pandas.core.datetools as datetools
 
 from pandas.core.config import (get_option, set_option, reset_option,
-                                describe_option, options)
+                                describe_option, option_context, options)
diff --git a/pandas/core/config.py b/pandas/core/config.py
--- a/pandas/core/config.py
+++ b/pandas/core/config.py
@@ -100,54 +100,29 @@ def _get_option(pat, silent=False):
     return root[k]
 
 
-def _set_single_option(pat, value, silent):
-    key = _get_single_key(pat, silent)
-
-    o = _get_registered_option(key)
-    if o and o.validator:
-        o.validator(value)
-
-    # walk the nested dict
-    root, k = _get_root(key)
-    root[k] = value
-
-    if o.cb:
-        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 "
+        raise ValueError("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")
+    # default to false
+    silent = kwargs.get('silent', False)
 
-    # if 1 kwarg then it must be silent=True or silent=False
-    if nkwargs:
-        k, = list(kwargs.keys())
-        v, = list(kwargs.values())
+    for k, v in zip(args[::2], args[1::2]):
+        key = _get_single_key(k, silent)
 
-        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__))
+        o = _get_registered_option(key)
+        if o and o.validator:
+            o.validator(v)
 
-    # default to false
-    silent = kwargs.get('silent', False)
-    _set_multiple_options(args, silent)
+        # walk the nested dict
+        root, k = _get_root(key)
+        root[k] = v
 
+        if o.cb:
+            o.cb(key)
 
 def _describe_option(pat='', _print_desc=True):
 
@@ -365,7 +340,7 @@ class option_context(object):
 
     def __init__(self, *args):
         if not (len(args) % 2 == 0 and len(args) >= 2):
-            raise AssertionError(
+            raise ValueError(
                 'Need to invoke as'
                 'option_context(pat, val, [(pat, val), ...)).'
             )


Test patch:
diff --git a/pandas/tests/test_config.py b/pandas/tests/test_config.py
--- a/pandas/tests/test_config.py
+++ b/pandas/tests/test_config.py
@@ -170,26 +170,13 @@ def test_set_option(self):
 
 
     def test_set_option_empty_args(self):
-        self.assertRaises(AssertionError, self.cf.set_option)
+        self.assertRaises(ValueError, 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)
+        self.assertRaises(ValueError, self.cf.set_option, 'a.b', 2, 'b.c')
 
     def test_set_option_invalid_single_argument_type(self):
-        self.assertRaises(AssertionError, self.cf.set_option, 2)
+        self.assertRaises(ValueError, self.cf.set_option, 2)
 
     def test_set_option_multiple(self):
         self.cf.register_option('a', 1, 'doc')