diff --git a/IPython/core/completer.py b/IPython/core/completer.py index 6ce32a7..86ba87e 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -71,7 +71,7 @@ from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol from IPython.utils import generics from IPython.utils import io from IPython.utils.decorators import undoc -from IPython.utils.dir2 import dir2, safe_hasattr +from IPython.utils.dir2 import dir2, get_real_method from IPython.utils.process import arg_split from IPython.utils.py3compat import builtin_mod, string_types, PY3 from traitlets import CBool, Enum @@ -472,19 +472,6 @@ def _safe_isinstance(obj, module, class_name): return (module in sys.modules and isinstance(obj, getattr(__import__(module), class_name))) -def _safe_really_hasattr(obj, name): - """Checks that an object genuinely has a given attribute. - - Some objects claim to have any attribute that's requested, to act as a lazy - proxy for something else. We want to catch these cases and ignore their - claim to have the attribute we're interested in. - """ - if safe_hasattr(obj, '_ipy_proxy_check_dont_define_this_'): - # If it claims this exists, don't trust it - return False - - return safe_hasattr(obj, name) - def back_unicode_name_matches(text): u"""Match unicode characters back to unicode name @@ -937,8 +924,9 @@ class IPCompleter(Completer): def get_keys(obj): # Objects can define their own completions by defining an # _ipy_key_completions_() method. - if _safe_really_hasattr(obj, '_ipython_key_completions_'): - return obj._ipython_key_completions_() + method = get_real_method(obj, '_ipython_key_completions_') + if method is not None: + return method() # Special case some common in-memory dict-like types if isinstance(obj, dict) or\ diff --git a/IPython/core/displayhook.py b/IPython/core/displayhook.py index 1e721f9..4d17408 100644 --- a/IPython/core/displayhook.py +++ b/IPython/core/displayhook.py @@ -13,7 +13,6 @@ import sys import io as _io import tokenize -from IPython.core.formatters import _safe_get_formatter_method from traitlets.config.configurable import Configurable from IPython.utils import io from IPython.utils.py3compat import builtin_mod, cast_unicode_py2 diff --git a/IPython/core/formatters.py b/IPython/core/formatters.py index aa05679..700a903 100644 --- a/IPython/core/formatters.py +++ b/IPython/core/formatters.py @@ -22,6 +22,7 @@ from decorator import decorator from traitlets.config.configurable import Configurable from IPython.core.getipython import get_ipython from IPython.utils.sentinel import Sentinel +from IPython.utils.dir2 import get_real_method from IPython.lib import pretty from traitlets import ( Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, @@ -32,29 +33,6 @@ from IPython.utils.py3compat import ( ) -#----------------------------------------------------------------------------- -# The main DisplayFormatter class -#----------------------------------------------------------------------------- - - -def _safe_get_formatter_method(obj, name): - """Safely get a formatter method - - - Classes cannot have formatter methods, only instance - - protect against proxy objects that claim to have everything - """ - if inspect.isclass(obj): - # repr methods only make sense on instances, not classes - return None - method = pretty._safe_getattr(obj, name, None) - if callable(method): - # obj claims to have repr method... - if callable(pretty._safe_getattr(obj, '_ipython_canary_method_should_not_exist_', None)): - # ...but don't trust proxy objects that claim to have everything - return None - return method - - class DisplayFormatter(Configurable): # When set to true only the default plain text formatter will be used. @@ -338,7 +316,7 @@ class BaseFormatter(Configurable): else: return printer(obj) # Finally look for special method names - method = _safe_get_formatter_method(obj, self.print_method) + method = get_real_method(obj, self.print_method) if method is not None: return method() return None @@ -904,7 +882,7 @@ class IPythonDisplayFormatter(BaseFormatter): printer(obj) return True # Finally look for special method names - method = _safe_get_formatter_method(obj, self.print_method) + method = get_real_method(obj, self.print_method) if method is not None: method() return True diff --git a/IPython/utils/dir2.py b/IPython/utils/dir2.py index 50c660a..f6f164f 100644 --- a/IPython/utils/dir2.py +++ b/IPython/utils/dir2.py @@ -2,21 +2,11 @@ """A fancy version of Python's builtin :func:`dir` function. """ -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -from .py3compat import string_types +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- +import inspect +from .py3compat import string_types def safe_hasattr(obj, attr): @@ -56,3 +46,36 @@ def dir2(obj): words = [w for w in words if isinstance(w, string_types)] return sorted(words) + + +def get_real_method(obj, name): + """Like getattr, but with a few extra sanity checks: + + - If obj is a class, ignore its methods + - Check if obj is a proxy that claims to have all attributes + - Catch attribute access failing with any exception + - Check that the attribute is a callable object + + Returns the method or None. + """ + if inspect.isclass(obj): + return None + + try: + canary = getattr(obj, '_ipython_canary_method_should_not_exist_', None) + except Exception: + return None + + if canary is not None: + # It claimed to have an attribute it should never have + return None + + try: + m = getattr(obj, name, None) + except Exception: + return None + + if callable(m): + return m + + return None