From e12212942fab7aebb090ea2e631149bf2560254b Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 01:01:25 +0100 Subject: [PATCH 1/9] Add a failing test for type inference from docstring --- test/regression.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/regression.py b/test/regression.py index 42027b8d..e818ec1a 100755 --- a/test/regression.py +++ b/test/regression.py @@ -297,6 +297,27 @@ class TestRegression(TestBase): assert c[0].word == 'IndentationError' self.assertEqual(c[0].complete, 'or') + def test_docstrings_type_str(self): + s = """ + def func(arg): + ''' + :type arg: str + ''' + arg.""" + + words = [c.word for c in self.complete(s)] + assert 'join' in words + + def test_docstrings_type_dotted_import(self): + s = """ + def func(arg): + ''' + :type arg: threading.Thread + ''' + arg.""" + words = [c.word for c in self.complete(s)] + assert 'start' in words + class TestFeature(TestBase): def test_full_name(self): From 9f416c9dae196f20068c40644ae860fe4e03cca6 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 01:12:45 +0100 Subject: [PATCH 2/9] Implement import for dotted types in dcostring --- jedi/docstrings.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index 39134b19..4332783c 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -20,9 +20,20 @@ def follow_param(param): func = param.parent_function #print func, param, param.parent_function param_str = search_param_in_docstr(func.docstr, str(param.get_name())) + user_position = (1, 0) if param_str is not None: - p = parsing.PyFuzzyParser(param_str, None, (1, 0), no_docstr=True) + + # Try to import module part in dotted name. + # (e.g., 'threading' in 'threading.Thread'). + if '.' in param_str: + param_str = 'import {0}\n{1}'.format( + param_str.rsplit('.', 1)[0], + param_str) + user_position = (2, 0) + + p = parsing.PyFuzzyParser(param_str, None, user_position, + no_docstr=True) p.user_stmt.parent = func return evaluate.follow_statement(p.user_stmt) return [] From 81f59f9a6e04ddd0be1ca6c5575bd0db4c5b376e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 01:13:50 +0100 Subject: [PATCH 3/9] Make the failing test pass; dotted type works now --- jedi/docstrings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index 4332783c..b6a0ec93 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -34,7 +34,6 @@ def follow_param(param): p = parsing.PyFuzzyParser(param_str, None, user_position, no_docstr=True) - p.user_stmt.parent = func return evaluate.follow_statement(p.user_stmt) return [] From 691177c20c02b14c52729df5f2cd03f57ac0de29 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 17:05:43 +0100 Subject: [PATCH 4/9] Fix Python 2.5 failure --- jedi/docstrings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index b6a0ec93..badb6348 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -27,7 +27,7 @@ def follow_param(param): # Try to import module part in dotted name. # (e.g., 'threading' in 'threading.Thread'). if '.' in param_str: - param_str = 'import {0}\n{1}'.format( + param_str = 'import %s\n%s' % ( param_str.rsplit('.', 1)[0], param_str) user_position = (2, 0) From 9a35301a29de65daf37b7b19a48bbcd20d3638ed Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 21:03:48 +0100 Subject: [PATCH 5/9] Add doctests for search_param_in_docstr --- jedi/docstrings.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index badb6348..a85d68f8 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -39,6 +39,17 @@ def follow_param(param): def search_param_in_docstr(docstr, param_str): + """ + Search `docstr` for a type of `param_str`. + + >>> search_param_in_docstr(':type param: int', 'param') + 'int' + >>> search_param_in_docstr('@type param: int', 'param') + 'int' + >>> search_param_in_docstr('no document', 'param') is None + True + + """ # look at #40 to see definitions of those params patterns = [ re.compile(p % re.escape(param_str)) for p in DOCSTRING_PARAM_PATTERNS ] for pattern in patterns: From ef1123a513c8d4ed286d83820af751e048f949e5 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 21:07:30 +0100 Subject: [PATCH 6/9] Add a failing doctest for :class:`ClassName` --- jedi/docstrings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index a85d68f8..8e69d8d9 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -46,6 +46,9 @@ def search_param_in_docstr(docstr, param_str): 'int' >>> search_param_in_docstr('@type param: int', 'param') 'int' + >>> search_param_in_docstr( + ... ':type param: :class:`threading.Thread`', 'param') + 'threading.Thread' >>> search_param_in_docstr('no document', 'param') is None True From a7131fa949d6d1d52d0eadcec53f018f7135ce50 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 21:09:41 +0100 Subject: [PATCH 7/9] Make the failing doctest pass --- jedi/docstrings.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index 8e69d8d9..72d773b4 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -58,7 +58,10 @@ def search_param_in_docstr(docstr, param_str): for pattern in patterns: match = pattern.search(docstr) if match: - return match.group(1) + type_str = match.group(1) + if type_str.startswith(':class:'): + type_str = type_str[len(':class:'):].strip('`') + return type_str return None From 0cbbb17a82d1e82b7ad8d5ec2ad4d20f373ecb71 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 30 Jan 2013 21:23:04 +0100 Subject: [PATCH 8/9] Add tests in completion/docstring.py --- test/completion/docstring.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/completion/docstring.py b/test/completion/docstring.py index 66b6744e..94c4a471 100644 --- a/test/completion/docstring.py +++ b/test/completion/docstring.py @@ -1,10 +1,12 @@ """ Test docstrings in functions and classes, which are used to infer types """ -def f(a, b): +def f(a, b, c, d): """ asdfasdf :param a: blablabla :type a: str :type b: (str, int) + :type c: threading.Thread + :type d: :class:`threading.Thread` :rtype: dict """ #? str() @@ -13,6 +15,10 @@ def f(a, b): b[0] #? int() b[1] + #? ['join'] + c.join + #? ['join'] + d.join #? dict() f() From 2917da8025a602be494b69a502f588c9c0870adc Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Fri, 1 Feb 2013 17:57:24 +0100 Subject: [PATCH 9/9] Make :ROLE:`TYPE` handling more general --- jedi/docstrings.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/jedi/docstrings.py b/jedi/docstrings.py index 72d773b4..9eff5004 100644 --- a/jedi/docstrings.py +++ b/jedi/docstrings.py @@ -15,6 +15,8 @@ DOCSTRING_RETURN_PATTERNS = [ re.compile(r'\s*@rtype:\s*([^\n]+)', re.M), # Epidoc ] +REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`') + #@cache.memoize_default() # TODO add def follow_param(param): func = param.parent_function @@ -58,14 +60,33 @@ def search_param_in_docstr(docstr, param_str): for pattern in patterns: match = pattern.search(docstr) if match: - type_str = match.group(1) - if type_str.startswith(':class:'): - type_str = type_str[len(':class:'):].strip('`') - return type_str + return strip_rest_role(match.group(1)) return None +def strip_rest_role(type_str): + """ + Strip off the part looks like a ReST role in `type_str`. + + >>> strip_rest_role(':class:`ClassName`') # strip off :class: + 'ClassName' + >>> strip_rest_role(':py:obj:`module.Object`') # works with domain + 'module.Object' + >>> strip_rest_role('ClassName') # do nothing when not ReST role + 'ClassName' + + See also: + http://sphinx-doc.org/domains.html#cross-referencing-python-objects + + """ + match = REST_ROLE_PATTERN.match(type_str) + if match: + return match.group(1) + else: + return type_str + + def find_return_types(func): if isinstance(func, evaluate.InstanceElement): func = func.var