test_config.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. # -*- coding: utf-8 -*-
  2. import warnings
  3. import pytest
  4. import pandas as pd
  5. class TestConfig(object):
  6. @classmethod
  7. def setup_class(cls):
  8. from copy import deepcopy
  9. cls.cf = pd.core.config
  10. cls.gc = deepcopy(getattr(cls.cf, '_global_config'))
  11. cls.do = deepcopy(getattr(cls.cf, '_deprecated_options'))
  12. cls.ro = deepcopy(getattr(cls.cf, '_registered_options'))
  13. def setup_method(self, method):
  14. setattr(self.cf, '_global_config', {})
  15. setattr(self.cf, 'options', self.cf.DictWrapper(
  16. self.cf._global_config))
  17. setattr(self.cf, '_deprecated_options', {})
  18. setattr(self.cf, '_registered_options', {})
  19. # Our test fixture in conftest.py sets "chained_assignment"
  20. # to "raise" only after all test methods have been setup.
  21. # However, after this setup, there is no longer any
  22. # "chained_assignment" option, so re-register it.
  23. self.cf.register_option('chained_assignment', 'raise')
  24. def teardown_method(self, method):
  25. setattr(self.cf, '_global_config', self.gc)
  26. setattr(self.cf, '_deprecated_options', self.do)
  27. setattr(self.cf, '_registered_options', self.ro)
  28. def test_api(self):
  29. # the pandas object exposes the user API
  30. assert hasattr(pd, 'get_option')
  31. assert hasattr(pd, 'set_option')
  32. assert hasattr(pd, 'reset_option')
  33. assert hasattr(pd, 'describe_option')
  34. def test_is_one_of_factory(self):
  35. v = self.cf.is_one_of_factory([None, 12])
  36. v(12)
  37. v(None)
  38. pytest.raises(ValueError, v, 1.1)
  39. def test_register_option(self):
  40. self.cf.register_option('a', 1, 'doc')
  41. # can't register an already registered option
  42. pytest.raises(KeyError, self.cf.register_option, 'a', 1, 'doc')
  43. # can't register an already registered option
  44. pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d1', 1,
  45. 'doc')
  46. pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d2', 1,
  47. 'doc')
  48. # no python keywords
  49. pytest.raises(ValueError, self.cf.register_option, 'for', 0)
  50. pytest.raises(ValueError, self.cf.register_option, 'a.for.b', 0)
  51. # must be valid identifier (ensure attribute access works)
  52. pytest.raises(ValueError, self.cf.register_option,
  53. 'Oh my Goddess!', 0)
  54. # we can register options several levels deep
  55. # without predefining the intermediate steps
  56. # and we can define differently named options
  57. # in the same namespace
  58. self.cf.register_option('k.b.c.d1', 1, 'doc')
  59. self.cf.register_option('k.b.c.d2', 1, 'doc')
  60. def test_describe_option(self):
  61. self.cf.register_option('a', 1, 'doc')
  62. self.cf.register_option('b', 1, 'doc2')
  63. self.cf.deprecate_option('b')
  64. self.cf.register_option('c.d.e1', 1, 'doc3')
  65. self.cf.register_option('c.d.e2', 1, 'doc4')
  66. self.cf.register_option('f', 1)
  67. self.cf.register_option('g.h', 1)
  68. self.cf.register_option('k', 2)
  69. self.cf.deprecate_option('g.h', rkey="k")
  70. self.cf.register_option('l', "foo")
  71. # non-existent keys raise KeyError
  72. pytest.raises(KeyError, self.cf.describe_option, 'no.such.key')
  73. # we can get the description for any key we registered
  74. assert 'doc' in self.cf.describe_option('a', _print_desc=False)
  75. assert 'doc2' in self.cf.describe_option('b', _print_desc=False)
  76. assert 'precated' in self.cf.describe_option('b', _print_desc=False)
  77. assert 'doc3' in self.cf.describe_option('c.d.e1', _print_desc=False)
  78. assert 'doc4' in self.cf.describe_option('c.d.e2', _print_desc=False)
  79. # if no doc is specified we get a default message
  80. # saying "description not available"
  81. assert 'vailable' in self.cf.describe_option('f', _print_desc=False)
  82. assert 'vailable' in self.cf.describe_option('g.h', _print_desc=False)
  83. assert 'precated' in self.cf.describe_option('g.h', _print_desc=False)
  84. assert 'k' in self.cf.describe_option('g.h', _print_desc=False)
  85. # default is reported
  86. assert 'foo' in self.cf.describe_option('l', _print_desc=False)
  87. # current value is reported
  88. assert 'bar' not in self.cf.describe_option('l', _print_desc=False)
  89. self.cf.set_option("l", "bar")
  90. assert 'bar' in self.cf.describe_option('l', _print_desc=False)
  91. def test_case_insensitive(self):
  92. self.cf.register_option('KanBAN', 1, 'doc')
  93. assert 'doc' in self.cf.describe_option('kanbaN', _print_desc=False)
  94. assert self.cf.get_option('kanBaN') == 1
  95. self.cf.set_option('KanBan', 2)
  96. assert self.cf.get_option('kAnBaN') == 2
  97. # gets of non-existent keys fail
  98. pytest.raises(KeyError, self.cf.get_option, 'no_such_option')
  99. self.cf.deprecate_option('KanBan')
  100. assert self.cf._is_deprecated('kAnBaN')
  101. def test_get_option(self):
  102. self.cf.register_option('a', 1, 'doc')
  103. self.cf.register_option('b.c', 'hullo', 'doc2')
  104. self.cf.register_option('b.b', None, 'doc2')
  105. # gets of existing keys succeed
  106. assert self.cf.get_option('a') == 1
  107. assert self.cf.get_option('b.c') == 'hullo'
  108. assert self.cf.get_option('b.b') is None
  109. # gets of non-existent keys fail
  110. pytest.raises(KeyError, self.cf.get_option, 'no_such_option')
  111. def test_set_option(self):
  112. self.cf.register_option('a', 1, 'doc')
  113. self.cf.register_option('b.c', 'hullo', 'doc2')
  114. self.cf.register_option('b.b', None, 'doc2')
  115. assert self.cf.get_option('a') == 1
  116. assert self.cf.get_option('b.c') == 'hullo'
  117. assert self.cf.get_option('b.b') is None
  118. self.cf.set_option('a', 2)
  119. self.cf.set_option('b.c', 'wurld')
  120. self.cf.set_option('b.b', 1.1)
  121. assert self.cf.get_option('a') == 2
  122. assert self.cf.get_option('b.c') == 'wurld'
  123. assert self.cf.get_option('b.b') == 1.1
  124. pytest.raises(KeyError, self.cf.set_option, 'no.such.key', None)
  125. def test_set_option_empty_args(self):
  126. pytest.raises(ValueError, self.cf.set_option)
  127. def test_set_option_uneven_args(self):
  128. pytest.raises(ValueError, self.cf.set_option, 'a.b', 2, 'b.c')
  129. def test_set_option_invalid_single_argument_type(self):
  130. pytest.raises(ValueError, self.cf.set_option, 2)
  131. def test_set_option_multiple(self):
  132. self.cf.register_option('a', 1, 'doc')
  133. self.cf.register_option('b.c', 'hullo', 'doc2')
  134. self.cf.register_option('b.b', None, 'doc2')
  135. assert self.cf.get_option('a') == 1
  136. assert self.cf.get_option('b.c') == 'hullo'
  137. assert self.cf.get_option('b.b') is None
  138. self.cf.set_option('a', '2', 'b.c', None, 'b.b', 10.0)
  139. assert self.cf.get_option('a') == '2'
  140. assert self.cf.get_option('b.c') is None
  141. assert self.cf.get_option('b.b') == 10.0
  142. def test_validation(self):
  143. self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int)
  144. self.cf.register_option('b.c', 'hullo', 'doc2',
  145. validator=self.cf.is_text)
  146. pytest.raises(ValueError, self.cf.register_option, 'a.b.c.d2',
  147. 'NO', 'doc', validator=self.cf.is_int)
  148. self.cf.set_option('a', 2) # int is_int
  149. self.cf.set_option('b.c', 'wurld') # str is_str
  150. pytest.raises(
  151. ValueError, self.cf.set_option, 'a', None) # None not is_int
  152. pytest.raises(ValueError, self.cf.set_option, 'a', 'ab')
  153. pytest.raises(ValueError, self.cf.set_option, 'b.c', 1)
  154. validator = self.cf.is_one_of_factory([None, self.cf.is_callable])
  155. self.cf.register_option('b', lambda: None, 'doc',
  156. validator=validator)
  157. self.cf.set_option('b', '%.1f'.format) # Formatter is callable
  158. self.cf.set_option('b', None) # Formatter is none (default)
  159. pytest.raises(ValueError, self.cf.set_option, 'b', '%.1f')
  160. def test_reset_option(self):
  161. self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int)
  162. self.cf.register_option('b.c', 'hullo', 'doc2',
  163. validator=self.cf.is_str)
  164. assert self.cf.get_option('a') == 1
  165. assert self.cf.get_option('b.c') == 'hullo'
  166. self.cf.set_option('a', 2)
  167. self.cf.set_option('b.c', 'wurld')
  168. assert self.cf.get_option('a') == 2
  169. assert self.cf.get_option('b.c') == 'wurld'
  170. self.cf.reset_option('a')
  171. assert self.cf.get_option('a') == 1
  172. assert self.cf.get_option('b.c') == 'wurld'
  173. self.cf.reset_option('b.c')
  174. assert self.cf.get_option('a') == 1
  175. assert self.cf.get_option('b.c') == 'hullo'
  176. def test_reset_option_all(self):
  177. self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int)
  178. self.cf.register_option('b.c', 'hullo', 'doc2',
  179. validator=self.cf.is_str)
  180. assert self.cf.get_option('a') == 1
  181. assert self.cf.get_option('b.c') == 'hullo'
  182. self.cf.set_option('a', 2)
  183. self.cf.set_option('b.c', 'wurld')
  184. assert self.cf.get_option('a') == 2
  185. assert self.cf.get_option('b.c') == 'wurld'
  186. self.cf.reset_option("all")
  187. assert self.cf.get_option('a') == 1
  188. assert self.cf.get_option('b.c') == 'hullo'
  189. def test_deprecate_option(self):
  190. # we can deprecate non-existent options
  191. self.cf.deprecate_option('foo')
  192. assert self.cf._is_deprecated('foo')
  193. with warnings.catch_warnings(record=True) as w:
  194. warnings.simplefilter('always')
  195. with pytest.raises(
  196. KeyError,
  197. match="No such keys.s.: 'foo'"):
  198. self.cf.get_option('foo')
  199. assert len(w) == 1 # should have raised one warning
  200. assert 'deprecated' in str(w[-1]) # we get the default message
  201. self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int)
  202. self.cf.register_option('b.c', 'hullo', 'doc2')
  203. self.cf.register_option('foo', 'hullo', 'doc2')
  204. self.cf.deprecate_option('a', removal_ver='nifty_ver')
  205. with warnings.catch_warnings(record=True) as w:
  206. warnings.simplefilter('always')
  207. self.cf.get_option('a')
  208. assert len(w) == 1 # should have raised one warning
  209. assert 'eprecated' in str(w[-1]) # we get the default message
  210. assert 'nifty_ver' in str(w[-1]) # with the removal_ver quoted
  211. pytest.raises(
  212. KeyError, self.cf.deprecate_option, 'a') # can't depr. twice
  213. self.cf.deprecate_option('b.c', 'zounds!')
  214. with warnings.catch_warnings(record=True) as w:
  215. warnings.simplefilter('always')
  216. self.cf.get_option('b.c')
  217. assert len(w) == 1 # should have raised one warning
  218. assert 'zounds!' in str(w[-1]) # we get the custom message
  219. # test rerouting keys
  220. self.cf.register_option('d.a', 'foo', 'doc2')
  221. self.cf.register_option('d.dep', 'bar', 'doc2')
  222. assert self.cf.get_option('d.a') == 'foo'
  223. assert self.cf.get_option('d.dep') == 'bar'
  224. self.cf.deprecate_option('d.dep', rkey='d.a') # reroute d.dep to d.a
  225. with warnings.catch_warnings(record=True) as w:
  226. warnings.simplefilter('always')
  227. assert self.cf.get_option('d.dep') == 'foo'
  228. assert len(w) == 1 # should have raised one warning
  229. assert 'eprecated' in str(w[-1]) # we get the custom message
  230. with warnings.catch_warnings(record=True) as w:
  231. warnings.simplefilter('always')
  232. self.cf.set_option('d.dep', 'baz') # should overwrite "d.a"
  233. assert len(w) == 1 # should have raised one warning
  234. assert 'eprecated' in str(w[-1]) # we get the custom message
  235. with warnings.catch_warnings(record=True) as w:
  236. warnings.simplefilter('always')
  237. assert self.cf.get_option('d.dep') == 'baz'
  238. assert len(w) == 1 # should have raised one warning
  239. assert 'eprecated' in str(w[-1]) # we get the custom message
  240. def test_config_prefix(self):
  241. with self.cf.config_prefix("base"):
  242. self.cf.register_option('a', 1, "doc1")
  243. self.cf.register_option('b', 2, "doc2")
  244. assert self.cf.get_option('a') == 1
  245. assert self.cf.get_option('b') == 2
  246. self.cf.set_option('a', 3)
  247. self.cf.set_option('b', 4)
  248. assert self.cf.get_option('a') == 3
  249. assert self.cf.get_option('b') == 4
  250. assert self.cf.get_option('base.a') == 3
  251. assert self.cf.get_option('base.b') == 4
  252. assert 'doc1' in self.cf.describe_option('base.a', _print_desc=False)
  253. assert 'doc2' in self.cf.describe_option('base.b', _print_desc=False)
  254. self.cf.reset_option('base.a')
  255. self.cf.reset_option('base.b')
  256. with self.cf.config_prefix("base"):
  257. assert self.cf.get_option('a') == 1
  258. assert self.cf.get_option('b') == 2
  259. def test_callback(self):
  260. k = [None]
  261. v = [None]
  262. def callback(key):
  263. k.append(key)
  264. v.append(self.cf.get_option(key))
  265. self.cf.register_option('d.a', 'foo', cb=callback)
  266. self.cf.register_option('d.b', 'foo', cb=callback)
  267. del k[-1], v[-1]
  268. self.cf.set_option("d.a", "fooz")
  269. assert k[-1] == "d.a"
  270. assert v[-1] == "fooz"
  271. del k[-1], v[-1]
  272. self.cf.set_option("d.b", "boo")
  273. assert k[-1] == "d.b"
  274. assert v[-1] == "boo"
  275. del k[-1], v[-1]
  276. self.cf.reset_option("d.b")
  277. assert k[-1] == "d.b"
  278. def test_set_ContextManager(self):
  279. def eq(val):
  280. assert self.cf.get_option("a") == val
  281. self.cf.register_option('a', 0)
  282. eq(0)
  283. with self.cf.option_context("a", 15):
  284. eq(15)
  285. with self.cf.option_context("a", 25):
  286. eq(25)
  287. eq(15)
  288. eq(0)
  289. self.cf.set_option("a", 17)
  290. eq(17)
  291. def test_attribute_access(self):
  292. holder = []
  293. def f():
  294. options.b = 1
  295. def f2():
  296. options.display = 1
  297. def f3(key):
  298. holder.append(True)
  299. self.cf.register_option('a', 0)
  300. self.cf.register_option('c', 0, cb=f3)
  301. options = self.cf.options
  302. assert options.a == 0
  303. with self.cf.option_context("a", 15):
  304. assert options.a == 15
  305. options.a = 500
  306. assert self.cf.get_option("a") == 500
  307. self.cf.reset_option("a")
  308. assert options.a == self.cf.get_option("a", 0)
  309. pytest.raises(KeyError, f)
  310. pytest.raises(KeyError, f2)
  311. # make sure callback kicks when using this form of setting
  312. options.c = 1
  313. assert len(holder) == 1
  314. def test_option_context_scope(self):
  315. # Ensure that creating a context does not affect the existing
  316. # environment as it is supposed to be used with the `with` statement.
  317. # See https://github.com/pandas-dev/pandas/issues/8514
  318. original_value = 60
  319. context_value = 10
  320. option_name = 'a'
  321. self.cf.register_option(option_name, original_value)
  322. # Ensure creating contexts didn't affect the current context.
  323. ctx = self.cf.option_context(option_name, context_value)
  324. assert self.cf.get_option(option_name) == original_value
  325. # Ensure the correct value is available inside the context.
  326. with ctx:
  327. assert self.cf.get_option(option_name) == context_value
  328. # Ensure the current context is reset
  329. assert self.cf.get_option(option_name) == original_value
  330. def test_dictwrapper_getattr(self):
  331. options = self.cf.options
  332. # GH 19789
  333. pytest.raises(self.cf.OptionError, getattr, options, 'bananas')
  334. assert not hasattr(options, 'bananas')