_unicodefun.py 4.2 KB

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