Repository: ipython/ipython

Issue:
display_method of objects with custom __getattr__
Some objects such as pymongo collections implement a custom  `__getattr__` method that always  retursn an object.

Then for the IPython display there is https://github.com/ipython/ipython/blob/master/IPython/core/displayhook.py#L245 which will call the `_ipython_display_` attribute returned by `__getattr__` and produce unpredictable behaviour; best case just an error, like:

```
TypeError: 'Collection' object is not callable. If you meant to call the '_ipython_display_'
 method on a 'Collection' object it is failing because no such method exists.
```

Probably can be considered a bug of pymongo, but may be worth considering to check for existence in `__dict__` rather than getattr.



Discussion:

Resolving patch:
diff --git a/IPython/core/display.py b/IPython/core/display.py
--- a/IPython/core/display.py
+++ b/IPython/core/display.py
@@ -22,6 +22,7 @@
 import os
 import struct
 
+from IPython.core.formatters import _safe_get_formatter_method
 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
                                      unicode_type)
 from IPython.testing.skipdoctest import skip_doctest
@@ -116,7 +117,7 @@ def display(*objs, **kwargs):
     for obj in objs:
 
         # If _ipython_display_ is defined, use that to display this object.
-        display_method = getattr(obj, '_ipython_display_', None)
+        display_method = _safe_get_formatter_method(obj, '_ipython_display_')
         if display_method is not None:
             try:
                 display_method(**kwargs)
diff --git a/IPython/core/displayhook.py b/IPython/core/displayhook.py
--- a/IPython/core/displayhook.py
+++ b/IPython/core/displayhook.py
@@ -25,7 +25,7 @@
 
 import sys
 
-
+from IPython.core.formatters import _safe_get_formatter_method
 from IPython.config.configurable import Configurable
 from IPython.utils import io
 from IPython.utils.py3compat import builtin_mod
@@ -242,7 +242,7 @@ def __call__(self, result=None):
         self.check_for_underscore()
         if result is not None and not self.quiet():
             # If _ipython_display_ is defined, use that to display this object.
-            display_method = getattr(result, '_ipython_display_', None)
+            display_method = _safe_get_formatter_method(result, '_ipython_display_')
             if display_method is not None:
                 try:
                     return display_method()
diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py
--- a/IPython/core/formatters.py
+++ b/IPython/core/formatters.py
@@ -64,7 +64,9 @@ def _valid_formatter(f):
     - unbound methods           NO
     - callable with zero args   OK
     """
-    if isinstance(f, type(str.find)):
+    if f is None:
+        return False
+    elif isinstance(f, type(str.find)):
         # unbound methods on compiled classes have type method_descriptor
         return False
     elif isinstance(f, types.BuiltinFunctionType):
@@ -80,6 +82,14 @@ def _valid_formatter(f):
             return True
     return False
 
+def _safe_get_formatter_method(obj, name):
+    """Safely get a formatter method"""
+    method = pretty._safe_getattr(obj, name, None)
+    # formatter methods must be bound
+    if _valid_formatter(method):
+        return method
+
+
 class DisplayFormatter(Configurable):
 
     # When set to true only the default plain text formatter will be used.
@@ -339,9 +349,8 @@ def __call__(self, obj):
             else:
                 return printer(obj)
             # Finally look for special method names
-            method = pretty._safe_getattr(obj, self.print_method, None)
-            # print_method must be a bound method:
-            if _valid_formatter(method):
+            method = _safe_get_formatter_method(obj, self.print_method)
+            if method is not None:
                 return method()
             return None
         else:


Test patch: