Show More
@@ -71,7 +71,7 b' from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol' | |||
|
71 | 71 | from IPython.utils import generics |
|
72 | 72 | from IPython.utils import io |
|
73 | 73 | from IPython.utils.decorators import undoc |
|
74 | from IPython.utils.dir2 import dir2 | |
|
74 | from IPython.utils.dir2 import dir2, safe_hasattr | |
|
75 | 75 | from IPython.utils.process import arg_split |
|
76 | 76 | from IPython.utils.py3compat import builtin_mod, string_types, PY3 |
|
77 | 77 | from traitlets import CBool, Enum |
@@ -472,6 +472,18 b' def _safe_isinstance(obj, module, class_name):' | |||
|
472 | 472 | return (module in sys.modules and |
|
473 | 473 | isinstance(obj, getattr(__import__(module), class_name))) |
|
474 | 474 | |
|
475 | def _safe_really_hasattr(obj, name): | |
|
476 | """Checks that an object genuinely has a given attribute. | |
|
477 | ||
|
478 | Some objects claim to have any attribute that's requested, to act as a lazy | |
|
479 | proxy for something else. We want to catch these cases and ignore their | |
|
480 | claim to have the attribute we're interested in. | |
|
481 | """ | |
|
482 | if safe_hasattr(obj, '_ipy_proxy_check_dont_define_this_'): | |
|
483 | # If it claims this exists, don't trust it | |
|
484 | return False | |
|
485 | ||
|
486 | return safe_hasattr(obj, name) | |
|
475 | 487 | |
|
476 | 488 | |
|
477 | 489 | def back_unicode_name_matches(text): |
@@ -923,7 +935,12 b' class IPCompleter(Completer):' | |||
|
923 | 935 | def dict_key_matches(self, text): |
|
924 | 936 | "Match string keys in a dictionary, after e.g. 'foo[' " |
|
925 | 937 | def get_keys(obj): |
|
926 | # Only allow completion for known in-memory dict-like types | |
|
938 | # Objects can define their own completions by defining an | |
|
939 | # _ipy_key_completions_() method. | |
|
940 | if _safe_really_hasattr(obj, '_ipy_key_completions_'): | |
|
941 | return obj._ipy_key_completions_() | |
|
942 | ||
|
943 | # Special case some common in-memory dict-like types | |
|
927 | 944 | if isinstance(obj, dict) or\ |
|
928 | 945 | _safe_isinstance(obj, 'pandas', 'DataFrame'): |
|
929 | 946 | try: |
@@ -761,6 +761,22 b' def test_dict_key_completion_invalids():' | |||
|
761 | 761 | _, matches = complete(line_buffer="name_error['") |
|
762 | 762 | _, matches = complete(line_buffer="d['\\") # incomplete escape |
|
763 | 763 | |
|
764 | class KeyCompletable(object): | |
|
765 | def __init__(self, things=()): | |
|
766 | self.things = things | |
|
767 | ||
|
768 | def _ipy_key_completions_(self): | |
|
769 | return list(self.things) | |
|
770 | ||
|
771 | def test_object_key_completion(): | |
|
772 | ip = get_ipython() | |
|
773 | ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick']) | |
|
774 | ||
|
775 | _, matches = ip.Completer.complete(line_buffer="key_completable['qw") | |
|
776 | nt.assert_in('qwerty', matches) | |
|
777 | nt.assert_in('qwick', matches) | |
|
778 | ||
|
779 | ||
|
764 | 780 | def test_aimport_module_completer(): |
|
765 | 781 | ip = get_ipython() |
|
766 | 782 | _, matches = ip.complete('i', '%aimport i') |
General Comments 0
You need to be logged in to leave comments.
Login now