Repository: DataDog/dd-trace-py

Issue:
pymongo trace exception
We're noticing this exception - using latest `pymongo==3.4.0` driver:

```exceptions.AttributeError: 'long' object has no attribute 'items'
Traceback (most recent call last):
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 105, in send_message_with_response
    span.resource = _resource_from_cmd(cmd)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 220, in _resource_from_cmd
    nq = normalize_filter(cmd.query)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 207, in normalize_filter
    out[k] = normalize_filter(v)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 207, in normalize_filter
    out[k] = normalize_filter(v)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 199, in normalize_filter
    return [normalize_filter(s) for s in f]
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 204, in normalize_filter
    for k, v in iteritems(f):
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/compat.py", line 32, in iteritems
    func = obj.items
AttributeError: 'long' object has no attribute 'items'
```
pymongo trace exception
We're noticing this exception - using latest `pymongo==3.4.0` driver:

```exceptions.AttributeError: 'long' object has no attribute 'items'
Traceback (most recent call last):
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 105, in send_message_with_response
    span.resource = _resource_from_cmd(cmd)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 220, in _resource_from_cmd
    nq = normalize_filter(cmd.query)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 207, in normalize_filter
    out[k] = normalize_filter(v)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 207, in normalize_filter
    out[k] = normalize_filter(v)
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 199, in normalize_filter
    return [normalize_filter(s) for s in f]
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/contrib/pymongo/client.py", line 204, in normalize_filter
    for k, v in iteritems(f):
  File "/home/deploy/virtualenvs/discord/local/lib/python2.7/site-packages/ddtrace/compat.py", line 32, in iteritems
    func = obj.items
AttributeError: 'long' object has no attribute 'items'
```


Discussion:

Resolving patch:
diff --git a/ddtrace/contrib/pymongo/client.py b/ddtrace/contrib/pymongo/client.py
--- a/ddtrace/contrib/pymongo/client.py
+++ b/ddtrace/contrib/pymongo/client.py
@@ -197,17 +197,25 @@ def normalize_filter(f=None):
         # normalize lists of filters
         # e.g. {$or: [ { age: { $lt: 30 } }, { type: 1 } ]}
         return [normalize_filter(s) for s in f]
-    else:
+    elif isinstance(f, dict):
         # normalize dicts of filters
-        # e.g. {$or: [ { age: { $lt: 30 } }, { type: 1 } ]})
+        #   {$or: [ { age: { $lt: 30 } }, { type: 1 } ]})
         out = {}
         for k, v in iteritems(f):
-            if isinstance(v, list) or isinstance(v, dict):
+            if k == "$in" or k == "$nin":
+                # special case $in queries so we don't loop over lists.
+                out[k] = "?"
+            elif isinstance(v, list) or isinstance(v, dict):
                 # RECURSION ALERT: needs to move to the agent
                 out[k] = normalize_filter(v)
             else:
+                # NOTE: this shouldn't happen, but let's have a safeguard.
                 out[k] = '?'
         return out
+    else:
+        # FIXME[matt] unexpected type. not sure this should ever happen, but at
+        # least it won't crash.
+        return {}
 
 def _set_address_tags(span, address):
     # the address is only set after the cursor is done.


Test patch:
diff --git a/tests/contrib/pymongo/test.py b/tests/contrib/pymongo/test.py
--- a/tests/contrib/pymongo/test.py
+++ b/tests/contrib/pymongo/test.py
@@ -28,6 +28,23 @@ def test_normalize_filter():
             {"age": {"$gt" : 20}},
             {"age": {"$gt" : "?"}},
         ),
+        (
+            {"age": {"$gt" : 20}},
+            {"age": {"$gt" : "?"}},
+        ),
+        (
+            {"_id": {"$in" : [1, 2, 3]}},
+            {"_id": {"$in" : "?"}},
+        ),
+        (
+            {"_id": {"$nin" : [1, 2, 3]}},
+            {"_id": {"$nin" : "?"}},
+        ),
+
+        (
+            20,
+            {},
+        ),
         (
             {
                 "status": "A",