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):
|
def is_class(self):
|
||||||
return inspect.isclass(self._obj)
|
return inspect.isclass(self._obj)
|
||||||
|
|
||||||
|
def is_module(self):
|
||||||
|
return inspect.ismodule(self._obj)
|
||||||
|
|
||||||
def ismethoddescriptor(self):
|
def ismethoddescriptor(self):
|
||||||
return inspect.ismethoddescriptor(self._obj)
|
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):
|
def dir(self):
|
||||||
return list(map(force_unicode, dir(self._obj)))
|
return list(map(force_unicode, dir(self._obj)))
|
||||||
|
|
||||||
|
|||||||
@@ -86,11 +86,11 @@ class CompiledObject(Context):
|
|||||||
# For modules
|
# For modules
|
||||||
name = self.py__name__()
|
name = self.py__name__()
|
||||||
if name is None:
|
if name is None:
|
||||||
return []
|
return ()
|
||||||
return tuple(name.split('.'))
|
return tuple(name.split('.'))
|
||||||
|
|
||||||
def get_qualified_names(self):
|
def get_qualified_names(self):
|
||||||
return self.string_names
|
return self.access_handle.get_qualified_names()
|
||||||
|
|
||||||
def py__bool__(self):
|
def py__bool__(self):
|
||||||
return self.access_handle.py__bool__()
|
return self.access_handle.py__bool__()
|
||||||
@@ -101,6 +101,9 @@ class CompiledObject(Context):
|
|||||||
def is_class(self):
|
def is_class(self):
|
||||||
return self.access_handle.is_class()
|
return self.access_handle.is_class()
|
||||||
|
|
||||||
|
def is_module(self):
|
||||||
|
return self.access_handle.is_module()
|
||||||
|
|
||||||
def is_compiled(self):
|
def is_compiled(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,15 @@ class MixedName(compiled.CompiledName):
|
|||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
assert len(access_paths)
|
assert len(access_paths)
|
||||||
context = None
|
contexts = [None]
|
||||||
for access in access_paths:
|
for access in access_paths:
|
||||||
if context is None or isinstance(context, MixedObject):
|
contexts = ContextSet.from_sets(
|
||||||
context = _create(self._evaluator, access, parent_context=context)
|
_create(self._evaluator, access, parent_context=c)
|
||||||
else:
|
if c is None or isinstance(c, MixedObject)
|
||||||
context = create_cached_compiled_object(context.evaluator, access, context)
|
else ContextSet({create_cached_compiled_object(c.evaluator, access, c)})
|
||||||
return ContextSet([context])
|
for c in contexts
|
||||||
|
)
|
||||||
|
return contexts
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
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
|
# TODO use stub contexts here. If we do that we probably have to care about
|
||||||
# generics from stuff like `[1]`.
|
# generics from stuff like `[1]`.
|
||||||
if result is None:
|
if result is None:
|
||||||
return compiled_object
|
return ContextSet({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]))
|
|
||||||
else:
|
else:
|
||||||
assert parent_context.tree_node.get_root_node() == module_node
|
module_node, tree_node, file_io, code_lines = result
|
||||||
module_context = parent_context.get_root_context()
|
|
||||||
|
|
||||||
tree_context = module_context.create_context(
|
if parent_context is None:
|
||||||
tree_node,
|
# TODO this __name__ is probably wrong.
|
||||||
node_is_context=True,
|
name = compiled_object.get_root_context().py__name__()
|
||||||
node_is_object=True
|
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
|
search_name = tree_name.value
|
||||||
found_names = _find_names(module_context, tree_name)
|
found_names = _find_names(module_context, tree_name)
|
||||||
modules = set(d.get_root_context() for d in found_names.values())
|
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 = {}
|
non_matching_usage_maps = {}
|
||||||
for m in imports.get_modules_containing_name(module_context.evaluator, modules, search_name):
|
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:
|
if context.parent_context == self._evaluator.builtins_module:
|
||||||
module_name = 'builtins'
|
module_name = 'builtins'
|
||||||
elif context.parent_context is not None and context.parent_context.is_module():
|
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:
|
else:
|
||||||
return callback(context, arguments=arguments)
|
return callback(context, arguments=arguments)
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,9 @@ def test_sys_path_docstring(): # Was an issue in #1298
|
|||||||
('counter.setdefa', ['setdefault']),
|
('counter.setdefa', ['setdefault']),
|
||||||
('counter.pop().imag', []), # TODO stubs could make this better
|
('counter.pop().imag', []), # TODO stubs could make this better
|
||||||
('counter.keys())[0].uppe', []),
|
('counter.keys())[0].uppe', []),
|
||||||
|
|
||||||
|
('string.upper().uppe', ['upper']),
|
||||||
|
('"".upper().uppe', ['upper']),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_simple_completions(code, completions):
|
def test_simple_completions(code, completions):
|
||||||
@@ -406,6 +409,7 @@ def test_simple_completions(code, completions):
|
|||||||
import collections
|
import collections
|
||||||
deq = collections.deque([1])
|
deq = collections.deque([1])
|
||||||
counter = collections.Counter(['asdf'])
|
counter = collections.Counter(['asdf'])
|
||||||
|
string = ''
|
||||||
|
|
||||||
defs = jedi.Interpreter(code, [locals()]).completions()
|
defs = jedi.Interpreter(code, [locals()]).completions()
|
||||||
assert [d.name for d in defs] == completions
|
assert [d.name for d in defs] == completions
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
from collections import Counter
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -107,6 +111,7 @@ def test_getitem_on_none(Script):
|
|||||||
def _return_int():
|
def _return_int():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'attribute, expected_name, expected_parent', [
|
'attribute, expected_name, expected_parent', [
|
||||||
('x', 'int', 'builtins'),
|
('x', 'int', 'builtins'),
|
||||||
@@ -119,9 +124,7 @@ def _return_int():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_parent_context(same_process_evaluator, attribute, expected_name, expected_parent):
|
def test_parent_context(same_process_evaluator, attribute, expected_name, expected_parent):
|
||||||
import math
|
|
||||||
import decimal
|
import decimal
|
||||||
import datetime
|
|
||||||
|
|
||||||
class C:
|
class C:
|
||||||
x = 1
|
x = 1
|
||||||
@@ -129,7 +132,7 @@ def test_parent_context(same_process_evaluator, attribute, expected_name, expect
|
|||||||
z = True
|
z = True
|
||||||
cos = math.cos
|
cos = math.cos
|
||||||
dec = decimal.Decimal(1)
|
dec = decimal.Decimal(1)
|
||||||
dt = datetime.datetime(2000, 1, 1)
|
dt = datetime(2000, 1, 1)
|
||||||
ret_int = _return_int
|
ret_int = _return_int
|
||||||
|
|
||||||
o = compiled.CompiledObject(
|
o = compiled.CompiledObject(
|
||||||
@@ -143,3 +146,27 @@ def test_parent_context(same_process_evaluator, attribute, expected_name, expect
|
|||||||
module_name = 'builtins' # Python 2
|
module_name = 'builtins' # Python 2
|
||||||
assert module_name == expected_parent
|
assert module_name == expected_parent
|
||||||
assert x.parent_context.parent_context is None
|
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