compat.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. """The compat module provides various Python 2 / Python 3
  2. compatibility functions
  3. """
  4. # pylint: disable=C0103
  5. import abc
  6. import os
  7. import platform
  8. import re
  9. import socket
  10. import sys as _sys
  11. import time
  12. PY2 = _sys.version_info.major == 2
  13. PY3 = not PY2
  14. RE_NUM = re.compile(r'(\d+).+')
  15. ON_LINUX = platform.system() == 'Linux'
  16. ON_OSX = platform.system() == 'Darwin'
  17. ON_WINDOWS = platform.system() == 'Windows'
  18. # Portable Abstract Base Class
  19. AbstractBase = abc.ABCMeta('AbstractBase', (object,), {})
  20. if _sys.version_info[:2] < (3, 3):
  21. SOCKET_ERROR = socket.error
  22. else:
  23. # socket.error was deprecated and replaced by OSError in python 3.3
  24. SOCKET_ERROR = OSError
  25. try:
  26. SOL_TCP = socket.SOL_TCP
  27. except AttributeError:
  28. SOL_TCP = socket.IPPROTO_TCP
  29. if PY3:
  30. # these were moved around for Python 3
  31. # pylint: disable=W0611
  32. from urllib.parse import (quote as url_quote, unquote as url_unquote,
  33. urlencode, parse_qs as url_parse_qs, urlparse)
  34. from io import StringIO
  35. # Python 3 does not have basestring anymore; we include
  36. # *only* the str here as this is used for textual data.
  37. basestring = (str,)
  38. # for assertions that the data is either encoded or non-encoded text
  39. str_or_bytes = (str, bytes)
  40. # xrange is gone, replace it with range
  41. xrange = range
  42. # the unicode type is str
  43. unicode_type = str
  44. def time_now():
  45. """
  46. Python 3 supports monotonic time
  47. """
  48. return time.monotonic()
  49. def dictkeys(dct):
  50. """
  51. Returns a list of keys of dictionary
  52. dict.keys returns a view that works like .keys in Python 2
  53. *except* any modifications in the dictionary will be visible
  54. (and will cause errors if the view is being iterated over while
  55. it is modified).
  56. """
  57. return list(dct.keys())
  58. def dictvalues(dct):
  59. """
  60. Returns a list of values of a dictionary
  61. dict.values returns a view that works like .values in Python 2
  62. *except* any modifications in the dictionary will be visible
  63. (and will cause errors if the view is being iterated over while
  64. it is modified).
  65. """
  66. return list(dct.values())
  67. def dict_iteritems(dct):
  68. """
  69. Returns an iterator of items (key/value pairs) of a dictionary
  70. dict.items returns a view that works like .items in Python 2
  71. *except* any modifications in the dictionary will be visible
  72. (and will cause errors if the view is being iterated over while
  73. it is modified).
  74. """
  75. return dct.items()
  76. def dict_itervalues(dct):
  77. """
  78. :param dict dct:
  79. :returns: an iterator of the values of a dictionary
  80. :rtype: iterator
  81. """
  82. return dct.values()
  83. def byte(*args):
  84. """
  85. This is the same as Python 2 `chr(n)` for bytes in Python 3
  86. Returns a single byte `bytes` for the given int argument (we
  87. optimize it a bit here by passing the positional argument tuple
  88. directly to the bytes constructor.
  89. """
  90. return bytes(args)
  91. class long(int):
  92. """
  93. A marker class that signifies that the integer value should be
  94. serialized as `l` instead of `I`
  95. """
  96. def __repr__(self):
  97. return str(self) + 'L'
  98. def canonical_str(value):
  99. """
  100. Return the canonical str value for the string.
  101. In both Python 3 and Python 2 this is str.
  102. """
  103. return str(value)
  104. def is_integer(value):
  105. """
  106. Is value an integer?
  107. """
  108. return isinstance(value, int)
  109. else:
  110. from urllib import (quote as url_quote, unquote as url_unquote, urlencode) # pylint: disable=C0412,E0611
  111. from urlparse import (parse_qs as url_parse_qs, urlparse) # pylint: disable=E0401
  112. from StringIO import StringIO # pylint: disable=E0401
  113. basestring = basestring
  114. str_or_bytes = basestring
  115. xrange = xrange
  116. unicode_type = unicode # pylint: disable=E0602
  117. dictkeys = dict.keys
  118. dictvalues = dict.values
  119. dict_iteritems = dict.iteritems # pylint: disable=E1101
  120. dict_itervalues = dict.itervalues # pylint: disable=E1101
  121. byte = chr
  122. long = long
  123. def time_now():
  124. """
  125. Python 2 does not support monotonic time
  126. """
  127. return time.time()
  128. def canonical_str(value):
  129. """
  130. Returns the canonical string value of the given string.
  131. In Python 2 this is the value unchanged if it is an str, otherwise
  132. it is the unicode value encoded as UTF-8.
  133. """
  134. try:
  135. return str(value)
  136. except UnicodeEncodeError:
  137. return str(value.encode('utf-8'))
  138. def is_integer(value):
  139. """
  140. Is value an integer?
  141. """
  142. return isinstance(value, (int, long))
  143. def as_bytes(value):
  144. """
  145. Returns value as bytes
  146. """
  147. if not isinstance(value, bytes):
  148. return value.encode('UTF-8')
  149. return value
  150. def to_digit(value):
  151. """
  152. Returns value as in integer
  153. """
  154. if value.isdigit():
  155. return int(value)
  156. match = RE_NUM.match(value)
  157. return int(match.groups()[0]) if match else 0
  158. def get_linux_version(release_str):
  159. """
  160. Gets linux version
  161. """
  162. ver_str = release_str.split('-')[0]
  163. return tuple(map(to_digit, ver_str.split('.')[:3]))
  164. HAVE_SIGNAL = os.name == 'posix'
  165. EINTR_IS_EXPOSED = _sys.version_info[:2] <= (3, 4)
  166. LINUX_VERSION = None
  167. if platform.system() == 'Linux':
  168. LINUX_VERSION = get_linux_version(platform.release())
  169. _LOCALHOST = '127.0.0.1'
  170. _LOCALHOST_V6 = '::1'
  171. def _nonblocking_socketpair(family=socket.AF_INET,
  172. socket_type=socket.SOCK_STREAM,
  173. proto=0):
  174. """
  175. Returns a pair of sockets in the manner of socketpair with the additional
  176. feature that they will be non-blocking. Prior to Python 3.5, socketpair
  177. did not exist on Windows at all.
  178. """
  179. if family == socket.AF_INET:
  180. host = _LOCALHOST
  181. elif family == socket.AF_INET6:
  182. host = _LOCALHOST_V6
  183. else:
  184. raise ValueError('Only AF_INET and AF_INET6 socket address families '
  185. 'are supported')
  186. if socket_type != socket.SOCK_STREAM:
  187. raise ValueError('Only SOCK_STREAM socket socket_type is supported')
  188. if proto != 0:
  189. raise ValueError('Only protocol zero is supported')
  190. lsock = socket.socket(family, socket_type, proto)
  191. try:
  192. lsock.bind((host, 0))
  193. lsock.listen(min(socket.SOMAXCONN, 128))
  194. # On IPv6, ignore flow_info and scope_id
  195. addr, port = lsock.getsockname()[:2]
  196. csock = socket.socket(family, socket_type, proto)
  197. try:
  198. csock.connect((addr, port))
  199. ssock, _ = lsock.accept()
  200. except Exception:
  201. csock.close()
  202. raise
  203. finally:
  204. lsock.close()
  205. # Make sockets non-blocking to prevent deadlocks
  206. # See https://github.com/pika/pika/issues/917
  207. csock.setblocking(False)
  208. ssock.setblocking(False)
  209. return ssock, csock