dostring fixes.

This commit is contained in:
Dave Halter
2016-11-13 20:23:17 +01:00
parent b2bdfe4a28
commit 65d3e29146
5 changed files with 52 additions and 35 deletions

View File

@@ -26,7 +26,7 @@ class Context(object):
return context return context
context = context.parent_context context = context.parent_context
def execute(self, arguments=None): def execute(self, arguments):
return self.evaluator.execute(self, arguments) return self.evaluator.execute(self, arguments)
def execute_evaluated(self, *value_list): def execute_evaluated(self, *value_list):

View File

@@ -16,12 +16,14 @@ annotations.
from ast import literal_eval from ast import literal_eval
import re import re
from itertools import chain
from textwrap import dedent from textwrap import dedent
from jedi._compatibility import u
from jedi.common import unite
from jedi.evaluate import context
from jedi.evaluate.cache import memoize_default from jedi.evaluate.cache import memoize_default
from jedi.parser import ParserWithRecovery, load_grammar from jedi.parser import ParserWithRecovery, load_grammar
from jedi.parser.tree import Class from jedi.parser.tree import search_ancestor
from jedi.common import indent_block from jedi.common import indent_block
from jedi.evaluate.iterable import ArrayLiteralContext, FakeSequence, AlreadyEvaluated from jedi.evaluate.iterable import ArrayLiteralContext, FakeSequence, AlreadyEvaluated
@@ -114,12 +116,12 @@ def _strip_rst_role(type_str):
return type_str return type_str
def _evaluate_for_statement_string(evaluator, string, module): def _evaluate_for_statement_string(module_context, string):
code = dedent(""" code = dedent(u("""
def pseudo_docstring_stuff(): def pseudo_docstring_stuff():
# Create a pseudo function for docstring statements. # Create a pseudo function for docstring statements.
%s {0}
""") """))
if string is None: if string is None:
return [] return []
@@ -131,31 +133,39 @@ def _evaluate_for_statement_string(evaluator, string, module):
# Take the default grammar here, if we load the Python 2.7 grammar here, it # Take the default grammar here, if we load the Python 2.7 grammar here, it
# will be impossible to use `...` (Ellipsis) as a token. Docstring types # will be impossible to use `...` (Ellipsis) as a token. Docstring types
# don't need to conform with the current grammar. # don't need to conform with the current grammar.
p = ParserWithRecovery(load_grammar(), code % indent_block(string)) p = ParserWithRecovery(load_grammar(), code.format(indent_block(string)))
try: try:
pseudo_cls = p.module.subscopes[0] funcdef = p.module.subscopes[0]
# First pick suite, then simple_stmt and then the node, # First pick suite, then simple_stmt and then the node,
# which is also not the last item, because there's a newline. # which is also not the last item, because there's a newline.
stmt = pseudo_cls.children[-1].children[-1].children[-2] stmt = funcdef.children[-1].children[-1].children[-2]
except (AttributeError, IndexError): except (AttributeError, IndexError):
return [] return []
from jedi.evaluate.param import ValuesArguments
from jedi.evaluate.representation import FunctionExecutionContext
func_context = FunctionExecutionContext(
module_context.evaluator,
module_context,
funcdef,
ValuesArguments([])
)
# Use the module of the param. # Use the module of the param.
# TODO this module is not the module of the param in case of a function # TODO this module is not the module of the param in case of a function
# call. In that case it's the module of the function call. # call. In that case it's the module of the function call.
# stuffed with content from a function call. # stuffed with content from a function call.
pseudo_cls.parent = module return list(_execute_types_in_stmt(func_context, stmt))
return list(_execute_types_in_stmt(evaluator, stmt))
def _execute_types_in_stmt(evaluator, stmt): def _execute_types_in_stmt(module_context, stmt):
""" """
Executing all types or general elements that we find in a statement. This Executing all types or general elements that we find in a statement. This
doesn't include tuple, list and dict literals, because the stuff they doesn't include tuple, list and dict literals, because the stuff they
contain is executed. (Used as type information). contain is executed. (Used as type information).
""" """
definitions = evaluator.eval_element(stmt) definitions = module_context.eval_node(stmt)
return chain.from_iterable(_execute_array_values(evaluator, d) for d in definitions) return unite(_execute_array_values(module_context.evaluator, d) for d in definitions)
def _execute_array_values(evaluator, array): def _execute_array_values(evaluator, array):
@@ -165,35 +175,33 @@ def _execute_array_values(evaluator, array):
""" """
if isinstance(array, ArrayLiteralContext): if isinstance(array, ArrayLiteralContext):
values = [] values = []
for types in array.py__iter__(): for lazy_context in array.py__iter__():
objects = set(chain.from_iterable(_execute_array_values(evaluator, typ) for typ in types)) objects = unite(_execute_array_values(evaluator, typ) for typ in lazy_context.infer())
values.append(AlreadyEvaluated(objects)) values.append(context.LazyKnownContexts(objects))
return [FakeSequence(evaluator, values, array.type)] return set([FakeSequence(evaluator, array.array_type, values)])
else: else:
return evaluator.execute(array) return array.execute_evaluated()
@memoize_default(None, evaluator_is_first_arg=True) @memoize_default()
def follow_param(evaluator, param): def follow_param(module_context, param):
def eval_docstring(docstring): def eval_docstring(docstring):
return set( return set(
[p for param_str in _search_param_in_docstr(docstring, str(param.name)) [p for param_str in _search_param_in_docstr(docstring, str(param.name))
for p in _evaluate_for_statement_string(evaluator, param_str, module)] for p in _evaluate_for_statement_string(module_context, param_str)]
) )
func = param.parent_function func = param.parent_function
module = param.get_parent_until()
types = eval_docstring(func.raw_doc) types = eval_docstring(func.raw_doc)
if func.name.value == '__init__': if func.name.value == '__init__':
cls = func.get_parent_until(Class) cls = search_ancestor(func, 'classdef')
if cls.type == 'classdef': if cls.type == 'classdef':
types |= eval_docstring(cls.raw_doc) types |= eval_docstring(cls.raw_doc)
return types return types
@memoize_default(None, evaluator_is_first_arg=True) @memoize_default()
def find_return_types(evaluator, func): def find_return_types(module_context, func):
def search_return_in_docstr(code): def search_return_in_docstr(code):
for p in DOCSTRING_RETURN_PATTERNS: for p in DOCSTRING_RETURN_PATTERNS:
match = p.search(code) match = p.search(code)
@@ -201,4 +209,4 @@ def find_return_types(evaluator, func):
return _strip_rst_role(match.group(1)) return _strip_rst_role(match.group(1))
type_str = search_return_in_docstr(func.raw_doc) type_str = search_return_in_docstr(func.raw_doc)
return _evaluate_for_statement_string(evaluator, type_str, func.get_parent_until()) return _evaluate_for_statement_string(module_context, type_str)

View File

@@ -7,6 +7,7 @@ from jedi.parser import tree
from jedi.evaluate import iterable from jedi.evaluate import iterable
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate import context from jedi.evaluate import context
from jedi.evaluate import docstrings
def try_iter_content(types, depth=0): def try_iter_content(types, depth=0):
@@ -175,13 +176,19 @@ class ValuesArguments(AbstractArguments):
class ExecutedParam(object): class ExecutedParam(object):
"""Fake a param and give it values.""" """Fake a param and give it values."""
def __init__(self, original_param, var_args, lazy_context): def __init__(self, var_args_context, original_param, var_args, lazy_context):
self._root_context = var_args_context.get_root_context()
self._original_param = original_param self._original_param = original_param
self.var_args = var_args self.var_args = var_args
self._lazy_context = lazy_context self._lazy_context = lazy_context
self.string_name = self._original_param.name.value self.string_name = self._original_param.name.value
def infer(self): def infer(self):
pep0484_hints = set()#pep0484.follow_param(evaluator, param)
doc_params = docstrings.follow_param(self._root_context, self._original_param)
if pep0484_hints or doc_params:
return list(set(pep0484_hints) | set(doc_params))
return self._lazy_context.infer() return self._lazy_context.infer()
@property @property
@@ -253,7 +260,7 @@ def get_params(evaluator, parent_context, func, var_args):
analysis.add(evaluator, 'type-error-multiple-values', analysis.add(evaluator, 'type-error-multiple-values',
calling_va, message=m) calling_va, message=m)
else: else:
keys_used[key] = ExecutedParam(key_param, var_args, argument) keys_used[key] = ExecutedParam(parent_context, key_param, var_args, argument)
key, argument = next(var_arg_iterator, (None, None)) key, argument = next(var_arg_iterator, (None, None))
try: try:
@@ -297,7 +304,7 @@ def get_params(evaluator, parent_context, func, var_args):
else: else:
result_arg = argument result_arg = argument
result_params.append(ExecutedParam(param, var_args, result_arg)) result_params.append(ExecutedParam(parent_context, param, var_args, result_arg))
keys_used[param.name.value] = result_params[-1] keys_used[param.name.value] = result_params[-1]
if keys_only: if keys_only:
@@ -403,4 +410,4 @@ def create_default_param(parent_context, param):
result_arg = context.LazyUnknownContext() result_arg = context.LazyUnknownContext()
else: else:
result_arg = context.LazyTreeContext(parent_context, param.default) result_arg = context.LazyTreeContext(parent_context, param.default)
return ExecutedParam(param, None, result_arg) return ExecutedParam(parent_context, param, None, result_arg)

View File

@@ -643,7 +643,7 @@ class FunctionExecutionContext(Executed):
returns = funcdef.yields returns = funcdef.yields
else: else:
returns = funcdef.returns returns = funcdef.returns
types = set(docstrings.find_return_types(self.evaluator, funcdef)) types = set(docstrings.find_return_types(self.get_root_context(), funcdef))
types |= set(pep0484.find_return_types(self.evaluator, funcdef)) types |= set(pep0484.find_return_types(self.evaluator, funcdef))
for r in returns: for r in returns:

View File

@@ -122,6 +122,7 @@ from jedi import debug
from jedi._compatibility import unicode, is_py3 from jedi._compatibility import unicode, is_py3
from jedi.parser import Parser, load_grammar from jedi.parser import Parser, load_grammar
from jedi.api.classes import Definition from jedi.api.classes import Definition
from jedi.api.completion import get_user_scope
from jedi.evaluate.representation import ModuleContext from jedi.evaluate.representation import ModuleContext
@@ -193,7 +194,8 @@ class IntegrationTestCase(object):
module_context = script._get_module() module_context = script._get_module()
# The context shouldn't matter for the test results. # The context shouldn't matter for the test results.
element.parent = module_context.module_node element.parent = module_context.module_node
results = evaluator.eval_element(module_context, element) user_context = get_user_scope(module_context, (self.line_nr, 0))
results = evaluator.eval_element(user_context, element)
if not results: if not results:
raise Exception('Could not resolve %s on line %s' raise Exception('Could not resolve %s on line %s'
% (match.string, self.line_nr - 1)) % (match.string, self.line_nr - 1))