Repository: scoutapp/scout_apm_python
Pull request #436 (2020-01-02 11:26:14)

Track missing PyMongo Collection methods
These seem to have been initially missed - maybe they weren't implemented in PyMongo at the time. Added a test to detect new methods and make sure we decide whether or not to instrument them.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7f761d..a175271 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@
   ([PR #430](https://github.com/scoutapp/scout_apm_python/pull/430)).
 - Add CherryPy integration
   ([PR #431](https://github.com/scoutapp/scout_apm_python/pull/431)).
+- Added tracking for missing PyMongo Collection methods
+  ([PR #436](https://github.com/scoutapp/scout_apm_python/pull/436)).
 
 ### Removed
 
diff --git a/src/scout_apm/instruments/pymongo.py b/src/scout_apm/instruments/pymongo.py
index e0063f1..0fbe9a8 100644
--- a/src/scout_apm/instruments/pymongo.py
+++ b/src/scout_apm/instruments/pymongo.py
@@ -25,7 +25,7 @@ def ensure_installed():
     if Collection is None:
         logger.info("Unable to import pymongo.Collection")
     elif not have_patched_collection:
-        for name in CLIENT_METHODS:
+        for name in COLLECTION_METHODS:
             try:
                 setattr(
                     Collection, name, wrap_collection_method(getattr(Collection, name))
@@ -40,10 +40,12 @@ def ensure_installed():
         have_patched_collection = True
 
 
-CLIENT_METHODS = [
+COLLECTION_METHODS = [
     "aggregate",
+    "aggregate_raw_batches",
     "bulk_write",
     "count",
+    "count_documents",
     "create_index",
     "create_indexes",
     "delete_many",
@@ -53,17 +55,23 @@ CLIENT_METHODS = [
     "drop_index",
     "drop_indexes",
     "ensure_index",
+    "estimated_document_count",
+    "find",
     "find_and_modify",
     "find_one",
     "find_one_and_delete",
     "find_one_and_replace",
     "find_one_and_update",
+    "find_raw_batches",
     "group",
+    "index_information",
     "inline_map_reduce",
     "insert",
     "insert_many",
     "insert_one",
+    "list_indexes",
     "map_reduce",
+    "parallel_scan",
     "reindex",
     "remove",
     "rename",


Test patch:
diff --git a/tests/integration/instruments/test_pymongo.py b/tests/integration/instruments/test_pymongo.py
index 2197fcd..2b44801 100644
--- a/tests/integration/instruments/test_pymongo.py
+++ b/tests/integration/instruments/test_pymongo.py
@@ -7,7 +7,7 @@ import os
 import pymongo
 import pytest
 
-from scout_apm.instruments.pymongo import ensure_installed
+from scout_apm.instruments.pymongo import COLLECTION_METHODS, ensure_installed
 from tests.compat import mock
 
 
@@ -20,6 +20,39 @@ def pymongo_client():
     yield pymongo.MongoClient(os.environ["MONGODB_URL"])
 
 
+def test_all_collection_attributes_accounted_for():
+    all_methods = {
+        m for m in dir(pymongo.collection.Collection) if not m.startswith("_")
+    }
+    deliberately_ignored_methods = {
+        # Properties:
+        "codec_options",
+        "database",
+        "full_name",
+        "name",
+        "read_concern",
+        "read_preference",
+        "write_concern",
+        # Non-querying methods:
+        "initialize_ordered_bulk_op",
+        "initialize_unordered_bulk_op",
+        "next",
+        "options",
+        "with_options",
+        # Returns a long running iterator rather than doing a direct query;
+        # probably won't be used in web requests:
+        "watch",
+    }
+    assert (
+        all_methods - deliberately_ignored_methods - set(COLLECTION_METHODS)
+    ) == set()
+
+
+@pytest.mark.parametrize(["method_name"], [[x] for x in COLLECTION_METHODS])
+def test_all_collection_methods_exist(method_name):
+    assert hasattr(pymongo.collection.Collection, method_name)
+
+
 def test_ensure_installed_twice(caplog):
     ensure_installed()
     ensure_installed()