##// END OF EJS Templates
Merge pull request #2599 from piti118/docstring_keyword...
Min RK -
r9435:918365c1 merge
parent child Browse files
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 return args[-len(defaults):]
725 ret+=args[-len(defaults):]
684 except TypeError: pass
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 self.namespace))
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