test_localization.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import codecs
  2. import locale
  3. import os
  4. import pytest
  5. from pandas._config.localization import (
  6. can_set_locale,
  7. get_locales,
  8. set_locale,
  9. )
  10. from pandas.compat import ISMUSL
  11. import pandas as pd
  12. _all_locales = get_locales()
  13. _current_locale = locale.setlocale(locale.LC_ALL) # getlocale() is wrong, see GH#46595
  14. # Don't run any of these tests if we have no locales.
  15. pytestmark = pytest.mark.skipif(not _all_locales, reason="Need locales")
  16. _skip_if_only_one_locale = pytest.mark.skipif(
  17. len(_all_locales) <= 1, reason="Need multiple locales for meaningful test"
  18. )
  19. def _get_current_locale(lc_var: int = locale.LC_ALL) -> str:
  20. # getlocale is not always compliant with setlocale, use setlocale. GH#46595
  21. return locale.setlocale(lc_var)
  22. @pytest.mark.parametrize("lc_var", (locale.LC_ALL, locale.LC_CTYPE, locale.LC_TIME))
  23. def test_can_set_current_locale(lc_var):
  24. # Can set the current locale
  25. before_locale = _get_current_locale(lc_var)
  26. assert can_set_locale(before_locale, lc_var=lc_var)
  27. after_locale = _get_current_locale(lc_var)
  28. assert before_locale == after_locale
  29. @pytest.mark.parametrize("lc_var", (locale.LC_ALL, locale.LC_CTYPE, locale.LC_TIME))
  30. def test_can_set_locale_valid_set(lc_var):
  31. # Can set the default locale.
  32. before_locale = _get_current_locale(lc_var)
  33. assert can_set_locale("", lc_var=lc_var)
  34. after_locale = _get_current_locale(lc_var)
  35. assert before_locale == after_locale
  36. @pytest.mark.parametrize(
  37. "lc_var",
  38. (
  39. locale.LC_ALL,
  40. locale.LC_CTYPE,
  41. pytest.param(
  42. locale.LC_TIME,
  43. marks=pytest.mark.skipif(
  44. ISMUSL, reason="MUSL allows setting invalid LC_TIME."
  45. ),
  46. ),
  47. ),
  48. )
  49. def test_can_set_locale_invalid_set(lc_var):
  50. # Cannot set an invalid locale.
  51. before_locale = _get_current_locale(lc_var)
  52. assert not can_set_locale("non-existent_locale", lc_var=lc_var)
  53. after_locale = _get_current_locale(lc_var)
  54. assert before_locale == after_locale
  55. @pytest.mark.parametrize(
  56. "lang,enc",
  57. [
  58. ("it_CH", "UTF-8"),
  59. ("en_US", "ascii"),
  60. ("zh_CN", "GB2312"),
  61. ("it_IT", "ISO-8859-1"),
  62. ],
  63. )
  64. @pytest.mark.parametrize("lc_var", (locale.LC_ALL, locale.LC_CTYPE, locale.LC_TIME))
  65. def test_can_set_locale_no_leak(lang, enc, lc_var):
  66. # Test that can_set_locale does not leak even when returning False. See GH#46595
  67. before_locale = _get_current_locale(lc_var)
  68. can_set_locale((lang, enc), locale.LC_ALL)
  69. after_locale = _get_current_locale(lc_var)
  70. assert before_locale == after_locale
  71. def test_can_set_locale_invalid_get(monkeypatch):
  72. # see GH#22129
  73. # In some cases, an invalid locale can be set,
  74. # but a subsequent getlocale() raises a ValueError.
  75. def mock_get_locale():
  76. raise ValueError()
  77. with monkeypatch.context() as m:
  78. m.setattr(locale, "getlocale", mock_get_locale)
  79. assert not can_set_locale("")
  80. def test_get_locales_at_least_one():
  81. # see GH#9744
  82. assert len(_all_locales) > 0
  83. @_skip_if_only_one_locale
  84. def test_get_locales_prefix():
  85. first_locale = _all_locales[0]
  86. assert len(get_locales(prefix=first_locale[:2])) > 0
  87. @_skip_if_only_one_locale
  88. @pytest.mark.parametrize(
  89. "lang,enc",
  90. [
  91. ("it_CH", "UTF-8"),
  92. ("en_US", "ascii"),
  93. ("zh_CN", "GB2312"),
  94. ("it_IT", "ISO-8859-1"),
  95. ],
  96. )
  97. def test_set_locale(lang, enc):
  98. before_locale = _get_current_locale()
  99. enc = codecs.lookup(enc).name
  100. new_locale = lang, enc
  101. if not can_set_locale(new_locale):
  102. msg = "unsupported locale setting"
  103. with pytest.raises(locale.Error, match=msg):
  104. with set_locale(new_locale):
  105. pass
  106. else:
  107. with set_locale(new_locale) as normalized_locale:
  108. new_lang, new_enc = normalized_locale.split(".")
  109. new_enc = codecs.lookup(enc).name
  110. normalized_locale = new_lang, new_enc
  111. assert normalized_locale == new_locale
  112. # Once we exit the "with" statement, locale should be back to what it was.
  113. after_locale = _get_current_locale()
  114. assert before_locale == after_locale
  115. def test_encoding_detected():
  116. system_locale = os.environ.get("LC_ALL")
  117. system_encoding = system_locale.split(".")[-1] if system_locale else "utf-8"
  118. assert (
  119. codecs.lookup(pd.options.display.encoding).name
  120. == codecs.lookup(system_encoding).name
  121. )