mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Move the docstring checking code to the names
This commit is contained in:
@@ -115,6 +115,11 @@ def goto_or_infer(request, Script):
|
|||||||
return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs)
|
return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session', params=['goto', 'help'])
|
||||||
|
def goto_or_help(request, Script):
|
||||||
|
return lambda code, *args, **kwargs: getattr(Script(code), request.param)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def has_typing(environment):
|
def has_typing(environment):
|
||||||
if environment.version_info >= (3, 5, 0):
|
if environment.version_info >= (3, 5, 0):
|
||||||
|
|||||||
@@ -742,9 +742,6 @@ class _Help(object):
|
|||||||
|
|
||||||
@memoize_method
|
@memoize_method
|
||||||
def _get_values(self, fast):
|
def _get_values(self, fast):
|
||||||
if isinstance(self._name, ImportName) and fast:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
if self._name.api_type == 'statement':
|
if self._name.api_type == 'statement':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -756,29 +753,6 @@ class _Help(object):
|
|||||||
|
|
||||||
See :attr:`doc` for example.
|
See :attr:`doc` for example.
|
||||||
"""
|
"""
|
||||||
full_doc = ''
|
if isinstance(self._name, ImportName) and fast:
|
||||||
# Using the first docstring that we see.
|
return ''
|
||||||
for value in self._get_values(fast=fast):
|
return self._name.py__doc__(include_signatures=not raw)
|
||||||
if full_doc:
|
|
||||||
# In case we have multiple values, just return all of them
|
|
||||||
# separated by a few dashes.
|
|
||||||
full_doc += '\n' + '-' * 30 + '\n'
|
|
||||||
|
|
||||||
doc = value.py__doc__()
|
|
||||||
|
|
||||||
signature_text = ''
|
|
||||||
if self._name.is_value_name:
|
|
||||||
if not raw:
|
|
||||||
signature_text = _format_signatures(value)
|
|
||||||
if not doc and value.is_stub():
|
|
||||||
for c in convert_values(ValueSet({value}), ignore_compiled=False):
|
|
||||||
doc = c.py__doc__()
|
|
||||||
if doc:
|
|
||||||
break
|
|
||||||
|
|
||||||
if signature_text and doc:
|
|
||||||
full_doc += signature_text + '\n\n' + doc
|
|
||||||
else:
|
|
||||||
full_doc += signature_text + doc
|
|
||||||
|
|
||||||
return full_doc
|
|
||||||
|
|||||||
@@ -15,41 +15,11 @@ except ImportError:
|
|||||||
pydoc_topics = None
|
pydoc_topics = None
|
||||||
|
|
||||||
|
|
||||||
def get_operator(inference_state, string, pos):
|
|
||||||
return Keyword(inference_state, string, pos)
|
|
||||||
|
|
||||||
|
|
||||||
class KeywordName(AbstractArbitraryName):
|
class KeywordName(AbstractArbitraryName):
|
||||||
api_type = u'keyword'
|
api_type = u'keyword'
|
||||||
|
|
||||||
def infer(self):
|
def py__doc__(self, include_signatures=False):
|
||||||
return [Keyword(self.inference_state, self.string_name, (0, 0))]
|
return imitate_pydoc(self.string_name)
|
||||||
|
|
||||||
|
|
||||||
class Keyword(object):
|
|
||||||
api_type = u'keyword'
|
|
||||||
|
|
||||||
def __init__(self, inference_state, name, pos):
|
|
||||||
self.name = KeywordName(inference_state, name)
|
|
||||||
self.start_pos = pos
|
|
||||||
self.parent = inference_state.builtins_module
|
|
||||||
|
|
||||||
@property
|
|
||||||
def names(self):
|
|
||||||
""" For a `parsing.Name` like comparision """
|
|
||||||
return [self.name]
|
|
||||||
|
|
||||||
def py__doc__(self):
|
|
||||||
return imitate_pydoc(self.name.string_name)
|
|
||||||
|
|
||||||
def get_signatures(self):
|
|
||||||
# TODO this makes no sense, I think Keyword should somehow merge with
|
|
||||||
# Value to make it easier for the api/classes.py to deal with all
|
|
||||||
# of it.
|
|
||||||
return []
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<%s: %s>' % (type(self).__name__, self.name)
|
|
||||||
|
|
||||||
|
|
||||||
def imitate_pydoc(string):
|
def imitate_pydoc(string):
|
||||||
|
|||||||
@@ -288,6 +288,10 @@ class CompiledName(AbstractNameDefinition):
|
|||||||
self.parent_context = parent_context
|
self.parent_context = parent_context
|
||||||
self.string_name = name
|
self.string_name = name
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
value, = self.infer()
|
||||||
|
return value.py__doc__()
|
||||||
|
|
||||||
def _get_qualified_names(self):
|
def _get_qualified_names(self):
|
||||||
parent_qualified_names = self.parent_context.get_qualified_names()
|
parent_qualified_names = self.parent_context.get_qualified_names()
|
||||||
if parent_qualified_names is None:
|
if parent_qualified_names is None:
|
||||||
|
|||||||
@@ -136,13 +136,14 @@ def _python_to_stub_names(names, fallback_to_python=False):
|
|||||||
yield name
|
yield name
|
||||||
|
|
||||||
|
|
||||||
def convert_names(names, only_stubs=False, prefer_stubs=False):
|
def convert_names(names, only_stubs=False, prefer_stubs=False, prefer_stub_to_compiled=True):
|
||||||
assert not (only_stubs and prefer_stubs)
|
assert not (only_stubs and prefer_stubs)
|
||||||
with debug.increase_indent_cm('convert names'):
|
with debug.increase_indent_cm('convert names'):
|
||||||
if only_stubs or prefer_stubs:
|
if only_stubs or prefer_stubs:
|
||||||
return _python_to_stub_names(names, fallback_to_python=prefer_stubs)
|
return _python_to_stub_names(names, fallback_to_python=prefer_stubs)
|
||||||
else:
|
else:
|
||||||
return _try_stub_to_python_names(names, prefer_stub_to_compiled=True)
|
return _try_stub_to_python_names(
|
||||||
|
names, prefer_stub_to_compiled=prefer_stub_to_compiled)
|
||||||
|
|
||||||
|
|
||||||
def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True):
|
def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True):
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from jedi.inference.base_value import ValueWrapper
|
from jedi.inference.base_value import ValueWrapper
|
||||||
from jedi.inference.value.module import ModuleValue
|
from jedi.inference.value.module import ModuleValue
|
||||||
from jedi.inference.filters import ParserTreeFilter, \
|
from jedi.inference.filters import ParserTreeFilter
|
||||||
TreeNameDefinition
|
from jedi.inference.names import StubName
|
||||||
from jedi.inference.gradual.typing import TypingModuleFilterWrapper
|
from jedi.inference.gradual.typing import TypingModuleFilterWrapper
|
||||||
from jedi.inference.context import ModuleContext
|
from jedi.inference.context import ModuleContext
|
||||||
|
|
||||||
@@ -81,17 +81,8 @@ class TypingModuleContext(ModuleContext):
|
|||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
|
||||||
# From here on down we make looking up the sys.version_info fast.
|
|
||||||
class _StubName(TreeNameDefinition):
|
|
||||||
def infer(self):
|
|
||||||
inferred = super(_StubName, self).infer()
|
|
||||||
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
|
|
||||||
return [VersionInfo(c) for c in inferred]
|
|
||||||
return inferred
|
|
||||||
|
|
||||||
|
|
||||||
class StubFilter(ParserTreeFilter):
|
class StubFilter(ParserTreeFilter):
|
||||||
name_class = _StubName
|
name_class = StubName
|
||||||
|
|
||||||
def _is_name_reachable(self, name):
|
def _is_name_reachable(self, name):
|
||||||
if not super(StubFilter, self)._is_name_reachable(name):
|
if not super(StubFilter, self)._is_name_reachable(name):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from abc import abstractmethod
|
|||||||
from parso.tree import search_ancestor
|
from parso.tree import search_ancestor
|
||||||
|
|
||||||
from jedi._compatibility import Parameter
|
from jedi._compatibility import Parameter
|
||||||
|
from jedi.parser_utils import clean_scope_docstring
|
||||||
from jedi.inference.utils import unite
|
from jedi.inference.utils import unite
|
||||||
from jedi.inference.base_value import ValueSet, NO_VALUES
|
from jedi.inference.base_value import ValueSet, NO_VALUES
|
||||||
from jedi.inference import docstrings
|
from jedi.inference import docstrings
|
||||||
@@ -10,6 +11,30 @@ from jedi.cache import memoize_method
|
|||||||
from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf
|
from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_name_docs(names):
|
||||||
|
doc = ''
|
||||||
|
for name in names:
|
||||||
|
if doc:
|
||||||
|
# In case we have multiple values, just return all of them
|
||||||
|
# separated by a few dashes.
|
||||||
|
doc += '\n' + '-' * 30 + '\n'
|
||||||
|
doc += name.py__doc__()
|
||||||
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_docs_and_signature(values, doc):
|
||||||
|
signature_text = '\n'.join(
|
||||||
|
signature.to_string()
|
||||||
|
for value in values
|
||||||
|
for signature in value.get_signatures()
|
||||||
|
)
|
||||||
|
|
||||||
|
if signature_text and doc:
|
||||||
|
return signature_text + '\n\n' + doc
|
||||||
|
else:
|
||||||
|
return signature_text + doc
|
||||||
|
|
||||||
|
|
||||||
class AbstractNameDefinition(object):
|
class AbstractNameDefinition(object):
|
||||||
start_pos = None
|
start_pos = None
|
||||||
string_name = None
|
string_name = None
|
||||||
@@ -59,6 +84,9 @@ class AbstractNameDefinition(object):
|
|||||||
def is_import(self):
|
def is_import(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
return ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
def api_type(self):
|
||||||
return self.parent_context.api_type
|
return self.parent_context.api_type
|
||||||
@@ -197,6 +225,20 @@ class ValueNameMixin(object):
|
|||||||
def infer(self):
|
def infer(self):
|
||||||
return ValueSet([self._value])
|
return ValueSet([self._value])
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
from jedi.inference.gradual.conversion import convert_names
|
||||||
|
doc = ''
|
||||||
|
if self._value.is_stub():
|
||||||
|
names = convert_names([self], prefer_stub_to_compiled=False)
|
||||||
|
if self not in names:
|
||||||
|
doc = _merge_name_docs(names)
|
||||||
|
if not doc:
|
||||||
|
doc = self._value.py__doc__()
|
||||||
|
|
||||||
|
if include_signatures:
|
||||||
|
doc = _merge_docs_and_signature([self._value], doc)
|
||||||
|
return doc
|
||||||
|
|
||||||
def _get_qualified_names(self):
|
def _get_qualified_names(self):
|
||||||
return self._value.get_qualified_names()
|
return self._value.get_qualified_names()
|
||||||
|
|
||||||
@@ -285,6 +327,17 @@ class TreeNameDefinition(AbstractTreeName):
|
|||||||
node = node.parent
|
node = node.parent
|
||||||
return indexes
|
return indexes
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
if self.api_type in ('function', 'class'):
|
||||||
|
return clean_scope_docstring(self.tree_name.get_definition())
|
||||||
|
|
||||||
|
if self.api_type == 'module':
|
||||||
|
names = self.goto()
|
||||||
|
if self not in names:
|
||||||
|
print('la', _merge_name_docs(names))
|
||||||
|
return _merge_name_docs(names)
|
||||||
|
return super(TreeNameDefinition, self).py__doc__(include_signatures)
|
||||||
|
|
||||||
|
|
||||||
class _ParamMixin(object):
|
class _ParamMixin(object):
|
||||||
def maybe_positional_argument(self, include_star=True):
|
def maybe_positional_argument(self, include_star=True):
|
||||||
@@ -531,6 +584,10 @@ class ImportName(AbstractNameDefinition):
|
|||||||
def api_type(self):
|
def api_type(self):
|
||||||
return 'module'
|
return 'module'
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
print('la', (self.goto()))
|
||||||
|
return _merge_name_docs(self.goto())
|
||||||
|
|
||||||
|
|
||||||
class SubModuleName(ImportName):
|
class SubModuleName(ImportName):
|
||||||
_level = 1
|
_level = 1
|
||||||
@@ -549,3 +606,26 @@ class NameWrapper(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
|
return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
|
||||||
|
|
||||||
|
|
||||||
|
# From here on down we make looking up the sys.version_info fast.
|
||||||
|
class StubName(TreeNameDefinition):
|
||||||
|
def infer(self):
|
||||||
|
inferred = super(StubName, self).infer()
|
||||||
|
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
|
||||||
|
from jedi.inference.gradual.stub_value import VersionInfo
|
||||||
|
return [VersionInfo(c) for c in inferred]
|
||||||
|
return inferred
|
||||||
|
|
||||||
|
def py__doc__(self, include_signatures=False):
|
||||||
|
from jedi.inference.gradual.conversion import convert_names
|
||||||
|
names = convert_names([self], prefer_stub_to_compiled=False)
|
||||||
|
if self in names:
|
||||||
|
doc = super(StubName, self).py__doc__(include_signatures)
|
||||||
|
else:
|
||||||
|
doc = _merge_name_docs(names)
|
||||||
|
if include_signatures:
|
||||||
|
parent = self.tree_name.parent
|
||||||
|
if parent.type in ('funcdef', 'classdef') and parent.name is self.tree_name:
|
||||||
|
doc = _merge_docs_and_signature(self.infer(), doc)
|
||||||
|
return doc
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
from parso.utils import PythonVersionInfo
|
from parso.utils import PythonVersionInfo
|
||||||
|
|
||||||
from jedi.inference.gradual import typeshed, stub_value
|
from jedi.inference.gradual import typeshed
|
||||||
from jedi.inference.value import TreeInstance, BoundMethod, FunctionValue, \
|
from jedi.inference.value import TreeInstance, BoundMethod, FunctionValue, \
|
||||||
MethodValue, ClassValue
|
MethodValue, ClassValue
|
||||||
|
from jedi.inference.names import StubName
|
||||||
|
|
||||||
TYPESHED_PYTHON3 = os.path.join(typeshed.TYPESHED_PATH, 'stdlib', '3')
|
TYPESHED_PYTHON3 = os.path.join(typeshed.TYPESHED_PATH, 'stdlib', '3')
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ def test_sys_getwindowsversion(Script, environment):
|
|||||||
def test_sys_hexversion(Script):
|
def test_sys_hexversion(Script):
|
||||||
script = Script('import sys; sys.hexversion')
|
script = Script('import sys; sys.hexversion')
|
||||||
def_, = script.complete()
|
def_, = script.complete()
|
||||||
assert isinstance(def_._name, stub_value._StubName), def_._name
|
assert isinstance(def_._name, StubName), def_._name
|
||||||
assert typeshed.TYPESHED_PATH in def_.module_path
|
assert typeshed.TYPESHED_PATH in def_.module_path
|
||||||
def_, = script.infer()
|
def_, = script.infer()
|
||||||
assert def_.name == 'int'
|
assert def_.name == 'int'
|
||||||
|
|||||||
Reference in New Issue
Block a user