##// END OF EJS Templates
Show signature with Jedi....
Matthias Bussonnier -
Show More
@@ -0,0 +1,4 b''
1 Terminal IPython will now show the signature of the function while completing.
2 Only the currently highlighted function will show its signature on the line
3 below the completer by default. The functionality is recent so might be
4 limited, we welcome bug report and enhancement request on it.
@@ -1,4 +1,3 b''
1 # encoding: utf-8
2 1 """Completion for IPython.
3 2
4 3 This module started as fork of the rlcompleter module in the Python standard
@@ -87,7 +86,7 b' We welcome any feedback on these new API, and we also encourage you to try this'
87 86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
88 87 to have extra logging information is :any:`jedi` is crashing, or if current
89 88 IPython completer pending deprecations are returning results not yet handled
90 by :any:`jedi`.
89 by :any:`jedi`
91 90
92 91 Using Jedi for tab completion allow snippets like the following to work without
93 92 having to execute any code:
@@ -103,8 +102,6 b' Be sure to update :any:`jedi` to the latest stable version or to try the'
103 102 current development version to get better completions.
104 103 """
105 104
106 # skip module docstests
107 skip_doctest = True
108 105
109 106 # Copyright (c) IPython Development Team.
110 107 # Distributed under the terms of the Modified BSD License.
@@ -142,9 +139,13 b' from IPython.utils.dir2 import dir2, get_real_method'
142 139 from IPython.utils.process import arg_split
143 140 from traitlets import Bool, Enum, observe, Int
144 141
142 # skip module docstests
143 skip_doctest = True
144
145 145 try:
146 146 import jedi
147 147 import jedi.api.helpers
148 import jedi.api.classes
148 149 JEDI_INSTALLED = True
149 150 except ImportError:
150 151 JEDI_INSTALLED = False
@@ -237,7 +238,7 b' def protect_filename(s, protectables=PROTECTABLES):'
237 238 return s
238 239
239 240
240 def expand_user(path):
241 def expand_user(path:str) -> Tuple[str, bool, str]:
241 242 """Expand ``~``-style usernames in strings.
242 243
243 244 This is similar to :func:`os.path.expanduser`, but it computes and returns
@@ -277,7 +278,7 b' def expand_user(path):'
277 278 return newpath, tilde_expand, tilde_val
278 279
279 280
280 def compress_user(path, tilde_expand, tilde_val):
281 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
281 282 """Does the opposite of expand_user, with its outputs.
282 283 """
283 284 if tilde_expand:
@@ -338,6 +339,8 b' class _FakeJediCompletion:'
338 339 self.complete = name
339 340 self.type = 'crashed'
340 341 self.name_with_symbols = name
342 self.signature = ''
343 self._origin = 'fake'
341 344
342 345 def __repr__(self):
343 346 return '<Fake completion object jedi has crashed>'
@@ -366,7 +369,9 b' class Completion:'
366 369 ``IPython.python_matches``, ``IPython.magics_matches``...).
367 370 """
368 371
369 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='') -> None:
372 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
373
374 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
370 375 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
371 376 "It may change without warnings. "
372 377 "Use in corresponding context manager.",
@@ -376,10 +381,12 b' class Completion:'
376 381 self.end = end
377 382 self.text = text
378 383 self.type = type
384 self.signature = signature
379 385 self._origin = _origin
380 386
381 387 def __repr__(self):
382 return '<Completion start=%s end=%s text=%r type=%r>' % (self.start, self.end, self.text, self.type or '?')
388 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
389 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
383 390
384 391 def __eq__(self, other)->Bool:
385 392 """
@@ -417,6 +424,10 b' def _deduplicate_completions(text: str, completions: _IC)-> _IC:'
417 424 completions: Iterator[Completion]
418 425 iterator over the completions to deduplicate
419 426
427 Yields
428 ------
429 `Completions` objects
430
420 431
421 432 Completions coming from multiple sources, may be different but end up having
422 433 the same effect when applied to ``text``. If this is the case, this will
@@ -489,7 +500,7 b' def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:'
489 500 seen_jedi.add(new_text)
490 501 elif c._origin == 'IPCompleter.python_matches':
491 502 seen_python_matches.add(new_text)
492 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin)
503 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
493 504 diff = seen_python_matches.difference(seen_jedi)
494 505 if diff and _debug:
495 506 print('IPython.python matches have extras:', diff)
@@ -933,6 +944,52 b' def back_latex_name_matches(text:str):'
933 944 return u'', ()
934 945
935 946
947 def _formatparamchildren(parameter) -> str:
948 """
949 Get parameter name and value from Jedi Private API
950
951 Jedi does not expose a simple way to get `param=value` from its API.
952
953 Prameter
954 ========
955
956 parameter:
957 Jedi's function `Param`
958
959 Returns
960 =======
961
962 A string like 'a', 'b=1', '*args', '**kwargs'
963
964
965 """
966 description = parameter.description
967 if not description.startswith('param '):
968 raise ValueError('Jedi function parameter description have change format.'
969 'Expected "param ...", found %r".' % description)
970 return description[6:]
971
972 def _make_signature(completion)-> str:
973 """
974 Make the signature from a jedi completion
975
976 Parameter
977 =========
978
979 completion: jedi.Completion
980 object does not complete a function type
981
982 Returns
983 =======
984
985 a string consisting of the function signature, with the parenthesis but
986 without the function name. example:
987 `(a, *args, b=1, **kwargs)`
988
989 """
990
991 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for p in completion.params) if f])
992
936 993 class IPCompleter(Completer):
937 994 """Extension of the completer class with IPython-specific features"""
938 995
@@ -1762,10 +1819,15 b' class IPCompleter(Completer):'
1762 1819 print("Error in Jedi getting type of ", jm)
1763 1820 type_ = None
1764 1821 delta = len(jm.name_with_symbols) - len(jm.complete)
1822 if type_ == 'function':
1823 signature = _make_signature(jm)
1824 else:
1825 signature = ''
1765 1826 yield Completion(start=offset - delta,
1766 1827 end=offset,
1767 1828 text=jm.name_with_symbols,
1768 1829 type=type_,
1830 signature=signature,
1769 1831 _origin='jedi')
1770 1832
1771 1833 if time.monotonic() > deadline:
@@ -1777,7 +1839,8 b' class IPCompleter(Completer):'
1777 1839 end=offset,
1778 1840 text=jm.name_with_symbols,
1779 1841 type='<unknown>', # don't compute type for speed
1780 _origin='jedi')
1842 _origin='jedi',
1843 signature='')
1781 1844
1782 1845
1783 1846 start_offset = before.rfind(matched_text)
@@ -1785,13 +1848,14 b' class IPCompleter(Completer):'
1785 1848 # TODO:
1786 1849 # Supress this, right now just for debug.
1787 1850 if jedi_matches and matches and self.debug:
1788 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--', _origin='debug')
1851 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
1852 _origin='debug', type='none', signature='')
1789 1853
1790 1854 # I'm unsure if this is always true, so let's assert and see if it
1791 1855 # crash
1792 1856 assert before.endswith(matched_text)
1793 1857 for m, t in zip(matches, matches_origin):
1794 yield Completion(start=start_offset, end=offset, text=m, _origin=t)
1858 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
1795 1859
1796 1860
1797 1861 def complete(self, text=None, line_buffer=None, cursor_pos=None):
@@ -37,6 +37,8 b' from IPython.utils._process_common import arg_split'
37 37 # FIXME: this should be pulled in with the right call via the component system
38 38 from IPython import get_ipython
39 39
40 from typing import List
41
40 42 #-----------------------------------------------------------------------------
41 43 # Globals and constants
42 44 #-----------------------------------------------------------------------------
@@ -153,7 +155,7 b' def is_importable(module, attr, only_modules):'
153 155 return not(attr[:2] == '__' and attr[-2:] == '__')
154 156
155 157
156 def try_import(mod: str, only_modules=False):
158 def try_import(mod: str, only_modules=False) -> List[str]:
157 159 """
158 160 Try to import given module and return list of potential completions.
159 161 """
@@ -173,9 +175,9 b' def try_import(mod: str, only_modules=False):'
173 175 completions.extend(getattr(m, '__all__', []))
174 176 if m_is_init:
175 177 completions.extend(module_list(os.path.dirname(m.__file__)))
176 completions = {c for c in completions if isinstance(c, str)}
177 completions.discard('__init__')
178 return list(completions)
178 completions_set = {c for c in completions if isinstance(c, str)}
179 completions_set.discard('__init__')
180 return list(completions_set)
179 181
180 182
181 183 #-----------------------------------------------------------------------------
@@ -335,6 +335,18 b' def test_jedi():'
335 335
336 336 yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize'
337 337
338 def test_completion_have_signature():
339 """
340 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
341 """
342 ip = get_ipython()
343 with provisionalcompleter():
344 completions = ip.Completer.completions('ope', 3)
345 c = next(completions) # should be `open`
346 assert 'file' in c.signature, "Signature of function was not found by completer"
347 assert 'encoding' in c.signature, "Signature of function was not found by completer"
348
349
338 350 def test_deduplicate_completions():
339 351 """
340 352 Test that completions are correctly deduplicated (even if ranges are not the same)
@@ -946,4 +958,4 b' def test_snake_case_completion():'
946 958 ip.user_ns['some_four'] = 4
947 959 _, matches = ip.complete("s_", "print(s_f")
948 960 nt.assert_in('some_three', matches)
949 nt.assert_in('some_four', matches) No newline at end of file
961 nt.assert_in('some_four', matches)
@@ -120,9 +120,9 b' class IPythonPTCompleter(Completer):'
120 120
121 121 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
122 122 if c.type == 'function':
123 display_text = display_text + '()'
124
125 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
123 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
124 else:
125 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
126 126
127 127 class IPythonPTLexer(Lexer):
128 128 """
@@ -100,6 +100,8 b' IPython and Jedi will be able to infer that ``data[0]`` is actually a string'
100 100 and should show relevant completions like ``upper()``, ``lower()`` and other
101 101 string methods. You can use the :kbd:`Tab` key to cycle through completions,
102 102 and while a completion is highlighted, its type will be shown as well.
103 When the type of the completion is a function, the completer will also show the
104 signature of the function when highlighted.
103 105
104 106 Exploring your objects
105 107 ======================
General Comments 0
You need to be logged in to leave comments. Login now