Files
2020-04-08 10:39:44 -07:00

152 lines
4.4 KiB
Python

import platform
import re
import sys
import textwrap
from ddtrace.vendor import six
__all__ = [
'httplib',
'iteritems',
'PY2',
'Queue',
'stringify',
'StringIO',
'urlencode',
'parse',
'reraise',
]
PYTHON_VERSION_INFO = sys.version_info
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
# Infos about python passed to the trace agent through the header
PYTHON_VERSION = platform.python_version()
PYTHON_INTERPRETER = platform.python_implementation()
try:
StringIO = six.moves.cStringIO
except ImportError:
StringIO = six.StringIO
httplib = six.moves.http_client
urlencode = six.moves.urllib.parse.urlencode
parse = six.moves.urllib.parse
Queue = six.moves.queue.Queue
iteritems = six.iteritems
reraise = six.reraise
reload_module = six.moves.reload_module
stringify = six.text_type
string_type = six.string_types[0]
msgpack_type = six.binary_type
# DEV: `six` doesn't have `float` in `integer_types`
numeric_types = six.integer_types + (float, )
# Pattern class generated by `re.compile`
if PYTHON_VERSION_INFO >= (3, 7):
pattern_type = re.Pattern
else:
pattern_type = re._pattern_type
def is_integer(obj):
"""Helper to determine if the provided ``obj`` is an integer type or not"""
# DEV: We have to make sure it is an integer and not a boolean
# >>> type(True)
# <class 'bool'>
# >>> isinstance(True, int)
# True
return isinstance(obj, six.integer_types) and not isinstance(obj, bool)
try:
from time import time_ns
except ImportError:
from time import time as _time
def time_ns():
return int(_time() * 10e5) * 1000
if PYTHON_VERSION_INFO[0:2] >= (3, 4):
from asyncio import iscoroutinefunction
# Execute from a string to get around syntax errors from `yield from`
# DEV: The idea to do this was stolen from `six`
# https://github.com/benjaminp/six/blob/15e31431af97e5e64b80af0a3f598d382bcdd49a/six.py#L719-L737
six.exec_(textwrap.dedent("""
import functools
import asyncio
def make_async_decorator(tracer, coro, *params, **kw_params):
\"\"\"
Decorator factory that creates an asynchronous wrapper that yields
a coroutine result. This factory is required to handle Python 2
compatibilities.
:param object tracer: the tracer instance that is used
:param function f: the coroutine that must be executed
:param tuple params: arguments given to the Tracer.trace()
:param dict kw_params: keyword arguments given to the Tracer.trace()
\"\"\"
@functools.wraps(coro)
@asyncio.coroutine
def func_wrapper(*args, **kwargs):
with tracer.trace(*params, **kw_params):
result = yield from coro(*args, **kwargs) # noqa: E999
return result
return func_wrapper
"""))
else:
# asyncio is missing so we can't have coroutines; these
# functions are used only to ensure code executions in case
# of an unexpected behavior
def iscoroutinefunction(fn):
return False
def make_async_decorator(tracer, fn, *params, **kw_params):
return fn
# DEV: There is `six.u()` which does something similar, but doesn't have the guard around `hasattr(s, 'decode')`
def to_unicode(s):
""" Return a unicode string for the given bytes or string instance. """
# No reason to decode if we already have the unicode compatible object we expect
# DEV: `six.text_type` will be a `str` for python 3 and `unicode` for python 2
# DEV: Double decoding a `unicode` can cause a `UnicodeEncodeError`
# e.g. `'\xc3\xbf'.decode('utf-8').decode('utf-8')`
if isinstance(s, six.text_type):
return s
# If the object has a `decode` method, then decode into `utf-8`
# e.g. Python 2 `str`, Python 2/3 `bytearray`, etc
if hasattr(s, 'decode'):
return s.decode('utf-8')
# Always try to coerce the object into the `six.text_type` object we expect
# e.g. `to_unicode(1)`, `to_unicode(dict(key='value'))`
return six.text_type(s)
def get_connection_response(conn):
"""Returns the response for a connection.
If using Python 2 enable buffering.
Python 2 does not enable buffering by default resulting in many recv
syscalls.
See:
https://bugs.python.org/issue4879
https://github.com/python/cpython/commit/3c43fcba8b67ea0cec4a443c755ce5f25990a6cf
"""
if PY2:
return conn.getresponse(buffering=True)
else:
return conn.getresponse()