152 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			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()
 |