_unicodefun.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import codecs
  2. import os
  3. import sys
  4. from ._compat import PY2
  5. def _find_unicode_literals_frame():
  6. import __future__
  7. if not hasattr(sys, "_getframe"): # not all Python implementations have it
  8. return 0
  9. frm = sys._getframe(1)
  10. idx = 1
  11. while frm is not None:
  12. if frm.f_globals.get("__name__", "").startswith("click."):
  13. frm = frm.f_back
  14. idx += 1
  15. elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag:
  16. return idx
  17. else:
  18. break
  19. return 0
  20. def _check_for_unicode_literals():
  21. if not __debug__:
  22. return
  23. from . import disable_unicode_literals_warning
  24. if not PY2 or disable_unicode_literals_warning:
  25. return
  26. bad_frame = _find_unicode_literals_frame()
  27. if bad_frame <= 0:
  28. return
  29. from warnings import warn
  30. warn(
  31. Warning(
  32. "Click detected the use of the unicode_literals __future__"
  33. " import. This is heavily discouraged because it can"
  34. " introduce subtle bugs in your code. You should instead"
  35. ' use explicit u"" literals for your unicode strings. For'
  36. " more information see"
  37. " https://click.palletsprojects.com/python3/"
  38. ),
  39. stacklevel=bad_frame,
  40. )
  41. def _verify_python3_env():
  42. """Ensures that the environment is good for unicode on Python 3."""
  43. if PY2:
  44. return
  45. try:
  46. import locale
  47. fs_enc = codecs.lookup(locale.getpreferredencoding()).name
  48. except Exception:
  49. fs_enc = "ascii"
  50. if fs_enc != "ascii":
  51. return
  52. extra = ""
  53. if os.name == "posix":
  54. import subprocess
  55. try:
  56. rv = subprocess.Popen(
  57. ["locale", "-a"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
  58. ).communicate()[0]
  59. except OSError:
  60. rv = b""
  61. good_locales = set()
  62. has_c_utf8 = False
  63. # Make sure we're operating on text here.
  64. if isinstance(rv, bytes):
  65. rv = rv.decode("ascii", "replace")
  66. for line in rv.splitlines():
  67. locale = line.strip()
  68. if locale.lower().endswith((".utf-8", ".utf8")):
  69. good_locales.add(locale)
  70. if locale.lower() in ("c.utf8", "c.utf-8"):
  71. has_c_utf8 = True
  72. extra += "\n\n"
  73. if not good_locales:
  74. extra += (
  75. "Additional information: on this system no suitable"
  76. " UTF-8 locales were discovered. This most likely"
  77. " requires resolving by reconfiguring the locale"
  78. " system."
  79. )
  80. elif has_c_utf8:
  81. extra += (
  82. "This system supports the C.UTF-8 locale which is"
  83. " recommended. You might be able to resolve your issue"
  84. " by exporting the following environment variables:\n\n"
  85. " export LC_ALL=C.UTF-8\n"
  86. " export LANG=C.UTF-8"
  87. )
  88. else:
  89. extra += (
  90. "This system lists a couple of UTF-8 supporting locales"
  91. " that you can pick from. The following suitable"
  92. " locales were discovered: {}".format(", ".join(sorted(good_locales)))
  93. )
  94. bad_locale = None
  95. for locale in os.environ.get("LC_ALL"), os.environ.get("LANG"):
  96. if locale and locale.lower().endswith((".utf-8", ".utf8")):
  97. bad_locale = locale
  98. if locale is not None:
  99. break
  100. if bad_locale is not None:
  101. extra += (
  102. "\n\nClick discovered that you exported a UTF-8 locale"
  103. " but the locale system could not pick up from it"
  104. " because it does not exist. The exported locale is"
  105. " '{}' but it is not supported".format(bad_locale)
  106. )
  107. raise RuntimeError(
  108. "Click will abort further execution because Python 3 was"
  109. " configured to use ASCII as encoding for the environment."
  110. " Consult https://click.palletsprojects.com/python3/ for"
  111. " mitigation steps.{}".format(extra)
  112. )