_unicodefun.py 4.3 KB

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