Show More
@@ -496,6 +496,12 b' class IPCompleter(Completer):' | |||||
496 | else: |
|
496 | else: | |
497 | self.clean_glob = self._clean_glob |
|
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 | # All active matcher routines for completion |
|
505 | # All active matcher routines for completion | |
500 | self.matchers = [self.python_matches, |
|
506 | self.matchers = [self.python_matches, | |
501 | self.file_matches, |
|
507 | self.file_matches, | |
@@ -664,25 +670,63 b' class IPCompleter(Completer):' | |||||
664 |
|
670 | |||
665 | return matches |
|
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 | def _default_arguments(self, obj): |
|
699 | def _default_arguments(self, obj): | |
668 | """Return the list of default arguments of obj if it is callable, |
|
700 | """Return the list of default arguments of obj if it is callable, | |
669 | or empty list otherwise.""" |
|
701 | or empty list otherwise.""" | |
670 |
|
702 | call_obj = obj | ||
671 | if not (inspect.isfunction(obj) or inspect.ismethod(obj)): |
|
703 | ret = [] | |
672 | # for classes, check for __init__,__new__ |
|
704 | if inspect.isbuiltin(obj): | |
|
705 | pass | |||
|
706 | elif not (inspect.isfunction(obj) or inspect.ismethod(obj)): | |||
673 | if inspect.isclass(obj): |
|
707 | if inspect.isclass(obj): | |
674 | obj = (getattr(obj,'__init__',None) or |
|
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 | |||
675 | getattr(obj,'__new__',None)) |
|
714 | getattr(obj, '__new__', None)) | |
676 | # for all others, check if they are __call__able |
|
715 | # for all others, check if they are __call__able | |
677 | elif hasattr(obj, '__call__'): |
|
716 | elif hasattr(obj, '__call__'): | |
678 | obj = obj.__call__ |
|
717 | call_obj = obj.__call__ | |
679 | # XXX: is there a way to handle the builtins ? |
|
718 | ||
|
719 | ret += self._default_arguments_from_docstring( | |||
|
720 | getattr(call_obj, '__doc__', '')) | |||
|
721 | ||||
680 | try: |
|
722 | try: | |
681 | args,_,_1,defaults = inspect.getargspec(obj) |
|
723 | args,_,_1,defaults = inspect.getargspec(call_obj) | |
682 | if defaults: |
|
724 | if defaults: | |
683 |
ret |
|
725 | ret+=args[-len(defaults):] | |
684 |
except TypeError: |
|
726 | except TypeError: | |
685 | return [] |
|
727 | pass | |
|
728 | ||||
|
729 | return list(set(ret)) | |||
686 |
|
730 | |||
687 | def python_func_kw_matches(self,text): |
|
731 | def python_func_kw_matches(self,text): | |
688 | """Match named parameters (kwargs) of the last open function""" |
|
732 | """Match named parameters (kwargs) of the last open function""" | |
@@ -703,6 +747,7 b' class IPCompleter(Completer):' | |||||
703 | tokens = regexp.findall(self.text_until_cursor) |
|
747 | tokens = regexp.findall(self.text_until_cursor) | |
704 | tokens.reverse() |
|
748 | tokens.reverse() | |
705 | iterTokens = iter(tokens); openPar = 0 |
|
749 | iterTokens = iter(tokens); openPar = 0 | |
|
750 | ||||
706 | for token in iterTokens: |
|
751 | for token in iterTokens: | |
707 | if token == ')': |
|
752 | if token == ')': | |
708 | openPar -= 1 |
|
753 | openPar -= 1 | |
@@ -716,6 +761,7 b' class IPCompleter(Completer):' | |||||
716 | # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) |
|
761 | # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) | |
717 | ids = [] |
|
762 | ids = [] | |
718 | isId = re.compile(r'\w+$').match |
|
763 | isId = re.compile(r'\w+$').match | |
|
764 | ||||
719 | while True: |
|
765 | while True: | |
720 | try: |
|
766 | try: | |
721 | ids.append(next(iterTokens)) |
|
767 | ids.append(next(iterTokens)) | |
@@ -738,6 +784,7 b' class IPCompleter(Completer):' | |||||
738 |
|
|
784 | self.namespace)) | |
739 | except: |
|
785 | except: | |
740 | continue |
|
786 | continue | |
|
787 | ||||
741 | for namedArg in namedArgs: |
|
788 | for namedArg in namedArgs: | |
742 | if namedArg.startswith(text): |
|
789 | if namedArg.startswith(text): | |
743 | argMatches.append("%s=" %namedArg) |
|
790 | argMatches.append("%s=" %namedArg) |
@@ -292,8 +292,27 b' def test_func_kw_completions():' | |||||
292 | nt.assert_in('b=', matches) |
|
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 | nt.assert_in('b=', matches) |
|
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 | def test_line_magics(): |
|
316 | def test_line_magics(): | |
298 | ip = get_ipython() |
|
317 | ip = get_ipython() | |
299 | c = ip.Completer |
|
318 | c = ip.Completer |
General Comments 0
You need to be logged in to leave comments.
Login now