We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
botocore gets monkey patched before gevent when using pynamoDB
In [0.43.0 ssl libs are patched on import](https://github.com/DataDog/dd-trace-py/pull/1629) to allow `ddtrace-run` and `gevent` to exist in harmony.

`pynamodb` imports `botocore` and PynamoDB is patched by default. The result of this is that `ddtrace-run` ends up monkey patching `botocore` before `gevent` does.

I believe PynamoDB should be listed in the SSL libs that only get patched on import.

### Which version of dd-trace-py are you using?
0.43.0

### Which version of the libraries are you using?

ddtrace==0.43.0
gevent==20.9.0
greenlet==0.4.17
gunicorn==20.0.4
pynamodb==4.3.3

### How can we reproduce your problem?

1. Create new virtualenv
```
$ mkdir temp
$ cd temp
$ virtualenv .
$ . ./bin/active
```

2. Install libs
```
pip install ddtrace gunicorn[gevent] pynamodb
```

3. Create empty `app.py`
```
import time
while True:
    time.sleep(1)
```

Run the failing command
```
 ddtrace-run gunicorn -k gevent app
```

The following warning is displayed, which will turn into a SSL recursion error if you try and use urllib3.

```
$ ddtrace-run gunicorn -k gevent app
- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused
- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused
[2020-10-12 16:46:09 +1100] [69996] [INFO] Starting gunicorn 20.0.4
[2020-10-12 16:46:09 +1100] [69996] [INFO] Listening at: http://127.0.0.1:8000 (69996)
[2020-10-12 16:46:09 +1100] [69996] [INFO] Using worker: gevent
[2020-10-12 16:46:09 +1100] [70004] [INFO] Booting worker with pid: 70004
/private/tmp/venv/lib/python3.7/site-packages/gunicorn/workers/ggevent.py:53: MonkeyPatchWarning: Monkey-patching ssl after ssl has already been imported may lead to errors, including RecursionError on Python 3.6. It may also silently lead to incorrect behaviour on Python 3.7. Please monkey-patch earlier. See https://github.com/gevent/gevent/issues/1016. Modules that had direct imports (NOT patched): ['botocore.httpsession (/private/tmp/venv/lib/python3.7/site-packages/botocore/httpsession.py)', 'urllib3.util.ssl_ (/private/tmp/venv/lib/python3.7/site-packages/urllib3/util/ssl_.py)', 'urllib3.util (/private/tmp/venv/lib/python3.7/site-packages/urllib3/util/__init__.py)'].
  monkey.patch_all()
```

Disable pynamodb tracing to fix
```
DD_TRACE_PYNAMODB_ENABLED=False ddtrace-run gunicorn -k gevent app
```

Which gives the following output
```
$ DD_TRACE_PYNAMODB_ENABLED=False ddtrace-run gunicorn -k gevent app
- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused
- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused
[2020-10-12 16:48:11 +1100] [70038] [INFO] Starting gunicorn 20.0.4
[2020-10-12 16:48:11 +1100] [70038] [INFO] Listening at: http://127.0.0.1:8000 (70038)
[2020-10-12 16:48:11 +1100] [70038] [INFO] Using worker: gevent
[2020-10-12 16:48:11 +1100] [70046] [INFO] Booting worker with pid: 70046
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/monkey.py`
Content:
```
1 """Patch libraries to be automatically instrumented.
2 
3 It can monkey patch supported standard libraries and third party modules.
4 A patched module will automatically report spans with its default configuration.
5 
6 A library instrumentation can be configured (for instance, to report as another service)
7 using Pin. For that, check its documentation.
8 """
9 import importlib
10 import os
11 import sys
12 import threading
13 
14 from ddtrace.vendor.wrapt.importer import when_imported
15 
16 from .internal.logger import get_logger
17 from .settings import config
18 from .utils import formats
19 
20 
21 log = get_logger(__name__)
22 
23 # Default set of modules to automatically patch or not
24 PATCH_MODULES = {
25     "asyncio": True,
26     "boto": True,
27     "botocore": True,
28     "bottle": False,
29     "cassandra": True,
30     "celery": True,
31     "consul": True,
32     "django": True,
33     "elasticsearch": True,
34     "algoliasearch": True,
35     "futures": False,  # experimental propagation
36     "grpc": True,
37     "mongoengine": True,
38     "mysql": True,
39     "mysqldb": True,
40     "pymysql": True,
41     "psycopg": True,
42     "pylibmc": True,
43     "pymemcache": True,
44     "pymongo": True,
45     "redis": True,
46     "rediscluster": True,
47     "requests": True,
48     "sanic": True,
49     "sqlalchemy": False,  # Prefer DB client instrumentation
50     "sqlite3": True,
51     "aiohttp": True,  # requires asyncio (Python 3.4+)
52     "aiopg": True,
53     "aiobotocore": False,
54     "httplib": False,
55     "vertica": True,
56     "molten": True,
57     "jinja2": True,
58     "mako": True,
59     "flask": True,
60     "kombu": False,
61     "starlette": True,
62     # Ignore some web framework integrations that might be configured explicitly in code
63     "falcon": False,
64     "pylons": False,
65     "pyramid": False,
66     # Auto-enable logging if the environment variable DD_LOGS_INJECTION is true
67     "logging": config.logs_injection,
68     "pynamodb": True,
69 }
70 
71 _LOCK = threading.Lock()
72 _PATCHED_MODULES = set()
73 
74 # Modules which are patched on first use
75 # DEV: These modules are patched when the user first imports them, rather than
76 #      explicitly importing and patching them on application startup `ddtrace.patch_all(module=True)`
77 # DEV: This ensures we do not patch a module until it is needed
78 # DEV: <contrib name> => <list of module names that trigger a patch>
79 _PATCH_ON_IMPORT = {
80     "aiohttp": ("aiohttp",),
81     "aiobotocore": ("aiobotocore",),
82     "celery": ("celery",),
83     "flask": ("flask, "),
84     "gevent": ("gevent",),
85     "requests": ("requests",),
86     "botocore": ("botocore",),
87     "elasticsearch": ("elasticsearch",),
88 }
89 
90 
91 class PatchException(Exception):
92     """Wraps regular `Exception` class when patching modules"""
93 
94     pass
95 
96 
97 class ModuleNotFoundException(PatchException):
98     pass
99 
100 
101 def _on_import_factory(module, raise_errors=True):
102     """Factory to create an import hook for the provided module name"""
103 
104     def on_import(hook):
105         # Import and patch module
106         path = "ddtrace.contrib.%s" % module
107         imported_module = importlib.import_module(path)
108         imported_module.patch()
109 
110     return on_import
111 
112 
113 def patch_all(**patch_modules):
114     """Automatically patches all available modules.
115 
116     In addition to ``patch_modules``, an override can be specified via an
117     environment variable, ``DD_TRACE_<module>_ENABLED`` for each module.
118 
119     ``patch_modules`` have the highest precedence for overriding.
120 
121     :param dict patch_modules: Override whether particular modules are patched or not.
122 
123         >>> patch_all(redis=False, cassandra=False)
124     """
125     modules = PATCH_MODULES.copy()
126 
127     # The enabled setting can be overridden by environment variables
128     for module, enabled in modules.items():
129         env_var = "DD_TRACE_%s_ENABLED" % module.upper()
130         if env_var not in os.environ:
131             continue
132 
133         override_enabled = formats.asbool(os.environ[env_var])
134         modules[module] = override_enabled
135 
136     # Arguments take precedence over the environment and the defaults.
137     modules.update(patch_modules)
138 
139     patch(raise_errors=False, **modules)
140 
141 
142 def patch(raise_errors=True, **patch_modules):
143     """Patch only a set of given modules.
144 
145     :param bool raise_errors: Raise error if one patch fail.
146     :param dict patch_modules: List of modules to patch.
147 
148         >>> patch(psycopg=True, elasticsearch=True)
149     """
150     modules = [m for (m, should_patch) in patch_modules.items() if should_patch]
151     for module in modules:
152         if module in _PATCH_ON_IMPORT:
153             # If the module has already been imported then patch immediately
154             if module in sys.modules:
155                 patch_module(module, raise_errors=raise_errors)
156 
157             # Otherwise, add a hook to patch when it is imported for the first time
158             else:
159                 # Use factory to create handler to close over `module` and `raise_errors` values from this loop
160                 when_imported(module)(_on_import_factory(module, raise_errors))
161 
162                 # manually add module to patched modules
163                 with _LOCK:
164                     _PATCHED_MODULES.add(module)
165         else:
166             patch_module(module, raise_errors=raise_errors)
167 
168     patched_modules = get_patched_modules()
169     log.info(
170         "patched %s/%s modules (%s)",
171         len(patched_modules),
172         len(modules),
173         ",".join(patched_modules),
174     )
175 
176 
177 def patch_module(module, raise_errors=True):
178     """Patch a single module
179 
180     Returns if the module got properly patched.
181     """
182     try:
183         return _patch_module(module)
184     except ModuleNotFoundException:
185         if raise_errors:
186             raise
187         return False
188     except Exception:
189         if raise_errors:
190             raise
191         log.debug("failed to patch %s", module, exc_info=True)
192         return False
193 
194 
195 def get_patched_modules():
196     """Get the list of patched modules"""
197     with _LOCK:
198         return sorted(_PATCHED_MODULES)
199 
200 
201 def _patch_module(module):
202     """_patch_module will attempt to monkey patch the module.
203 
204     Returns if the module got patched.
205     Can also raise errors if it fails.
206     """
207     path = "ddtrace.contrib.%s" % module
208     with _LOCK:
209         if module in _PATCHED_MODULES and module not in _PATCH_ON_IMPORT:
210             log.debug("already patched: %s", path)
211             return False
212 
213         try:
214             imported_module = importlib.import_module(path)
215         except ImportError:
216             # if the import fails, the integration is not available
217             raise PatchException("integration '%s' not available" % path)
218         else:
219             # if patch() is not available in the module, it means
220             # that the library is not installed in the environment
221             if not hasattr(imported_module, "patch"):
222                 raise ModuleNotFoundException("module '%s' not installed" % module)
223 
224             imported_module.patch()
225             _PATCHED_MODULES.add(module)
226             return True
227
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ddtrace/monkey.py b/ddtrace/monkey.py
--- a/ddtrace/monkey.py
+++ b/ddtrace/monkey.py
@@ -85,6 +85,7 @@
     "requests": ("requests",),
     "botocore": ("botocore",),
     "elasticsearch": ("elasticsearch",),
+    "pynamodb": ("pynamodb",),
 }

{"golden_diff": "diff --git a/ddtrace/monkey.py b/ddtrace/monkey.py\n--- a/ddtrace/monkey.py\n+++ b/ddtrace/monkey.py\n@@ -85,6 +85,7 @@\n     \"requests\": (\"requests\",),\n     \"botocore\": (\"botocore\",),\n     \"elasticsearch\": (\"elasticsearch\",),\n+    \"pynamodb\": (\"pynamodb\",),\n }\n", "issue": "botocore gets monkey patched before gevent when using pynamoDB\nIn [0.43.0 ssl libs are patched on import](https://github.com/DataDog/dd-trace-py/pull/1629) to allow `ddtrace-run` and `gevent` to exist in harmony.\r\n\r\n`pynamodb` imports `botocore` and PynamoDB is patched by default. The result of this is that `ddtrace-run` ends up monkey patching `botocore` before `gevent` does.\r\n\r\nI believe PynamoDB should be listed in the SSL libs that only get patched on import.\r\n\r\n### Which version of dd-trace-py are you using?\r\n0.43.0\r\n\r\n### Which version of the libraries are you using?\r\n\r\nddtrace==0.43.0\r\ngevent==20.9.0\r\ngreenlet==0.4.17\r\ngunicorn==20.0.4\r\npynamodb==4.3.3\r\n\r\n### How can we reproduce your problem?\r\n\r\n1. Create new virtualenv\r\n```\r\n$ mkdir temp\r\n$ cd temp\r\n$ virtualenv .\r\n$ . ./bin/active\r\n```\r\n\r\n2. Install libs\r\n```\r\npip install ddtrace gunicorn[gevent] pynamodb\r\n```\r\n\r\n3. Create empty `app.py`\r\n```\r\nimport time\r\nwhile True:\r\n    time.sleep(1)\r\n```\r\n\r\nRun the failing command\r\n```\r\n ddtrace-run gunicorn -k gevent app\r\n```\r\n\r\nThe following warning is displayed, which will turn into a SSL recursion error if you try and use urllib3.\r\n\r\n```\r\n$ ddtrace-run gunicorn -k gevent app\r\n- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused\r\n- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused\r\n[2020-10-12 16:46:09 +1100] [69996] [INFO] Starting gunicorn 20.0.4\r\n[2020-10-12 16:46:09 +1100] [69996] [INFO] Listening at: http://127.0.0.1:8000 (69996)\r\n[2020-10-12 16:46:09 +1100] [69996] [INFO] Using worker: gevent\r\n[2020-10-12 16:46:09 +1100] [70004] [INFO] Booting worker with pid: 70004\r\n/private/tmp/venv/lib/python3.7/site-packages/gunicorn/workers/ggevent.py:53: MonkeyPatchWarning: Monkey-patching ssl after ssl has already been imported may lead to errors, including RecursionError on Python 3.6. It may also silently lead to incorrect behaviour on Python 3.7. Please monkey-patch earlier. See https://github.com/gevent/gevent/issues/1016. Modules that had direct imports (NOT patched): ['botocore.httpsession (/private/tmp/venv/lib/python3.7/site-packages/botocore/httpsession.py)', 'urllib3.util.ssl_ (/private/tmp/venv/lib/python3.7/site-packages/urllib3/util/ssl_.py)', 'urllib3.util (/private/tmp/venv/lib/python3.7/site-packages/urllib3/util/__init__.py)'].\r\n  monkey.patch_all()\r\n```\r\n\r\nDisable pynamodb tracing to fix\r\n```\r\nDD_TRACE_PYNAMODB_ENABLED=False ddtrace-run gunicorn -k gevent app\r\n```\r\n\r\nWhich gives the following output\r\n```\r\n$ DD_TRACE_PYNAMODB_ENABLED=False ddtrace-run gunicorn -k gevent app\r\n- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused\r\n- DATADOG TRACER DIAGNOSTIC - Agent not reachable. Exception raised: [Errno 61] Connection refused\r\n[2020-10-12 16:48:11 +1100] [70038] [INFO] Starting gunicorn 20.0.4\r\n[2020-10-12 16:48:11 +1100] [70038] [INFO] Listening at: http://127.0.0.1:8000 (70038)\r\n[2020-10-12 16:48:11 +1100] [70038] [INFO] Using worker: gevent\r\n[2020-10-12 16:48:11 +1100] [70046] [INFO] Booting worker with pid: 70046\r\n```\r\n\n", "before_files": [{"content": "\"\"\"Patch libraries to be automatically instrumented.\n\nIt can monkey patch supported standard libraries and third party modules.\nA patched module will automatically report spans with its default configuration.\n\nA library instrumentation can be configured (for instance, to report as another service)\nusing Pin. For that, check its documentation.\n\"\"\"\nimport importlib\nimport os\nimport sys\nimport threading\n\nfrom ddtrace.vendor.wrapt.importer import when_imported\n\nfrom .internal.logger import get_logger\nfrom .settings import config\nfrom .utils import formats\n\n\nlog = get_logger(__name__)\n\n# Default set of modules to automatically patch or not\nPATCH_MODULES = {\n    \"asyncio\": True,\n    \"boto\": True,\n    \"botocore\": True,\n    \"bottle\": False,\n    \"cassandra\": True,\n    \"celery\": True,\n    \"consul\": True,\n    \"django\": True,\n    \"elasticsearch\": True,\n    \"algoliasearch\": True,\n    \"futures\": False,  # experimental propagation\n    \"grpc\": True,\n    \"mongoengine\": True,\n    \"mysql\": True,\n    \"mysqldb\": True,\n    \"pymysql\": True,\n    \"psycopg\": True,\n    \"pylibmc\": True,\n    \"pymemcache\": True,\n    \"pymongo\": True,\n    \"redis\": True,\n    \"rediscluster\": True,\n    \"requests\": True,\n    \"sanic\": True,\n    \"sqlalchemy\": False,  # Prefer DB client instrumentation\n    \"sqlite3\": True,\n    \"aiohttp\": True,  # requires asyncio (Python 3.4+)\n    \"aiopg\": True,\n    \"aiobotocore\": False,\n    \"httplib\": False,\n    \"vertica\": True,\n    \"molten\": True,\n    \"jinja2\": True,\n    \"mako\": True,\n    \"flask\": True,\n    \"kombu\": False,\n    \"starlette\": True,\n    # Ignore some web framework integrations that might be configured explicitly in code\n    \"falcon\": False,\n    \"pylons\": False,\n    \"pyramid\": False,\n    # Auto-enable logging if the environment variable DD_LOGS_INJECTION is true\n    \"logging\": config.logs_injection,\n    \"pynamodb\": True,\n}\n\n_LOCK = threading.Lock()\n_PATCHED_MODULES = set()\n\n# Modules which are patched on first use\n# DEV: These modules are patched when the user first imports them, rather than\n#      explicitly importing and patching them on application startup `ddtrace.patch_all(module=True)`\n# DEV: This ensures we do not patch a module until it is needed\n# DEV: <contrib name> => <list of module names that trigger a patch>\n_PATCH_ON_IMPORT = {\n    \"aiohttp\": (\"aiohttp\",),\n    \"aiobotocore\": (\"aiobotocore\",),\n    \"celery\": (\"celery\",),\n    \"flask\": (\"flask, \"),\n    \"gevent\": (\"gevent\",),\n    \"requests\": (\"requests\",),\n    \"botocore\": (\"botocore\",),\n    \"elasticsearch\": (\"elasticsearch\",),\n}\n\n\nclass PatchException(Exception):\n    \"\"\"Wraps regular `Exception` class when patching modules\"\"\"\n\n    pass\n\n\nclass ModuleNotFoundException(PatchException):\n    pass\n\n\ndef _on_import_factory(module, raise_errors=True):\n    \"\"\"Factory to create an import hook for the provided module name\"\"\"\n\n    def on_import(hook):\n        # Import and patch module\n        path = \"ddtrace.contrib.%s\" % module\n        imported_module = importlib.import_module(path)\n        imported_module.patch()\n\n    return on_import\n\n\ndef patch_all(**patch_modules):\n    \"\"\"Automatically patches all available modules.\n\n    In addition to ``patch_modules``, an override can be specified via an\n    environment variable, ``DD_TRACE_<module>_ENABLED`` for each module.\n\n    ``patch_modules`` have the highest precedence for overriding.\n\n    :param dict patch_modules: Override whether particular modules are patched or not.\n\n        >>> patch_all(redis=False, cassandra=False)\n    \"\"\"\n    modules = PATCH_MODULES.copy()\n\n    # The enabled setting can be overridden by environment variables\n    for module, enabled in modules.items():\n        env_var = \"DD_TRACE_%s_ENABLED\" % module.upper()\n        if env_var not in os.environ:\n            continue\n\n        override_enabled = formats.asbool(os.environ[env_var])\n        modules[module] = override_enabled\n\n    # Arguments take precedence over the environment and the defaults.\n    modules.update(patch_modules)\n\n    patch(raise_errors=False, **modules)\n\n\ndef patch(raise_errors=True, **patch_modules):\n    \"\"\"Patch only a set of given modules.\n\n    :param bool raise_errors: Raise error if one patch fail.\n    :param dict patch_modules: List of modules to patch.\n\n        >>> patch(psycopg=True, elasticsearch=True)\n    \"\"\"\n    modules = [m for (m, should_patch) in patch_modules.items() if should_patch]\n    for module in modules:\n        if module in _PATCH_ON_IMPORT:\n            # If the module has already been imported then patch immediately\n            if module in sys.modules:\n                patch_module(module, raise_errors=raise_errors)\n\n            # Otherwise, add a hook to patch when it is imported for the first time\n            else:\n                # Use factory to create handler to close over `module` and `raise_errors` values from this loop\n                when_imported(module)(_on_import_factory(module, raise_errors))\n\n                # manually add module to patched modules\n                with _LOCK:\n                    _PATCHED_MODULES.add(module)\n        else:\n            patch_module(module, raise_errors=raise_errors)\n\n    patched_modules = get_patched_modules()\n    log.info(\n        \"patched %s/%s modules (%s)\",\n        len(patched_modules),\n        len(modules),\n        \",\".join(patched_modules),\n    )\n\n\ndef patch_module(module, raise_errors=True):\n    \"\"\"Patch a single module\n\n    Returns if the module got properly patched.\n    \"\"\"\n    try:\n        return _patch_module(module)\n    except ModuleNotFoundException:\n        if raise_errors:\n            raise\n        return False\n    except Exception:\n        if raise_errors:\n            raise\n        log.debug(\"failed to patch %s\", module, exc_info=True)\n        return False\n\n\ndef get_patched_modules():\n    \"\"\"Get the list of patched modules\"\"\"\n    with _LOCK:\n        return sorted(_PATCHED_MODULES)\n\n\ndef _patch_module(module):\n    \"\"\"_patch_module will attempt to monkey patch the module.\n\n    Returns if the module got patched.\n    Can also raise errors if it fails.\n    \"\"\"\n    path = \"ddtrace.contrib.%s\" % module\n    with _LOCK:\n        if module in _PATCHED_MODULES and module not in _PATCH_ON_IMPORT:\n            log.debug(\"already patched: %s\", path)\n            return False\n\n        try:\n            imported_module = importlib.import_module(path)\n        except ImportError:\n            # if the import fails, the integration is not available\n            raise PatchException(\"integration '%s' not available\" % path)\n        else:\n            # if patch() is not available in the module, it means\n            # that the library is not installed in the environment\n            if not hasattr(imported_module, \"patch\"):\n                raise ModuleNotFoundException(\"module '%s' not installed\" % module)\n\n            imported_module.patch()\n            _PATCHED_MODULES.add(module)\n            return True\n", "path": "ddtrace/monkey.py"}], "after_files": [{"content": "\"\"\"Patch libraries to be automatically instrumented.\n\nIt can monkey patch supported standard libraries and third party modules.\nA patched module will automatically report spans with its default configuration.\n\nA library instrumentation can be configured (for instance, to report as another service)\nusing Pin. For that, check its documentation.\n\"\"\"\nimport importlib\nimport os\nimport sys\nimport threading\n\nfrom ddtrace.vendor.wrapt.importer import when_imported\n\nfrom .internal.logger import get_logger\nfrom .settings import config\nfrom .utils import formats\n\n\nlog = get_logger(__name__)\n\n# Default set of modules to automatically patch or not\nPATCH_MODULES = {\n    \"asyncio\": True,\n    \"boto\": True,\n    \"botocore\": True,\n    \"bottle\": False,\n    \"cassandra\": True,\n    \"celery\": True,\n    \"consul\": True,\n    \"django\": True,\n    \"elasticsearch\": True,\n    \"algoliasearch\": True,\n    \"futures\": False,  # experimental propagation\n    \"grpc\": True,\n    \"mongoengine\": True,\n    \"mysql\": True,\n    \"mysqldb\": True,\n    \"pymysql\": True,\n    \"psycopg\": True,\n    \"pylibmc\": True,\n    \"pymemcache\": True,\n    \"pymongo\": True,\n    \"redis\": True,\n    \"rediscluster\": True,\n    \"requests\": True,\n    \"sanic\": True,\n    \"sqlalchemy\": False,  # Prefer DB client instrumentation\n    \"sqlite3\": True,\n    \"aiohttp\": True,  # requires asyncio (Python 3.4+)\n    \"aiopg\": True,\n    \"aiobotocore\": False,\n    \"httplib\": False,\n    \"vertica\": True,\n    \"molten\": True,\n    \"jinja2\": True,\n    \"mako\": True,\n    \"flask\": True,\n    \"kombu\": False,\n    \"starlette\": True,\n    # Ignore some web framework integrations that might be configured explicitly in code\n    \"falcon\": False,\n    \"pylons\": False,\n    \"pyramid\": False,\n    # Auto-enable logging if the environment variable DD_LOGS_INJECTION is true\n    \"logging\": config.logs_injection,\n    \"pynamodb\": True,\n}\n\n_LOCK = threading.Lock()\n_PATCHED_MODULES = set()\n\n# Modules which are patched on first use\n# DEV: These modules are patched when the user first imports them, rather than\n#      explicitly importing and patching them on application startup `ddtrace.patch_all(module=True)`\n# DEV: This ensures we do not patch a module until it is needed\n# DEV: <contrib name> => <list of module names that trigger a patch>\n_PATCH_ON_IMPORT = {\n    \"aiohttp\": (\"aiohttp\",),\n    \"aiobotocore\": (\"aiobotocore\",),\n    \"celery\": (\"celery\",),\n    \"flask\": (\"flask, \"),\n    \"gevent\": (\"gevent\",),\n    \"requests\": (\"requests\",),\n    \"botocore\": (\"botocore\",),\n    \"elasticsearch\": (\"elasticsearch\",),\n    \"pynamodb\": (\"pynamodb\",),\n}\n\n\nclass PatchException(Exception):\n    \"\"\"Wraps regular `Exception` class when patching modules\"\"\"\n\n    pass\n\n\nclass ModuleNotFoundException(PatchException):\n    pass\n\n\ndef _on_import_factory(module, raise_errors=True):\n    \"\"\"Factory to create an import hook for the provided module name\"\"\"\n\n    def on_import(hook):\n        # Import and patch module\n        path = \"ddtrace.contrib.%s\" % module\n        imported_module = importlib.import_module(path)\n        imported_module.patch()\n\n    return on_import\n\n\ndef patch_all(**patch_modules):\n    \"\"\"Automatically patches all available modules.\n\n    In addition to ``patch_modules``, an override can be specified via an\n    environment variable, ``DD_TRACE_<module>_ENABLED`` for each module.\n\n    ``patch_modules`` have the highest precedence for overriding.\n\n    :param dict patch_modules: Override whether particular modules are patched or not.\n\n        >>> patch_all(redis=False, cassandra=False)\n    \"\"\"\n    modules = PATCH_MODULES.copy()\n\n    # The enabled setting can be overridden by environment variables\n    for module, enabled in modules.items():\n        env_var = \"DD_TRACE_%s_ENABLED\" % module.upper()\n        if env_var not in os.environ:\n            continue\n\n        override_enabled = formats.asbool(os.environ[env_var])\n        modules[module] = override_enabled\n\n    # Arguments take precedence over the environment and the defaults.\n    modules.update(patch_modules)\n\n    patch(raise_errors=False, **modules)\n\n\ndef patch(raise_errors=True, **patch_modules):\n    \"\"\"Patch only a set of given modules.\n\n    :param bool raise_errors: Raise error if one patch fail.\n    :param dict patch_modules: List of modules to patch.\n\n        >>> patch(psycopg=True, elasticsearch=True)\n    \"\"\"\n    modules = [m for (m, should_patch) in patch_modules.items() if should_patch]\n    for module in modules:\n        if module in _PATCH_ON_IMPORT:\n            # If the module has already been imported then patch immediately\n            if module in sys.modules:\n                patch_module(module, raise_errors=raise_errors)\n\n            # Otherwise, add a hook to patch when it is imported for the first time\n            else:\n                # Use factory to create handler to close over `module` and `raise_errors` values from this loop\n                when_imported(module)(_on_import_factory(module, raise_errors))\n\n                # manually add module to patched modules\n                with _LOCK:\n                    _PATCHED_MODULES.add(module)\n        else:\n            patch_module(module, raise_errors=raise_errors)\n\n    patched_modules = get_patched_modules()\n    log.info(\n        \"patched %s/%s modules (%s)\",\n        len(patched_modules),\n        len(modules),\n        \",\".join(patched_modules),\n    )\n\n\ndef patch_module(module, raise_errors=True):\n    \"\"\"Patch a single module\n\n    Returns if the module got properly patched.\n    \"\"\"\n    try:\n        return _patch_module(module)\n    except ModuleNotFoundException:\n        if raise_errors:\n            raise\n        return False\n    except Exception:\n        if raise_errors:\n            raise\n        log.debug(\"failed to patch %s\", module, exc_info=True)\n        return False\n\n\ndef get_patched_modules():\n    \"\"\"Get the list of patched modules\"\"\"\n    with _LOCK:\n        return sorted(_PATCHED_MODULES)\n\n\ndef _patch_module(module):\n    \"\"\"_patch_module will attempt to monkey patch the module.\n\n    Returns if the module got patched.\n    Can also raise errors if it fails.\n    \"\"\"\n    path = \"ddtrace.contrib.%s\" % module\n    with _LOCK:\n        if module in _PATCHED_MODULES and module not in _PATCH_ON_IMPORT:\n            log.debug(\"already patched: %s\", path)\n            return False\n\n        try:\n            imported_module = importlib.import_module(path)\n        except ImportError:\n            # if the import fails, the integration is not available\n            raise PatchException(\"integration '%s' not available\" % path)\n        else:\n            # if patch() is not available in the module, it means\n            # that the library is not installed in the environment\n            if not hasattr(imported_module, \"patch\"):\n                raise ModuleNotFoundException(\"module '%s' not installed\" % module)\n\n            imported_module.patch()\n            _PATCHED_MODULES.add(module)\n            return True\n", "path": "ddtrace/monkey.py"}]}