1
0
forked from VimPlug/jedi

Refactor docstrings

This commit is contained in:
Dave Halter
2021-01-14 01:11:50 +01:00
parent b9067ccdbb
commit 0ff532b937
4 changed files with 58 additions and 41 deletions

View File

@@ -18,7 +18,8 @@ from jedi.inference import imports
from jedi.inference.base_value import ValueSet from jedi.inference.base_value import ValueSet
from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names
from jedi.inference.context import get_global_filters from jedi.inference.context import get_global_filters
from jedi.inference.value import TreeInstance, ModuleValue from jedi.inference.value import TreeInstance
from jedi.inference.docstring_utils import DocstringModule
from jedi.inference.names import ParamNameWrapper, SubModuleName from jedi.inference.names import ParamNameWrapper, SubModuleName
from jedi.inference.gradual.conversion import convert_values, convert_names from jedi.inference.gradual.conversion import convert_values, convert_names
from jedi.parser_utils import cut_value_at_position from jedi.parser_utils import cut_value_at_position
@@ -462,12 +463,12 @@ class Completion:
def _complete_code_lines(self, code_lines): def _complete_code_lines(self, code_lines):
module_node = self._inference_state.grammar.parse(''.join(code_lines)) module_node = self._inference_state.grammar.parse(''.join(code_lines))
module_value = ModuleValue( module_value = DocstringModule(
self._inference_state, in_module_context=self._module_context,
module_node, inference_state=self._inference_state,
module_node=module_node,
code_lines=code_lines, code_lines=code_lines,
) )
module_value.parent_context = self._module_context
return Completion( return Completion(
self._inference_state, self._inference_state,
module_value.as_context(), module_value.as_context(),

View File

@@ -0,0 +1,21 @@
from jedi.inference.value import ModuleValue
from jedi.inference.context import ModuleContext
class DocstringModule(ModuleValue):
def __init__(self, in_module_context, **kwargs):
super().__init__(**kwargs)
self._in_module_context = in_module_context
def _as_context(self):
return DocstringModuleContext(self, self._in_module_context)
class DocstringModuleContext(ModuleContext):
def __init__(self, module_value, in_module_context):
super().__init__(module_value)
self._in_module_context = in_module_context
def get_filters(self, origin_scope=None, until_position=None):
yield from super().get_filters(until_position=until_position)
yield from self._in_module_context.get_filters()

View File

@@ -17,12 +17,10 @@ annotations.
import re import re
import warnings import warnings
from textwrap import dedent
from parso import parse, ParserSyntaxError from parso import parse, ParserSyntaxError
from jedi import debug from jedi import debug
from jedi.common import indent_block
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.base_value import iterator_to_value_set, ValueSet, \ from jedi.inference.base_value import iterator_to_value_set, ValueSet, \
NO_VALUES NO_VALUES
@@ -182,52 +180,40 @@ def _strip_rst_role(type_str):
def _infer_for_statement_string(module_context, string): def _infer_for_statement_string(module_context, string):
code = dedent("""
def pseudo_docstring_stuff():
'''
Create a pseudo function for docstring statements.
Need this docstring so that if the below part is not valid Python this
is still a function.
'''
{}
""")
if string is None: if string is None:
return [] return []
for element in re.findall(r'((?:\w+\.)*\w+)\.', string): potential_imports = re.findall(r'((?:\w+\.)*\w+)\.', string)
# Try to import module part in dotted name. # Try to import module part in dotted name.
# (e.g., 'threading' in 'threading.Thread'). # (e.g., 'threading' in 'threading.Thread').
string = 'import %s\n' % element + string imports = "\n".join(f"import {p}" for p in potential_imports)
string = f'{imports}\n{string}'
debug.dbg('Parse docstring code %s', string, color='BLUE') debug.dbg('Parse docstring code %s', string, color='BLUE')
grammar = module_context.inference_state.grammar grammar = module_context.inference_state.grammar
try: try:
module = grammar.parse(code.format(indent_block(string)), error_recovery=False) module = grammar.parse(string, error_recovery=False)
except ParserSyntaxError: except ParserSyntaxError:
return [] return []
try: try:
funcdef = next(module.iter_funcdefs()) # It's not the last item, because that's an end marker.
# First pick suite, then simple_stmt and then the node, stmt = module.children[-2]
# which is also not the last item, because there's a newline.
stmt = funcdef.children[-1].children[-1].children[-2]
except (AttributeError, IndexError): except (AttributeError, IndexError):
return [] return []
if stmt.type not in ('name', 'atom', 'atom_expr'): if stmt.type not in ('name', 'atom', 'atom_expr'):
return [] return []
from jedi.inference.value import FunctionValue # Here we basically use a fake module that also uses the filters in
function_value = FunctionValue( # the actual module.
module_context.inference_state, from jedi.inference.docstring_utils import DocstringModule
module_context, m = DocstringModule(
funcdef in_module_context=module_context,
inference_state=module_context.inference_state,
module_node=module,
code_lines=[],
) )
func_execution_context = function_value.as_context() return list(_execute_types_in_stmt(m.as_context(), stmt))
# Use the module of the param.
# 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.
# stuffed with content from a function call.
return list(_execute_types_in_stmt(func_execution_context, stmt))
def _execute_types_in_stmt(module_context, stmt): def _execute_types_in_stmt(module_context, stmt):

View File

@@ -55,6 +55,10 @@ class FilterWrapper:
def _get_definition_names(parso_cache_node, used_names, name_key): def _get_definition_names(parso_cache_node, used_names, name_key):
if parso_cache_node is None:
names = used_names.get(name_key, ())
return tuple(name for name in names if name.is_definition(include_setitem=True))
try: try:
for_module = _definition_name_cache[parso_cache_node] for_module = _definition_name_cache[parso_cache_node]
except KeyError: except KeyError:
@@ -88,11 +92,16 @@ class _AbstractUsedNamesFilter(AbstractFilter):
# used_names. However that also does not work, because it has a # used_names. However that also does not work, because it has a
# reference from the module, which itself is referenced by any node # reference from the module, which itself is referenced by any node
# through parents. # through parents.
self._parso_cache_node = get_parso_cache_node( path = module_context.py__file__()
module_context.inference_state.latest_grammar if path is None:
if module_context.is_stub() else module_context.inference_state.grammar, # If the path is None, there is no guarantee that parso caches it.
module_context.py__file__() self._parso_cache_node = None
) else:
self._parso_cache_node = get_parso_cache_node(
module_context.inference_state.latest_grammar
if module_context.is_stub() else module_context.inference_state.grammar,
path
)
self._used_names = module_context.tree_node.get_used_names() self._used_names = module_context.tree_node.get_used_names()
self.parent_context = parent_context self.parent_context = parent_context