Repository: scoutapp/scout_apm_python
Pull request #693 (2021-11-01 17:32:28)

[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.9b0 → 21.10b0](https://github.com/psf/black/compare/21.9b0...21.10b0)



Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 747e315..5669776 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,7 +13,7 @@ repos:
   - id: end-of-file-fixer
   - id: trailing-whitespace
 - repo: https://github.com/psf/black
-  rev: 21.9b0
+  rev: 21.10b0
   hooks:
   - id: black
     language_version: python3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df6b517..c8fb63b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@
 - Added support for Django 4.0 and Python 3.10.
 
 ### Fixed
+- Make `module_filepath` more robust avoiding throwing exceptions.
+  ([Issue 691](https://github.com/scoutapp/scout_apm_python/issues/691))
 
 ## [2.23.2] 2021-10-04
 
diff --git a/src/scout_apm/core/backtrace.py b/src/scout_apm/core/backtrace.py
index b36bead..7ccf797 100644
--- a/src/scout_apm/core/backtrace.py
+++ b/src/scout_apm/core/backtrace.py
@@ -32,22 +32,27 @@ def module_filepath(module, filepath):
     if root_module_name == module:
         return os.path.basename(filepath)
 
+    module_dir = None
     root_module = sys.modules[root_module_name]
-    if root_module.__file__:
-        module_dir = root_module.__file__.rsplit(os.sep, 2)[0]
-    elif root_module.__path__:
-        # Default to using the first path specified for the module.
-        module_dir = root_module.__path__[0].rsplit(os.sep, 1)[0]
-        if len(root_module.__path__) > 1:
-            logger.debug(
-                "{} has {} paths. Use the first and ignore the rest.".format(
-                    root_module, len(root_module.__path__)
+    try:
+        if root_module.__file__:
+            module_dir = root_module.__file__.rsplit(os.sep, 2)[0]
+        elif root_module.__path__ and isinstance(root_module.__path__, (list, tuple)):
+            # Default to using the first path specified for the module.
+            module_dir = root_module.__path__[0].rsplit(os.sep, 1)[0]
+            if len(root_module.__path__) > 1:
+                logger.debug(
+                    "{} has {} paths. Use the first and ignore the rest.".format(
+                        root_module, len(root_module.__path__)
+                    )
                 )
-            )
-    else:
-        # If the file path don't exist, then return the full path.
-        return filepath
-    return filepath.split(module_dir, 1)[-1].lstrip(os.sep)
+    except Exception as exc:
+        logger.debug(
+            "Error processing module {} and filepath {}".format(root_module, filepath),
+            exc_info=exc,
+        )
+
+    return filepath.split(module_dir, 1)[-1].lstrip(os.sep) if module_dir else filepath
 
 
 def filepath(frame):


Test patch:
diff --git a/tests/unit/core/test_backtrace.py b/tests/unit/core/test_backtrace.py
index 682a0d0..85fe87f 100644
--- a/tests/unit/core/test_backtrace.py
+++ b/tests/unit/core/test_backtrace.py
@@ -107,18 +107,27 @@ def test_module_filepath_with_namespace(test_package_as_namespace_package):
     )
 
 
-@pytest.fixture
-def test_package_no_file_or_path():
+@pytest.fixture(
+    params=[
+        # Invalid path case
+        [None, "invalid"],
+        # Raise an error case
+        [None, [None]],
+        # No file or path case.
+        [None, None],
+    ]
+)
+def error_module(request):
     orig_file = sys.modules["tests"].__file__
     orig_path = sys.modules["tests"].__path__
-    sys.modules["tests"].__file__ = None
-    sys.modules["tests"].__path__ = None
+    sys.modules["tests"].__file__ = request.param[0]
+    sys.modules["tests"].__path__ = request.param[1]
     yield
     sys.modules["tests"].__file__ = orig_file
     sys.modules["tests"].__path__ = orig_path
 
 
-def test_module_filepath_without_file_or_path(test_package_no_file_or_path):
+def test_module_filepath_error_flows(error_module):
     frame = get_tb().tb_frame
     module = frame.f_globals["__name__"]
     filepath = frame.f_code.co_filename