/opentracing_instrumentation/client_hooks/__init__.py

https://github.com/uber-common/opentracing-python-instrumentation · Python · 133 lines · 96 code · 7 blank · 30 comment · 7 complexity · 44cc50f5553be7975592a16643e15276 MD5 · raw file

  1. # Copyright (c) 2015-2017 Uber Technologies, Inc.
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. # THE SOFTWARE.
  20. from __future__ import absolute_import
  21. import six
  22. if six.PY2:
  23. from collections import Sequence
  24. else:
  25. from collections.abc import Sequence
  26. import importlib
  27. import logging
  28. from ._current_span import set_current_span_func # noqa
  29. def install_all_patches():
  30. """
  31. A convenience method that installs all available hooks.
  32. If a specific module is not available on the path, it is ignored.
  33. """
  34. from . import boto3
  35. from . import celery
  36. from . import mysqldb
  37. from . import psycopg2
  38. from . import strict_redis
  39. from . import sqlalchemy
  40. from . import tornado_http
  41. from . import urllib
  42. from . import urllib2
  43. from . import requests
  44. boto3.install_patches()
  45. celery.install_patches()
  46. mysqldb.install_patches()
  47. psycopg2.install_patches()
  48. strict_redis.install_patches()
  49. sqlalchemy.install_patches()
  50. tornado_http.install_patches()
  51. urllib.install_patches()
  52. urllib2.install_patches()
  53. requests.install_patches()
  54. def install_patches(patchers='all'):
  55. """
  56. Usually called from middleware to install client hooks
  57. specified in the client_hooks section of the configuration.
  58. :param patchers: a list of patchers to run. Acceptable values include:
  59. * None - installs all client patches
  60. * 'all' - installs all client patches
  61. * empty list - does not install any patches
  62. * list of function names - executes the functions
  63. """
  64. if patchers is None or patchers == 'all':
  65. install_all_patches()
  66. return
  67. if not _valid_args(patchers):
  68. raise ValueError('patchers argument must be None, "all", or a list')
  69. for patch_func_name in patchers:
  70. logging.info('Loading client hook %s', patch_func_name)
  71. patch_func = _load_symbol(patch_func_name)
  72. logging.info('Applying client hook %s', patch_func_name)
  73. patch_func()
  74. def install_client_interceptors(client_interceptors=()):
  75. """
  76. Install client interceptors for the patchers.
  77. :param client_interceptors: a list of client interceptors to install.
  78. Should be a list of classes
  79. """
  80. if not _valid_args(client_interceptors):
  81. raise ValueError('client_interceptors argument must be a list')
  82. from ..http_client import ClientInterceptors
  83. for client_interceptor in client_interceptors:
  84. logging.info('Loading client interceptor %s', client_interceptor)
  85. interceptor_class = _load_symbol(client_interceptor)
  86. logging.info('Adding client interceptor %s', client_interceptor)
  87. ClientInterceptors.append(interceptor_class())
  88. def _valid_args(value):
  89. return isinstance(value, Sequence) and \
  90. not isinstance(value, six.string_types)
  91. def _load_symbol(name):
  92. """Load a symbol by name.
  93. :param str name: The name to load, specified by `module.attr`.
  94. :returns: The attribute value. If the specified module does not contain
  95. the requested attribute then `None` is returned.
  96. """
  97. module_name, key = name.rsplit('.', 1)
  98. try:
  99. module = importlib.import_module(module_name)
  100. except ImportError as err:
  101. # it's possible the symbol is a class method
  102. module_name, class_name = module_name.rsplit('.', 1)
  103. module = importlib.import_module(module_name)
  104. cls = getattr(module, class_name, None)
  105. if cls:
  106. attr = getattr(cls, key, None)
  107. else:
  108. raise err
  109. else:
  110. attr = getattr(module, key, None)
  111. if not callable(attr):
  112. raise ValueError('%s is not callable (was %r)' % (name, attr))
  113. return attr