Show More
@@ -496,6 +496,12 b' class IPCompleter(Completer):' | |||
|
496 | 496 | else: |
|
497 | 497 | self.clean_glob = self._clean_glob |
|
498 | 498 | |
|
499 | #regexp to parse docstring for function signature | |
|
500 | self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') | |
|
501 | self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') | |
|
502 | #use this if positional argument name is also needed | |
|
503 | #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)') | |
|
504 | ||
|
499 | 505 | # All active matcher routines for completion |
|
500 | 506 | self.matchers = [self.python_matches, |
|
501 | 507 | self.file_matches, |
@@ -664,29 +670,67 b' class IPCompleter(Completer):' | |||
|
664 | 670 | |
|
665 | 671 | return matches |
|
666 | 672 | |
|
673 | def _default_arguments_from_docstring(self, doc): | |
|
674 | """Parse the first line of docstring for call signature. | |
|
675 | ||
|
676 | Docstring should be of the form 'min(iterable[, key=func])\n'. | |
|
677 | It can also parse cython docstring of the form | |
|
678 | 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'. | |
|
679 | """ | |
|
680 | if doc is None: | |
|
681 | return [] | |
|
682 | ||
|
683 | #care only the firstline | |
|
684 | line = doc.lstrip().splitlines()[0] | |
|
685 | ||
|
686 | #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') | |
|
687 | #'min(iterable[, key=func])\n' -> 'iterable[, key=func]' | |
|
688 | sig = self.docstring_sig_re.search(line) | |
|
689 | if sig is None: | |
|
690 | return [] | |
|
691 | # iterable[, key=func]' -> ['iterable[' ,' key=func]'] | |
|
692 | sig = sig.groups()[0].split(',') | |
|
693 | ret = [] | |
|
694 | for s in sig: | |
|
695 | #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') | |
|
696 | ret += self.docstring_kwd_re.findall(s) | |
|
697 | return ret | |
|
698 | ||
|
667 | 699 | def _default_arguments(self, obj): |
|
668 | 700 | """Return the list of default arguments of obj if it is callable, |
|
669 | 701 | or empty list otherwise.""" |
|
670 | ||
|
671 | if not (inspect.isfunction(obj) or inspect.ismethod(obj)): | |
|
672 | # for classes, check for __init__,__new__ | |
|
702 | call_obj = obj | |
|
703 | ret = [] | |
|
704 | if inspect.isbuiltin(obj): | |
|
705 | pass | |
|
706 | elif not (inspect.isfunction(obj) or inspect.ismethod(obj)): | |
|
673 | 707 | if inspect.isclass(obj): |
|
674 | obj = (getattr(obj,'__init__',None) or | |
|
675 | getattr(obj,'__new__',None)) | |
|
708 | #for cython embededsignature=True the constructor docstring | |
|
709 | #belongs to the object itself not __init__ | |
|
710 | ret += self._default_arguments_from_docstring( | |
|
711 | getattr(obj, '__doc__', '')) | |
|
712 | # for classes, check for __init__,__new__ | |
|
713 | call_obj = (getattr(obj, '__init__', None) or | |
|
714 | getattr(obj, '__new__', None)) | |
|
676 | 715 | # for all others, check if they are __call__able |
|
677 | 716 | elif hasattr(obj, '__call__'): |
|
678 | obj = obj.__call__ | |
|
679 | # XXX: is there a way to handle the builtins ? | |
|
717 | call_obj = obj.__call__ | |
|
718 | ||
|
719 | ret += self._default_arguments_from_docstring( | |
|
720 | getattr(call_obj, '__doc__', '')) | |
|
721 | ||
|
680 | 722 | try: |
|
681 | args,_,_1,defaults = inspect.getargspec(obj) | |
|
723 | args,_,_1,defaults = inspect.getargspec(call_obj) | |
|
682 | 724 | if defaults: |
|
683 |
ret |
|
|
684 |
except TypeError: |
|
|
685 | return [] | |
|
725 | ret+=args[-len(defaults):] | |
|
726 | except TypeError: | |
|
727 | pass | |
|
728 | ||
|
729 | return list(set(ret)) | |
|
686 | 730 | |
|
687 | 731 | def python_func_kw_matches(self,text): |
|
688 | 732 | """Match named parameters (kwargs) of the last open function""" |
|
689 | ||
|
733 | ||
|
690 | 734 | if "." in text: # a parameter cannot be dotted |
|
691 | 735 | return [] |
|
692 | 736 | try: regexp = self.__funcParamsRegex |
@@ -703,6 +747,7 b' class IPCompleter(Completer):' | |||
|
703 | 747 | tokens = regexp.findall(self.text_until_cursor) |
|
704 | 748 | tokens.reverse() |
|
705 | 749 | iterTokens = iter(tokens); openPar = 0 |
|
750 | ||
|
706 | 751 | for token in iterTokens: |
|
707 | 752 | if token == ')': |
|
708 | 753 | openPar -= 1 |
@@ -716,6 +761,7 b' class IPCompleter(Completer):' | |||
|
716 | 761 | # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) |
|
717 | 762 | ids = [] |
|
718 | 763 | isId = re.compile(r'\w+$').match |
|
764 | ||
|
719 | 765 | while True: |
|
720 | 766 | try: |
|
721 | 767 | ids.append(next(iterTokens)) |
@@ -735,9 +781,10 b' class IPCompleter(Completer):' | |||
|
735 | 781 | for callableMatch in callableMatches: |
|
736 | 782 | try: |
|
737 | 783 | namedArgs = self._default_arguments(eval(callableMatch, |
|
738 |
|
|
|
784 | self.namespace)) | |
|
739 | 785 | except: |
|
740 | 786 | continue |
|
787 | ||
|
741 | 788 | for namedArg in namedArgs: |
|
742 | 789 | if namedArg.startswith(text): |
|
743 | 790 | argMatches.append("%s=" %namedArg) |
@@ -288,12 +288,31 b' def test_func_kw_completions():' | |||
|
288 | 288 | s, matches = c.complete(None, 'myfunc(1,b') |
|
289 | 289 | nt.assert_in('b=', matches) |
|
290 | 290 | # Simulate completing with cursor right after b (pos==10): |
|
291 | s, matches = c.complete(None,'myfunc(1,b)', 10) | |
|
291 | s, matches = c.complete(None, 'myfunc(1,b)', 10) | |
|
292 | 292 | nt.assert_in('b=', matches) |
|
293 | s, matches = c.complete(None,'myfunc(a="escaped\\")string",b') | |
|
293 | s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b') | |
|
294 | 294 | nt.assert_in('b=', matches) |
|
295 | #builtin function | |
|
296 | s, matches = c.complete(None, 'min(k, k') | |
|
297 | nt.assert_in('key=', matches) | |
|
295 | 298 | |
|
296 | 299 | |
|
300 | def test_default_arguments_from_docstring(): | |
|
301 | doc = min.__doc__ | |
|
302 | ip = get_ipython() | |
|
303 | c = ip.Completer | |
|
304 | kwd = c._default_arguments_from_docstring( | |
|
305 | 'min(iterable[, key=func]) -> value') | |
|
306 | nt.assert_equal(kwd, ['key']) | |
|
307 | #with cython type etc | |
|
308 | kwd = c._default_arguments_from_docstring( | |
|
309 | 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n') | |
|
310 | nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit']) | |
|
311 | #white spaces | |
|
312 | kwd = c._default_arguments_from_docstring( | |
|
313 | '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n') | |
|
314 | nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit']) | |
|
315 | ||
|
297 | 316 | def test_line_magics(): |
|
298 | 317 | ip = get_ipython() |
|
299 | 318 | c = ip.Completer |
General Comments 0
You need to be logged in to leave comments.
Login now