mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Fix qualified names for CompiledObject
This commit is contained in:
@@ -279,9 +279,25 @@ class DirectObjectAccess(object):
|
||||
def is_class(self):
|
||||
return inspect.isclass(self._obj)
|
||||
|
||||
def is_module(self):
|
||||
return inspect.ismodule(self._obj)
|
||||
|
||||
def ismethoddescriptor(self):
|
||||
return inspect.ismethoddescriptor(self._obj)
|
||||
|
||||
def get_qualified_names(self):
|
||||
def try_to_get_name(obj):
|
||||
return getattr(obj, '__qualname__', getattr(obj, '__name__', None))
|
||||
|
||||
if self.is_module():
|
||||
return ()
|
||||
name = try_to_get_name(self._obj)
|
||||
if name is None:
|
||||
name = try_to_get_name(type(self._obj))
|
||||
if name is None:
|
||||
return ()
|
||||
return name.split('.')
|
||||
|
||||
def dir(self):
|
||||
return list(map(force_unicode, dir(self._obj)))
|
||||
|
||||
|
||||
@@ -86,11 +86,11 @@ class CompiledObject(Context):
|
||||
# For modules
|
||||
name = self.py__name__()
|
||||
if name is None:
|
||||
return []
|
||||
return ()
|
||||
return tuple(name.split('.'))
|
||||
|
||||
def get_qualified_names(self):
|
||||
return self.string_names
|
||||
return self.access_handle.get_qualified_names()
|
||||
|
||||
def py__bool__(self):
|
||||
return self.access_handle.py__bool__()
|
||||
@@ -101,6 +101,9 @@ class CompiledObject(Context):
|
||||
def is_class(self):
|
||||
return self.access_handle.is_class()
|
||||
|
||||
def is_module(self):
|
||||
return self.access_handle.is_module()
|
||||
|
||||
def is_compiled(self):
|
||||
return True
|
||||
|
||||
|
||||
@@ -81,13 +81,15 @@ class MixedName(compiled.CompiledName):
|
||||
default=None
|
||||
)
|
||||
assert len(access_paths)
|
||||
context = None
|
||||
contexts = [None]
|
||||
for access in access_paths:
|
||||
if context is None or isinstance(context, MixedObject):
|
||||
context = _create(self._evaluator, access, parent_context=context)
|
||||
else:
|
||||
context = create_cached_compiled_object(context.evaluator, access, context)
|
||||
return ContextSet([context])
|
||||
contexts = ContextSet.from_sets(
|
||||
_create(self._evaluator, access, parent_context=c)
|
||||
if c is None or isinstance(c, MixedObject)
|
||||
else ContextSet({create_cached_compiled_object(c.evaluator, access, c)})
|
||||
for c in contexts
|
||||
)
|
||||
return contexts
|
||||
|
||||
@property
|
||||
def api_type(self):
|
||||
@@ -211,35 +213,40 @@ def _create(evaluator, access_handle, parent_context, *args):
|
||||
# TODO use stub contexts here. If we do that we probably have to care about
|
||||
# generics from stuff like `[1]`.
|
||||
if result is None:
|
||||
return compiled_object
|
||||
|
||||
module_node, tree_node, file_io, code_lines = result
|
||||
|
||||
if parent_context is None:
|
||||
# TODO this __name__ is probably wrong.
|
||||
name = compiled_object.get_root_context().py__name__()
|
||||
string_names = tuple(name.split('.'))
|
||||
module_context = ModuleContext(
|
||||
evaluator, module_node,
|
||||
file_io=file_io,
|
||||
string_names=string_names,
|
||||
code_lines=code_lines,
|
||||
is_package=hasattr(compiled_object, 'py__path__'),
|
||||
)
|
||||
if name is not None:
|
||||
evaluator.module_cache.add(string_names, ContextSet([module_context]))
|
||||
return ContextSet({compiled_object})
|
||||
else:
|
||||
assert parent_context.tree_node.get_root_node() == module_node
|
||||
module_context = parent_context.get_root_context()
|
||||
module_node, tree_node, file_io, code_lines = result
|
||||
|
||||
tree_context = module_context.create_context(
|
||||
tree_node,
|
||||
node_is_context=True,
|
||||
node_is_object=True
|
||||
if parent_context is None:
|
||||
# TODO this __name__ is probably wrong.
|
||||
name = compiled_object.get_root_context().py__name__()
|
||||
string_names = tuple(name.split('.'))
|
||||
module_context = ModuleContext(
|
||||
evaluator, module_node,
|
||||
file_io=file_io,
|
||||
string_names=string_names,
|
||||
code_lines=code_lines,
|
||||
is_package=hasattr(compiled_object, 'py__path__'),
|
||||
)
|
||||
if name is not None:
|
||||
evaluator.module_cache.add(string_names, ContextSet([module_context]))
|
||||
else:
|
||||
assert parent_context.tree_node.get_root_node() == module_node
|
||||
module_context = parent_context.get_root_context()
|
||||
|
||||
tree_contexts = ContextSet({
|
||||
module_context.create_context(
|
||||
tree_node,
|
||||
node_is_context=True,
|
||||
node_is_object=True
|
||||
)
|
||||
})
|
||||
if tree_node.type == 'classdef':
|
||||
if not access_handle.is_class():
|
||||
# Is an instance, not a class.
|
||||
tree_contexts = tree_contexts.execute_evaluated()
|
||||
|
||||
return ContextSet(
|
||||
MixedObject(compiled_object, tree_context=tree_context)
|
||||
for tree_context in tree_contexts
|
||||
)
|
||||
if tree_node.type == 'classdef':
|
||||
if not access_handle.is_class():
|
||||
# Is an instance, not a class.
|
||||
tree_context, = execute_evaluated(tree_context)
|
||||
|
||||
return MixedObject(compiled_object, tree_context=tree_context)
|
||||
|
||||
@@ -38,7 +38,7 @@ def usages(module_context, tree_name):
|
||||
search_name = tree_name.value
|
||||
found_names = _find_names(module_context, tree_name)
|
||||
modules = set(d.get_root_context() for d in found_names.values())
|
||||
modules = set(m for m in modules if m.is_module())
|
||||
modules = set(m for m in modules if m.is_module() and not m.is_compiled())
|
||||
|
||||
non_matching_usage_maps = {}
|
||||
for m in imports.get_modules_containing_name(module_context.evaluator, modules, search_name):
|
||||
|
||||
@@ -110,7 +110,7 @@ class StdlibPlugin(BasePlugin):
|
||||
if context.parent_context == self._evaluator.builtins_module:
|
||||
module_name = 'builtins'
|
||||
elif context.parent_context is not None and context.parent_context.is_module():
|
||||
module_name = context.parent_context.name.string_name
|
||||
module_name = context.parent_context.py__name__()
|
||||
else:
|
||||
return callback(context, arguments=arguments)
|
||||
|
||||
|
||||
@@ -397,6 +397,9 @@ def test_sys_path_docstring(): # Was an issue in #1298
|
||||
('counter.setdefa', ['setdefault']),
|
||||
('counter.pop().imag', []), # TODO stubs could make this better
|
||||
('counter.keys())[0].uppe', []),
|
||||
|
||||
('string.upper().uppe', ['upper']),
|
||||
('"".upper().uppe', ['upper']),
|
||||
]
|
||||
)
|
||||
def test_simple_completions(code, completions):
|
||||
@@ -406,6 +409,7 @@ def test_simple_completions(code, completions):
|
||||
import collections
|
||||
deq = collections.deque([1])
|
||||
counter = collections.Counter(['asdf'])
|
||||
string = ''
|
||||
|
||||
defs = jedi.Interpreter(code, [locals()]).completions()
|
||||
assert [d.name for d in defs] == completions
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from textwrap import dedent
|
||||
import math
|
||||
import sys
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -107,6 +111,7 @@ def test_getitem_on_none(Script):
|
||||
def _return_int():
|
||||
return 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'attribute, expected_name, expected_parent', [
|
||||
('x', 'int', 'builtins'),
|
||||
@@ -119,9 +124,7 @@ def _return_int():
|
||||
]
|
||||
)
|
||||
def test_parent_context(same_process_evaluator, attribute, expected_name, expected_parent):
|
||||
import math
|
||||
import decimal
|
||||
import datetime
|
||||
|
||||
class C:
|
||||
x = 1
|
||||
@@ -129,7 +132,7 @@ def test_parent_context(same_process_evaluator, attribute, expected_name, expect
|
||||
z = True
|
||||
cos = math.cos
|
||||
dec = decimal.Decimal(1)
|
||||
dt = datetime.datetime(2000, 1, 1)
|
||||
dt = datetime(2000, 1, 1)
|
||||
ret_int = _return_int
|
||||
|
||||
o = compiled.CompiledObject(
|
||||
@@ -143,3 +146,27 @@ def test_parent_context(same_process_evaluator, attribute, expected_name, expect
|
||||
module_name = 'builtins' # Python 2
|
||||
assert module_name == expected_parent
|
||||
assert x.parent_context.parent_context is None
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info[0] == 2, reason="Ignore Python 2, because EOL")
|
||||
@pytest.mark.parametrize(
|
||||
'obj, expected_names', [
|
||||
('', ['str']),
|
||||
(str, ['str']),
|
||||
(''.upper, ['str', 'upper']),
|
||||
(str.upper, ['str', 'upper']),
|
||||
|
||||
(math.cos, ['cos']),
|
||||
|
||||
(Counter, ['Counter']),
|
||||
(Counter(""), ['Counter']),
|
||||
(Counter.most_common, ['Counter', 'most_common']),
|
||||
(Counter("").most_common, ['Counter', 'most_common']),
|
||||
]
|
||||
)
|
||||
def test_qualified_names(same_process_evaluator, obj, expected_names):
|
||||
o = compiled.CompiledObject(
|
||||
same_process_evaluator,
|
||||
DirectObjectAccess(same_process_evaluator, obj)
|
||||
)
|
||||
assert o.get_qualified_names() == expected_names
|
||||
|
||||
Reference in New Issue
Block a user