Show More
@@ -71,7 +71,7 b' from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol' | |||||
71 | from IPython.utils import generics |
|
71 | from IPython.utils import generics | |
72 | from IPython.utils import io |
|
72 | from IPython.utils import io | |
73 | from IPython.utils.decorators import undoc |
|
73 | from IPython.utils.decorators import undoc | |
74 | from IPython.utils.dir2 import dir2 |
|
74 | from IPython.utils.dir2 import dir2, safe_hasattr | |
75 | from IPython.utils.process import arg_split |
|
75 | from IPython.utils.process import arg_split | |
76 | from IPython.utils.py3compat import builtin_mod, string_types, PY3 |
|
76 | from IPython.utils.py3compat import builtin_mod, string_types, PY3 | |
77 | from traitlets import CBool, Enum |
|
77 | from traitlets import CBool, Enum | |
@@ -472,6 +472,18 b' def _safe_isinstance(obj, module, class_name):' | |||||
472 | return (module in sys.modules and |
|
472 | return (module in sys.modules and | |
473 | isinstance(obj, getattr(__import__(module), class_name))) |
|
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 | def back_unicode_name_matches(text): |
|
489 | def back_unicode_name_matches(text): | |
@@ -923,7 +935,12 b' class IPCompleter(Completer):' | |||||
923 | def dict_key_matches(self, text): |
|
935 | def dict_key_matches(self, text): | |
924 | "Match string keys in a dictionary, after e.g. 'foo[' " |
|
936 | "Match string keys in a dictionary, after e.g. 'foo[' " | |
925 | def get_keys(obj): |
|
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 | if isinstance(obj, dict) or\ |
|
944 | if isinstance(obj, dict) or\ | |
928 | _safe_isinstance(obj, 'pandas', 'DataFrame'): |
|
945 | _safe_isinstance(obj, 'pandas', 'DataFrame'): | |
929 | try: |
|
946 | try: |
@@ -761,6 +761,22 b' def test_dict_key_completion_invalids():' | |||||
761 | _, matches = complete(line_buffer="name_error['") |
|
761 | _, matches = complete(line_buffer="name_error['") | |
762 | _, matches = complete(line_buffer="d['\\") # incomplete escape |
|
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 | def test_aimport_module_completer(): |
|
780 | def test_aimport_module_completer(): | |
765 | ip = get_ipython() |
|
781 | ip = get_ipython() | |
766 | _, matches = ip.complete('i', '%aimport i') |
|
782 | _, matches = ip.complete('i', '%aimport i') |
General Comments 0
You need to be logged in to leave comments.
Login now