# -*- coding: utf-8 -*- import pytest import pandas as pd import warnings class TestConfig(object): @classmethod def setup_class(cls): from copy import deepcopy cls.cf = pd.core.config cls.gc = deepcopy(getattr(cls.cf, '_global_config')) cls.do = deepcopy(getattr(cls.cf, '_deprecated_options')) cls.ro = deepcopy(getattr(cls.cf, '_registered_options')) def setup_method(self, method): setattr(self.cf, '_global_config', {}) setattr(self.cf, 'options', self.cf.DictWrapper( self.cf._global_config)) setattr(self.cf, '_deprecated_options', {}) setattr(self.cf, '_registered_options', {}) # Our test fixture in conftest.py sets "chained_assignment" # to "raise" only after all test methods have been setup. # However, after this setup, there is no longer any # "chained_assignment" option, so re-register it. self.cf.register_option('chained_assignment', 'raise') def teardown_method(self, method): setattr(self.cf, '_global_config', self.gc) setattr(self.cf, '_deprecated_options', self.do) setattr(self.cf, '_registered_options', self.ro) def test_api(self): # the pandas object exposes the user API assert hasattr(pd, 'get_option') assert hasattr(pd, 'set_option') assert hasattr(pd, 'reset_option') assert hasattr(pd, 'describe_option') def test_is_one_of_factory(self): v = self.cf.is_one_of_factory([None, 12]) v(12) v(None) pytest.raises(ValueError, v, 1.1) def test_register_option(self): self.cf.register_option('a', 1, 'doc') # can't register an already registered option pytest.raises(KeyError, self.cf.register_option, 'a', 1, 'doc') # can't register an already registered option pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d1', 1, 'doc') pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d2', 1, 'doc') # no python keywords pytest.raises(ValueError, self.cf.register_option, 'for', 0) pytest.raises(ValueError, self.cf.register_option, 'a.for.b', 0) # must be valid identifier (ensure attribute access works) pytest.raises(ValueError, self.cf.register_option, 'Oh my Goddess!', 0) # we can register options several levels deep # without predefining the intermediate steps # and we can define differently named options # in the same namespace self.cf.register_option('k.b.c.d1', 1, 'doc') self.cf.register_option('k.b.c.d2', 1, 'doc') def test_describe_option(self): self.cf.register_option('a', 1, 'doc') self.cf.register_option('b', 1, 'doc2') self.cf.deprecate_option('b') self.cf.register_option('c.d.e1', 1, 'doc3') self.cf.register_option('c.d.e2', 1, 'doc4') self.cf.register_option('f', 1) self.cf.register_option('g.h', 1) self.cf.register_option('k', 2) self.cf.deprecate_option('g.h', rkey="k") self.cf.register_option('l', "foo") # non-existent keys raise KeyError pytest.raises(KeyError, self.cf.describe_option, 'no.such.key') # we can get the description for any key we registered assert 'doc' in self.cf.describe_option('a', _print_desc=False) assert 'doc2' in self.cf.describe_option('b', _print_desc=False) assert 'precated' in self.cf.describe_option('b', _print_desc=False) assert 'doc3' in self.cf.describe_option('c.d.e1', _print_desc=False) assert 'doc4' in self.cf.describe_option('c.d.e2', _print_desc=False) # if no doc is specified we get a default message # saying "description not available" assert 'vailable' in self.cf.describe_option('f', _print_desc=False) assert 'vailable' in self.cf.describe_option('g.h', _print_desc=False) assert 'precated' in self.cf.describe_option('g.h', _print_desc=False) assert 'k' in self.cf.describe_option('g.h', _print_desc=False) # default is reported assert 'foo' in self.cf.describe_option('l', _print_desc=False) # current value is reported assert 'bar' not in self.cf.describe_option('l', _print_desc=False) self.cf.set_option("l", "bar") assert 'bar' in self.cf.describe_option('l', _print_desc=False) def test_case_insensitive(self): self.cf.register_option('KanBAN', 1, 'doc') assert 'doc' in self.cf.describe_option('kanbaN', _print_desc=False) assert self.cf.get_option('kanBaN') == 1 self.cf.set_option('KanBan', 2) assert self.cf.get_option('kAnBaN') == 2 # gets of non-existent keys fail pytest.raises(KeyError, self.cf.get_option, 'no_such_option') self.cf.deprecate_option('KanBan') assert self.cf._is_deprecated('kAnBaN') def test_get_option(self): self.cf.register_option('a', 1, 'doc') self.cf.register_option('b.c', 'hullo', 'doc2') self.cf.register_option('b.b', None, 'doc2') # gets of existing keys succeed assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' assert self.cf.get_option('b.b') is None # gets of non-existent keys fail pytest.raises(KeyError, self.cf.get_option, 'no_such_option') def test_set_option(self): self.cf.register_option('a', 1, 'doc') self.cf.register_option('b.c', 'hullo', 'doc2') self.cf.register_option('b.b', None, 'doc2') assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' assert self.cf.get_option('b.b') is None self.cf.set_option('a', 2) self.cf.set_option('b.c', 'wurld') self.cf.set_option('b.b', 1.1) assert self.cf.get_option('a') == 2 assert self.cf.get_option('b.c') == 'wurld' assert self.cf.get_option('b.b') == 1.1 pytest.raises(KeyError, self.cf.set_option, 'no.such.key', None) def test_set_option_empty_args(self): pytest.raises(ValueError, self.cf.set_option) def test_set_option_uneven_args(self): pytest.raises(ValueError, self.cf.set_option, 'a.b', 2, 'b.c') def test_set_option_invalid_single_argument_type(self): pytest.raises(ValueError, 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') assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' assert self.cf.get_option('b.b') is None self.cf.set_option('a', '2', 'b.c', None, 'b.b', 10.0) assert self.cf.get_option('a') == '2' assert self.cf.get_option('b.c') is None assert 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', validator=self.cf.is_text) pytest.raises(ValueError, self.cf.register_option, 'a.b.c.d2', 'NO', 'doc', validator=self.cf.is_int) self.cf.set_option('a', 2) # int is_int self.cf.set_option('b.c', 'wurld') # str is_str pytest.raises( ValueError, self.cf.set_option, 'a', None) # None not is_int pytest.raises(ValueError, self.cf.set_option, 'a', 'ab') pytest.raises(ValueError, self.cf.set_option, 'b.c', 1) validator = self.cf.is_one_of_factory([None, self.cf.is_callable]) self.cf.register_option('b', lambda: None, 'doc', validator=validator) self.cf.set_option('b', '%.1f'.format) # Formatter is callable self.cf.set_option('b', None) # Formatter is none (default) pytest.raises(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', validator=self.cf.is_str) assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' self.cf.set_option('a', 2) self.cf.set_option('b.c', 'wurld') assert self.cf.get_option('a') == 2 assert self.cf.get_option('b.c') == 'wurld' self.cf.reset_option('a') assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'wurld' self.cf.reset_option('b.c') assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' def test_reset_option_all(self): self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) self.cf.register_option('b.c', 'hullo', 'doc2', validator=self.cf.is_str) assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' self.cf.set_option('a', 2) self.cf.set_option('b.c', 'wurld') assert self.cf.get_option('a') == 2 assert self.cf.get_option('b.c') == 'wurld' self.cf.reset_option("all") assert self.cf.get_option('a') == 1 assert self.cf.get_option('b.c') == 'hullo' def test_deprecate_option(self): # we can deprecate non-existent options self.cf.deprecate_option('foo') assert self.cf._is_deprecated('foo') with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') try: self.cf.get_option('foo') except KeyError: pass else: self.fail("Nonexistent option didn't raise KeyError") assert len(w) == 1 # should have raised one warning assert 'deprecated' in str(w[-1]) # we get the default message self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) self.cf.register_option('b.c', 'hullo', 'doc2') self.cf.register_option('foo', 'hullo', 'doc2') self.cf.deprecate_option('a', removal_ver='nifty_ver') with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.cf.get_option('a') assert len(w) == 1 # should have raised one warning assert 'eprecated' in str(w[-1]) # we get the default message assert 'nifty_ver' in str(w[-1]) # with the removal_ver quoted pytest.raises( KeyError, self.cf.deprecate_option, 'a') # can't depr. twice self.cf.deprecate_option('b.c', 'zounds!') with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.cf.get_option('b.c') assert len(w) == 1 # should have raised one warning assert 'zounds!' in str(w[-1]) # we get the custom message # test rerouting keys self.cf.register_option('d.a', 'foo', 'doc2') self.cf.register_option('d.dep', 'bar', 'doc2') assert self.cf.get_option('d.a') == 'foo' assert self.cf.get_option('d.dep') == 'bar' self.cf.deprecate_option('d.dep', rkey='d.a') # reroute d.dep to d.a with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') assert self.cf.get_option('d.dep') == 'foo' assert len(w) == 1 # should have raised one warning assert 'eprecated' in str(w[-1]) # we get the custom message with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.cf.set_option('d.dep', 'baz') # should overwrite "d.a" assert len(w) == 1 # should have raised one warning assert 'eprecated' in str(w[-1]) # we get the custom message with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') assert self.cf.get_option('d.dep') == 'baz' assert len(w) == 1 # should have raised one warning assert 'eprecated' in str(w[-1]) # we get the custom message def test_config_prefix(self): with self.cf.config_prefix("base"): self.cf.register_option('a', 1, "doc1") self.cf.register_option('b', 2, "doc2") assert self.cf.get_option('a') == 1 assert self.cf.get_option('b') == 2 self.cf.set_option('a', 3) self.cf.set_option('b', 4) assert self.cf.get_option('a') == 3 assert self.cf.get_option('b') == 4 assert self.cf.get_option('base.a') == 3 assert self.cf.get_option('base.b') == 4 assert 'doc1' in self.cf.describe_option('base.a', _print_desc=False) assert 'doc2' in self.cf.describe_option('base.b', _print_desc=False) self.cf.reset_option('base.a') self.cf.reset_option('base.b') with self.cf.config_prefix("base"): assert self.cf.get_option('a') == 1 assert self.cf.get_option('b') == 2 def test_callback(self): k = [None] v = [None] def callback(key): k.append(key) v.append(self.cf.get_option(key)) self.cf.register_option('d.a', 'foo', cb=callback) self.cf.register_option('d.b', 'foo', cb=callback) del k[-1], v[-1] self.cf.set_option("d.a", "fooz") assert k[-1] == "d.a" assert v[-1] == "fooz" del k[-1], v[-1] self.cf.set_option("d.b", "boo") assert k[-1] == "d.b" assert v[-1] == "boo" del k[-1], v[-1] self.cf.reset_option("d.b") assert k[-1] == "d.b" def test_set_ContextManager(self): def eq(val): assert self.cf.get_option("a") == val self.cf.register_option('a', 0) eq(0) with self.cf.option_context("a", 15): eq(15) with self.cf.option_context("a", 25): eq(25) eq(15) eq(0) self.cf.set_option("a", 17) eq(17) def test_attribute_access(self): holder = [] def f(): options.b = 1 def f2(): options.display = 1 def f3(key): holder.append(True) self.cf.register_option('a', 0) self.cf.register_option('c', 0, cb=f3) options = self.cf.options assert options.a == 0 with self.cf.option_context("a", 15): assert options.a == 15 options.a = 500 assert self.cf.get_option("a") == 500 self.cf.reset_option("a") assert options.a == self.cf.get_option("a", 0) pytest.raises(KeyError, f) pytest.raises(KeyError, f2) # make sure callback kicks when using this form of setting options.c = 1 assert len(holder) == 1 def test_option_context_scope(self): # Ensure that creating a context does not affect the existing # environment as it is supposed to be used with the `with` statement. # See https://github.com/pandas-dev/pandas/issues/8514 original_value = 60 context_value = 10 option_name = 'a' self.cf.register_option(option_name, original_value) # Ensure creating contexts didn't affect the current context. ctx = self.cf.option_context(option_name, context_value) assert self.cf.get_option(option_name) == original_value # Ensure the correct value is available inside the context. with ctx: assert self.cf.get_option(option_name) == context_value # Ensure the current context is reset assert self.cf.get_option(option_name) == original_value def test_dictwrapper_getattr(self): options = self.cf.options # GH 19789 pytest.raises(self.cf.OptionError, getattr, options, 'bananas') assert not hasattr(options, 'bananas')