_compat.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. # -*- coding: utf-8 -*-
  2. """
  3. flask._compat
  4. ~~~~~~~~~~~~~
  5. Some py2/py3 compatibility support based on a stripped down
  6. version of six so we don't have to depend on a specific version
  7. of it.
  8. :copyright: 2010 Pallets
  9. :license: BSD-3-Clause
  10. """
  11. import sys
  12. PY2 = sys.version_info[0] == 2
  13. _identity = lambda x: x
  14. try: # Python 2
  15. text_type = unicode
  16. string_types = (str, unicode)
  17. integer_types = (int, long)
  18. except NameError: # Python 3
  19. text_type = str
  20. string_types = (str,)
  21. integer_types = (int,)
  22. if not PY2:
  23. iterkeys = lambda d: iter(d.keys())
  24. itervalues = lambda d: iter(d.values())
  25. iteritems = lambda d: iter(d.items())
  26. from inspect import getfullargspec as getargspec
  27. from io import StringIO
  28. import collections.abc as collections_abc
  29. def reraise(tp, value, tb=None):
  30. if value.__traceback__ is not tb:
  31. raise value.with_traceback(tb)
  32. raise value
  33. implements_to_string = _identity
  34. else:
  35. iterkeys = lambda d: d.iterkeys()
  36. itervalues = lambda d: d.itervalues()
  37. iteritems = lambda d: d.iteritems()
  38. from inspect import getargspec
  39. from cStringIO import StringIO
  40. import collections as collections_abc
  41. exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
  42. def implements_to_string(cls):
  43. cls.__unicode__ = cls.__str__
  44. cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
  45. return cls
  46. def with_metaclass(meta, *bases):
  47. """Create a base class with a metaclass."""
  48. # This requires a bit of explanation: the basic idea is to make a
  49. # dummy metaclass for one level of class instantiation that replaces
  50. # itself with the actual metaclass.
  51. class metaclass(type):
  52. def __new__(metacls, name, this_bases, d):
  53. return meta(name, bases, d)
  54. return type.__new__(metaclass, "temporary_class", (), {})
  55. # Certain versions of pypy have a bug where clearing the exception stack
  56. # breaks the __exit__ function in a very peculiar way. The second level of
  57. # exception blocks is necessary because pypy seems to forget to check if an
  58. # exception happened until the next bytecode instruction?
  59. #
  60. # Relevant PyPy bugfix commit:
  61. # https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301
  62. # According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later
  63. # versions.
  64. #
  65. # Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug.
  66. BROKEN_PYPY_CTXMGR_EXIT = False
  67. if hasattr(sys, "pypy_version_info"):
  68. class _Mgr(object):
  69. def __enter__(self):
  70. return self
  71. def __exit__(self, *args):
  72. if hasattr(sys, "exc_clear"):
  73. # Python 3 (PyPy3) doesn't have exc_clear
  74. sys.exc_clear()
  75. try:
  76. try:
  77. with _Mgr():
  78. raise AssertionError()
  79. except: # noqa: B001
  80. # We intentionally use a bare except here. See the comment above
  81. # regarding a pypy bug as to why.
  82. raise
  83. except TypeError:
  84. BROKEN_PYPY_CTXMGR_EXIT = True
  85. except AssertionError:
  86. pass
  87. try:
  88. from os import fspath
  89. except ImportError:
  90. # Backwards compatibility as proposed in PEP 0519:
  91. # https://www.python.org/dev/peps/pep-0519/#backwards-compatibility
  92. def fspath(path):
  93. return path.__fspath__() if hasattr(path, "__fspath__") else path
  94. class _DeprecatedBool(object):
  95. def __init__(self, name, version, value):
  96. self.message = "'{}' is deprecated and will be removed in version {}.".format(
  97. name, version
  98. )
  99. self.value = value
  100. def _warn(self):
  101. import warnings
  102. warnings.warn(self.message, DeprecationWarning, stacklevel=2)
  103. def __eq__(self, other):
  104. self._warn()
  105. return other == self.value
  106. def __ne__(self, other):
  107. self._warn()
  108. return other != self.value
  109. def __bool__(self):
  110. self._warn()
  111. return self.value
  112. __nonzero__ = __bool__
  113. json_available = _DeprecatedBool("flask.json_available", "2.0.0", True)