|
20 | 20 | import os |
21 | 21 | import platform |
22 | 22 | import sys |
| 23 | +import sysconfig |
23 | 24 |
|
24 | 25 | import newrelic |
25 | 26 | from newrelic.common.system_info import ( |
@@ -178,41 +179,50 @@ def environment_settings(): |
178 | 179 | env.extend(dispatcher) |
179 | 180 |
|
180 | 181 | # Module information. |
| 182 | + purelib = sysconfig.get_path("purelib") |
| 183 | + platlib = sysconfig.get_path("platlib") |
181 | 184 |
|
182 | 185 | plugins = [] |
183 | 186 |
|
| 187 | + get_version = None |
| 188 | + # importlib was introduced into the standard library starting in Python3.8. |
| 189 | + if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"): |
| 190 | + get_version = sys.modules["importlib"].metadata.version |
| 191 | + elif "pkg_resources" in sys.modules: |
| 192 | + |
| 193 | + def get_version(name): # pylint: disable=function-redefined |
| 194 | + return sys.modules["pkg_resources"].get_distribution(name).version |
| 195 | + |
184 | 196 | # Using any iterable to create a snapshot of sys.modules can occassionally |
185 | 197 | # fail in a rare case when modules are imported in parallel by different |
186 | 198 | # threads. |
187 | 199 | # |
188 | 200 | # TL;DR: Do NOT use an iterable on the original sys.modules to generate the |
189 | 201 | # list |
190 | | - |
191 | 202 | for name, module in sys.modules.copy().items(): |
| 203 | + # Exclude lib.sub_paths as independent modules except for newrelic.hooks. |
| 204 | + if "." in name and not name.startswith("newrelic.hooks."): |
| 205 | + continue |
192 | 206 | # If the module isn't actually loaded (such as failed relative imports |
193 | 207 | # in Python 2.7), the module will be None and should not be reported. |
194 | | - if module is None: |
| 208 | + if not module: |
| 209 | + continue |
| 210 | + # Exclude standard library/built-in modules. |
| 211 | + # Third-party modules can be installed in either purelib or platlib directories. |
| 212 | + # See https://docs.python.org/3/library/sysconfig.html#installation-paths. |
| 213 | + if ( |
| 214 | + not hasattr(module, "__file__") |
| 215 | + or not module.__file__ |
| 216 | + or not module.__file__.startswith(purelib) |
| 217 | + or not module.__file__.startswith(platlib) |
| 218 | + ): |
195 | 219 | continue |
196 | 220 |
|
197 | | - if name.startswith("newrelic.hooks."): |
198 | | - plugins.append(name) |
199 | | - |
200 | | - elif name.find(".") == -1 and hasattr(module, "__file__"): |
201 | | - # XXX This is disabled as it can cause notable overhead in |
202 | | - # pathalogical cases. Will be replaced with a new system |
203 | | - # where have a allowlist of packages we really want version |
204 | | - # information for and will work out on case by case basis |
205 | | - # how to extract that from the modules themselves. |
206 | | - |
207 | | - # try: |
208 | | - # if 'pkg_resources' in sys.modules: |
209 | | - # version = pkg_resources.get_distribution(name).version |
210 | | - # if version: |
211 | | - # name = '%s (%s)' % (name, version) |
212 | | - # except Exception: |
213 | | - # pass |
214 | | - |
215 | | - plugins.append(name) |
| 221 | + try: |
| 222 | + version = get_version(name) |
| 223 | + plugins.append("%s (%s)" % (name, version)) |
| 224 | + except Exception: |
| 225 | + pass |
216 | 226 |
|
217 | 227 | env.append(("Plugin List", plugins)) |
218 | 228 |
|
|
0 commit comments