diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index dac52e1d..4f420291 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -15,9 +15,11 @@ annotations. """ import re +from textwrap import dedent from jedi.evaluate.cache import memoize_default from jedi.parser import Parser +from jedi.common import indent_block DOCSTRING_PARAM_PATTERNS = [ r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx @@ -35,24 +37,30 @@ REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`') @memoize_default(None, evaluator_is_first_arg=True) def follow_param(evaluator, param): func = param.parent_function - # print func, param, param.parent_function param_str = _search_param_in_docstr(func.raw_doc, str(param.get_name())) - position = (1, 0) + code = dedent(""" + class PseudoDocstring(): + '''Create a pseudo class for docstring statements.''' + %s + """) if param_str is not None: - # Try to import module part in dotted name. # (e.g., 'threading' in 'threading.Thread'). if '.' in param_str: param_str = 'import %s\n%s' % ( param_str.rsplit('.', 1)[0], param_str) - position = (2, 0) - p = Parser(param_str, no_docstr=True) - stmt = p.module.get_statement_for_position(position) - if stmt is None: + p = Parser(code % indent_block(param_str), no_docstr=True) + pseudo_cls = p.module.subscopes[0] + try: + stmt = pseudo_cls.statements[-1] + except IndexError: return [] + + stmt.start_pos = param.start_pos + pseudo_cls.parent = param.get_parent_until() return evaluator.eval_statement(stmt) return [] @@ -105,6 +113,7 @@ def _strip_rest_role(type_str): return type_str +@memoize_default(None, evaluator_is_first_arg=True) def find_return_types(evaluator, func): def search_return_in_docstr(code): for p in DOCSTRING_RETURN_PATTERNS: diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 55c86ed9..b17e7732 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -87,7 +87,7 @@ class NameFinder(object): break if not result and isinstance(self.scope, er.Instance): - # __getattr__ / __getattribute__ + # handling __getattr__ / __getattribute__ for r in self._check_getattr(self.scope): if not isinstance(r, compiled.CompiledObject): new_name = copy.copy(r.name) diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 23aa21ed..cb9434ad 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -173,7 +173,7 @@ class FakeStatement(pr.Statement): def __init__(self, expression_list, start_pos=(0, 0)): p = start_pos super(FakeStatement, self).__init__(FakeSubModule, expression_list, p, p) - self._expression_list = expression_list + self.set_expression_list(expression_list) class FakeName(pr.Name): diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 04b21e21..34875999 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -32,7 +32,7 @@ def get_params(evaluator, func, var_args): arr.keys = key_stmts arr.type = array_type - new_param._expression_list = [arr] + new_param.set_expression_list([arr]) name = copy.copy(param.get_name()) name.parent = new_param diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 534cd88c..185154d5 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -1159,6 +1159,10 @@ isinstance(c, (tokenize.Token, Operator)) else unicode(c) result.append(tok) return result + def set_expression_list(self, lst): + """It's necessary for some "hacks" to change the expression_list.""" + self._expression_list = lst + class Param(Statement): """