mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-17 19:15:55 +08:00
Merge branch 'django'
This commit is contained in:
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -3,4 +3,4 @@
|
|||||||
url = https://github.com/davidhalter/typeshed.git
|
url = https://github.com/davidhalter/typeshed.git
|
||||||
[submodule "jedi/third_party/django-stubs"]
|
[submodule "jedi/third_party/django-stubs"]
|
||||||
path = jedi/third_party/django-stubs
|
path = jedi/third_party/django-stubs
|
||||||
url = https://github.com/typeddjango/django-stubs
|
url = https://github.com/davidhalter/django-stubs
|
||||||
|
|||||||
@@ -244,6 +244,9 @@ class Value(HelperValueMixin):
|
|||||||
debug.warning("No __get__ defined on %s", self)
|
debug.warning("No __get__ defined on %s", self)
|
||||||
return ValueSet([self])
|
return ValueSet([self])
|
||||||
|
|
||||||
|
def py__get__on_class(self, calling_instance, instance, class_value):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
def get_qualified_names(self):
|
def get_qualified_names(self):
|
||||||
# Returns Optional[Tuple[str, ...]]
|
# Returns Optional[Tuple[str, ...]]
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class _BuiltinMappedMethod(ValueWrapper):
|
|||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
# TODO add TypeError if params are given/or not correct.
|
# TODO add TypeError if params are given/or not correct.
|
||||||
return self._method(self._value)
|
return self._method(self._value, arguments)
|
||||||
|
|
||||||
|
|
||||||
class SpecialMethodFilter(DictFilter):
|
class SpecialMethodFilter(DictFilter):
|
||||||
@@ -330,7 +330,7 @@ class _AttributeOverwriteMixin(object):
|
|||||||
def get_filters(self, *args, **kwargs):
|
def get_filters(self, *args, **kwargs):
|
||||||
yield SpecialMethodFilter(self, self.overwritten_methods, self._wrapped_value)
|
yield SpecialMethodFilter(self, self.overwritten_methods, self._wrapped_value)
|
||||||
|
|
||||||
for filter in self._wrapped_value.get_filters():
|
for filter in self._wrapped_value.get_filters(*args, **kwargs):
|
||||||
yield filter
|
yield filter
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class DefineGenericBaseClass(LazyValueWrapper):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GenericClass(ClassMixin, DefineGenericBaseClass):
|
class GenericClass(DefineGenericBaseClass, ClassMixin):
|
||||||
"""
|
"""
|
||||||
A class that is defined with generics, might be something simple like:
|
A class that is defined with generics, might be something simple like:
|
||||||
|
|
||||||
@@ -205,6 +205,9 @@ class GenericClass(ClassMixin, DefineGenericBaseClass):
|
|||||||
return True
|
return True
|
||||||
return self._class_value.is_sub_class_of(class_value)
|
return self._class_value.is_sub_class_of(class_value)
|
||||||
|
|
||||||
|
def with_generics(self, generics_tuple):
|
||||||
|
return self._class_value.with_generics(generics_tuple)
|
||||||
|
|
||||||
def infer_type_vars(self, value_set):
|
def infer_type_vars(self, value_set):
|
||||||
# Circular
|
# Circular
|
||||||
from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
|
from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
|
||||||
@@ -292,6 +295,9 @@ class _LazyGenericBaseClass(object):
|
|||||||
new |= ValueSet([type_var])
|
new |= ValueSet([type_var])
|
||||||
yield new
|
yield new
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class)
|
||||||
|
|
||||||
|
|
||||||
class _GenericInstanceWrapper(ValueWrapper):
|
class _GenericInstanceWrapper(ValueWrapper):
|
||||||
def py__stop_iteration_returns(self):
|
def py__stop_iteration_returns(self):
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ class _TypingClassMixin(ClassMixin):
|
|||||||
return ValueName(self, self._tree_name)
|
return ValueName(self, self._tree_name)
|
||||||
|
|
||||||
|
|
||||||
class TypingClassWithGenerics(_TypingClassMixin, ProxyWithGenerics):
|
class TypingClassWithGenerics(ProxyWithGenerics, _TypingClassMixin):
|
||||||
def infer_type_vars(self, value_set):
|
def infer_type_vars(self, value_set):
|
||||||
type_var_dict = {}
|
type_var_dict = {}
|
||||||
annotation_generics = self.get_generics()
|
annotation_generics = self.get_generics()
|
||||||
@@ -240,7 +240,7 @@ class TypingClassWithGenerics(_TypingClassMixin, ProxyWithGenerics):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProxyTypingClassValue(_TypingClassMixin, ProxyTypingValue):
|
class ProxyTypingClassValue(ProxyTypingValue, _TypingClassMixin):
|
||||||
index_class = TypingClassWithGenerics
|
index_class = TypingClassWithGenerics
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -288,6 +288,11 @@ class _BaseTreeInstance(AbstractInstanceValue):
|
|||||||
"""
|
"""
|
||||||
# Arguments in __get__ descriptors are obj, class.
|
# Arguments in __get__ descriptors are obj, class.
|
||||||
# `method` is the new parent of the array, don't know if that's good.
|
# `method` is the new parent of the array, don't know if that's good.
|
||||||
|
for cls in self.class_value.py__mro__():
|
||||||
|
result = cls.py__get__on_class(self, instance, class_value)
|
||||||
|
if result is not NotImplemented:
|
||||||
|
return result
|
||||||
|
|
||||||
names = self.get_function_slot_names(u'__get__')
|
names = self.get_function_slot_names(u'__get__')
|
||||||
if names:
|
if names:
|
||||||
if instance is None:
|
if instance is None:
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@publish_method('__iter__')
|
@publish_method('__iter__')
|
||||||
def py__iter__(self, contextualized_node=None):
|
def _iter(self, arguments):
|
||||||
return ValueSet([self])
|
return ValueSet([self])
|
||||||
|
|
||||||
@publish_method('send')
|
@publish_method('send')
|
||||||
@publish_method('next', python_version_match=2)
|
@publish_method('next', python_version_match=2)
|
||||||
@publish_method('__next__', python_version_match=3)
|
@publish_method('__next__', python_version_match=3)
|
||||||
def py__next__(self):
|
def py__next__(self, arguments):
|
||||||
return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
|
return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
|
||||||
|
|
||||||
def py__stop_iteration_returns(self):
|
def py__stop_iteration_returns(self):
|
||||||
@@ -290,12 +290,12 @@ class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin):
|
|||||||
return ValueSet.from_sets(values for keys, values in self._iterate())
|
return ValueSet.from_sets(values for keys, values in self._iterate())
|
||||||
|
|
||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self, arguments):
|
||||||
lazy_value = LazyKnownValues(self._dict_values())
|
lazy_value = LazyKnownValues(self._dict_values())
|
||||||
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
||||||
|
|
||||||
@publish_method('items')
|
@publish_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self, arguments):
|
||||||
lazy_values = [
|
lazy_values = [
|
||||||
LazyKnownValue(
|
LazyKnownValue(
|
||||||
FakeTuple(
|
FakeTuple(
|
||||||
@@ -457,12 +457,12 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin):
|
|||||||
yield LazyKnownValues(types)
|
yield LazyKnownValues(types)
|
||||||
|
|
||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self, arguments):
|
||||||
lazy_value = LazyKnownValues(self._dict_values())
|
lazy_value = LazyKnownValues(self._dict_values())
|
||||||
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
||||||
|
|
||||||
@publish_method('items')
|
@publish_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self, arguments):
|
||||||
lazy_values = [
|
lazy_values = [
|
||||||
LazyKnownValue(FakeTuple(
|
LazyKnownValue(FakeTuple(
|
||||||
self.inference_state,
|
self.inference_state,
|
||||||
@@ -552,7 +552,7 @@ class FakeDict(_DictMixin, Sequence, _DictKeyMixin):
|
|||||||
return lazy_value.infer()
|
return lazy_value.infer()
|
||||||
|
|
||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _values(self):
|
def _values(self, arguments):
|
||||||
return ValueSet([FakeTuple(
|
return ValueSet([FakeTuple(
|
||||||
self.inference_state,
|
self.inference_state,
|
||||||
[LazyKnownValues(self._dict_values())]
|
[LazyKnownValues(self._dict_values())]
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ class ClassFilter(ParserTreeFilter):
|
|||||||
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
||||||
annassign = expr_stmt.children[1]
|
annassign = expr_stmt.children[1]
|
||||||
if annassign.type == 'annassign':
|
if annassign.type == 'annassign':
|
||||||
# TODO this is not proper matching
|
|
||||||
|
|
||||||
# If there is an =, the variable is obviously also
|
# If there is an =, the variable is obviously also
|
||||||
# defined on the class.
|
# defined on the class.
|
||||||
if 'ClassVar' not in annassign.children[1].get_code() \
|
if 'ClassVar' not in annassign.children[1].get_code() \
|
||||||
@@ -138,7 +136,7 @@ class ClassMixin(object):
|
|||||||
def is_class_mixin(self):
|
def is_class_mixin(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def py__call__(self, arguments=None):
|
def py__call__(self, arguments):
|
||||||
from jedi.inference.value import TreeInstance
|
from jedi.inference.value import TreeInstance
|
||||||
|
|
||||||
from jedi.inference.gradual.typing import TypedDict
|
from jedi.inference.gradual.typing import TypedDict
|
||||||
@@ -189,12 +187,13 @@ class ClassMixin(object):
|
|||||||
mro.append(cls_new)
|
mro.append(cls_new)
|
||||||
yield cls_new
|
yield cls_new
|
||||||
|
|
||||||
def get_filters(self, origin_scope=None, is_instance=False, include_metaclasses=True):
|
def get_filters(self, origin_scope=None, is_instance=False,
|
||||||
|
include_metaclasses=True, include_type_when_class=True):
|
||||||
if include_metaclasses:
|
if include_metaclasses:
|
||||||
metaclasses = self.get_metaclasses()
|
metaclasses = self.get_metaclasses()
|
||||||
if metaclasses:
|
if metaclasses:
|
||||||
for f in self.get_metaclass_filters(metaclasses):
|
for f in self.get_metaclass_filters(metaclasses, is_instance):
|
||||||
yield f
|
yield f # Python 2..
|
||||||
|
|
||||||
for cls in self.py__mro__():
|
for cls in self.py__mro__():
|
||||||
if cls.is_compiled():
|
if cls.is_compiled():
|
||||||
@@ -206,7 +205,7 @@ class ClassMixin(object):
|
|||||||
origin_scope=origin_scope,
|
origin_scope=origin_scope,
|
||||||
is_instance=is_instance
|
is_instance=is_instance
|
||||||
)
|
)
|
||||||
if not is_instance:
|
if not is_instance and include_type_when_class:
|
||||||
from jedi.inference.compiled import builtin_from_name
|
from jedi.inference.compiled import builtin_from_name
|
||||||
type_ = builtin_from_name(self.inference_state, u'type')
|
type_ = builtin_from_name(self.inference_state, u'type')
|
||||||
assert isinstance(type_, ClassValue)
|
assert isinstance(type_, ClassValue)
|
||||||
@@ -228,6 +227,11 @@ class ClassMixin(object):
|
|||||||
# Since calling staticmethod without a function is illegal, the Jedi
|
# Since calling staticmethod without a function is illegal, the Jedi
|
||||||
# plugin doesn't return anything. Therefore call directly and get what
|
# plugin doesn't return anything. Therefore call directly and get what
|
||||||
# we want: An instance of staticmethod.
|
# we want: An instance of staticmethod.
|
||||||
|
metaclasses = self.get_metaclasses()
|
||||||
|
if metaclasses:
|
||||||
|
sigs = self.get_metaclass_signatures(metaclasses)
|
||||||
|
if sigs:
|
||||||
|
return sigs
|
||||||
args = ValuesArguments([])
|
args = ValuesArguments([])
|
||||||
init_funcs = self.py__call__(args).py__getattribute__('__init__')
|
init_funcs = self.py__call__(args).py__getattribute__('__init__')
|
||||||
return [sig.bind(self) for sig in init_funcs.get_signatures()]
|
return [sig.bind(self) for sig in init_funcs.get_signatures()]
|
||||||
@@ -270,50 +274,6 @@ class ClassMixin(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)):
|
|
||||||
api_type = u'class'
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
|
||||||
def list_type_vars(self):
|
|
||||||
found = []
|
|
||||||
arglist = self.tree_node.get_super_arglist()
|
|
||||||
if arglist is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
for stars, node in unpack_arglist(arglist):
|
|
||||||
if stars:
|
|
||||||
continue # These are not relevant for this search.
|
|
||||||
|
|
||||||
from jedi.inference.gradual.annotation import find_unknown_type_vars
|
|
||||||
for type_var in find_unknown_type_vars(self.parent_context, node):
|
|
||||||
if type_var not in found:
|
|
||||||
# The order matters and it's therefore a list.
|
|
||||||
found.append(type_var)
|
|
||||||
return found
|
|
||||||
|
|
||||||
def _get_bases_arguments(self):
|
|
||||||
arglist = self.tree_node.get_super_arglist()
|
|
||||||
if arglist:
|
|
||||||
from jedi.inference import arguments
|
|
||||||
return arguments.TreeArguments(self.inference_state, self.parent_context, arglist)
|
|
||||||
return None
|
|
||||||
|
|
||||||
@inference_state_method_cache(default=())
|
|
||||||
def py__bases__(self):
|
|
||||||
args = self._get_bases_arguments()
|
|
||||||
if args is not None:
|
|
||||||
lst = [value for key, value in args.unpack() if key is None]
|
|
||||||
if lst:
|
|
||||||
return lst
|
|
||||||
|
|
||||||
if self.py__name__() == 'object' \
|
|
||||||
and self.parent_context.is_builtins_module():
|
|
||||||
return []
|
|
||||||
return [LazyKnownValues(
|
|
||||||
self.inference_state.builtins_module.py__getattribute__('object')
|
|
||||||
)]
|
|
||||||
|
|
||||||
def py__getitem__(self, index_value_set, contextualized_node):
|
def py__getitem__(self, index_value_set, contextualized_node):
|
||||||
from jedi.inference.gradual.base import GenericClass
|
from jedi.inference.gradual.base import GenericClass
|
||||||
if not index_value_set:
|
if not index_value_set:
|
||||||
@@ -360,9 +320,53 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase
|
|||||||
)])
|
)])
|
||||||
return ValueSet({self})
|
return ValueSet({self})
|
||||||
|
|
||||||
|
|
||||||
|
class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)):
|
||||||
|
api_type = u'class'
|
||||||
|
|
||||||
|
@inference_state_method_cache()
|
||||||
|
def list_type_vars(self):
|
||||||
|
found = []
|
||||||
|
arglist = self.tree_node.get_super_arglist()
|
||||||
|
if arglist is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
for stars, node in unpack_arglist(arglist):
|
||||||
|
if stars:
|
||||||
|
continue # These are not relevant for this search.
|
||||||
|
|
||||||
|
from jedi.inference.gradual.annotation import find_unknown_type_vars
|
||||||
|
for type_var in find_unknown_type_vars(self.parent_context, node):
|
||||||
|
if type_var not in found:
|
||||||
|
# The order matters and it's therefore a list.
|
||||||
|
found.append(type_var)
|
||||||
|
return found
|
||||||
|
|
||||||
|
def _get_bases_arguments(self):
|
||||||
|
arglist = self.tree_node.get_super_arglist()
|
||||||
|
if arglist:
|
||||||
|
from jedi.inference import arguments
|
||||||
|
return arguments.TreeArguments(self.inference_state, self.parent_context, arglist)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@inference_state_method_cache(default=())
|
||||||
|
def py__bases__(self):
|
||||||
|
args = self._get_bases_arguments()
|
||||||
|
if args is not None:
|
||||||
|
lst = [value for key, value in args.unpack() if key is None]
|
||||||
|
if lst:
|
||||||
|
return lst
|
||||||
|
|
||||||
|
if self.py__name__() == 'object' \
|
||||||
|
and self.parent_context.is_builtins_module():
|
||||||
|
return []
|
||||||
|
return [LazyKnownValues(
|
||||||
|
self.inference_state.builtins_module.py__getattribute__('object')
|
||||||
|
)]
|
||||||
|
|
||||||
@plugin_manager.decorate()
|
@plugin_manager.decorate()
|
||||||
def get_metaclass_filters(self, metaclass):
|
def get_metaclass_filters(self, metaclasses, is_instance):
|
||||||
debug.dbg('Unprocessed metaclass %s', metaclass)
|
debug.warning('Unprocessed metaclass %s', metaclasses)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@inference_state_method_cache(default=NO_VALUES)
|
@inference_state_method_cache(default=NO_VALUES)
|
||||||
@@ -382,3 +386,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase
|
|||||||
if values:
|
if values:
|
||||||
return values
|
return values
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
|
@plugin_manager.decorate()
|
||||||
|
def get_metaclass_signatures(self, metaclasses):
|
||||||
|
return []
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
Module is used to infer Django model fields.
|
Module is used to infer Django model fields.
|
||||||
"""
|
"""
|
||||||
|
from jedi._compatibility import Parameter
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.inference.base_value import ValueSet, iterator_to_value_set
|
from jedi.inference.cache import inference_state_function_cache
|
||||||
from jedi.inference.filters import ParserTreeFilter, DictFilter
|
from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper
|
||||||
from jedi.inference.names import NameWrapper
|
from jedi.inference.filters import DictFilter, AttributeOverwrite
|
||||||
|
from jedi.inference.names import NameWrapper, BaseTreeParamName
|
||||||
|
from jedi.inference.compiled.value import EmptyCompiledName
|
||||||
from jedi.inference.value.instance import TreeInstance
|
from jedi.inference.value.instance import TreeInstance
|
||||||
|
from jedi.inference.value.klass import ClassMixin
|
||||||
from jedi.inference.gradual.base import GenericClass
|
from jedi.inference.gradual.base import GenericClass
|
||||||
from jedi.inference.gradual.generics import TupleGenericManager
|
from jedi.inference.gradual.generics import TupleGenericManager
|
||||||
|
from jedi.inference.signature import AbstractSignature
|
||||||
|
|
||||||
|
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -31,13 +36,26 @@ mapping = {
|
|||||||
'UUIDField': ('uuid', 'UUID'),
|
'UUIDField': ('uuid', 'UUID'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FILTER_LIKE_METHODS = ('create', 'filter', 'exclude', 'update', 'get',
|
||||||
|
'get_or_create', 'update_or_create')
|
||||||
|
|
||||||
def _infer_scalar_field(inference_state, field_name, field_tree_instance):
|
|
||||||
|
@inference_state_function_cache()
|
||||||
|
def _get_deferred_attributes(inference_state):
|
||||||
|
return inference_state.import_module(
|
||||||
|
('django', 'db', 'models', 'query_utils')
|
||||||
|
).py__getattribute__('DeferredAttribute').execute_annotation()
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_scalar_field(inference_state, field_name, field_tree_instance, is_instance):
|
||||||
try:
|
try:
|
||||||
module_name, attribute_name = mapping[field_tree_instance.py__name__()]
|
module_name, attribute_name = mapping[field_tree_instance.py__name__()]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if not is_instance:
|
||||||
|
return _get_deferred_attributes(inference_state)
|
||||||
|
|
||||||
if module_name is None:
|
if module_name is None:
|
||||||
module = inference_state.builtins_module
|
module = inference_state.builtins_module
|
||||||
else:
|
else:
|
||||||
@@ -65,16 +83,21 @@ def _get_foreign_key_values(cls, field_tree_instance):
|
|||||||
yield value
|
yield value
|
||||||
|
|
||||||
|
|
||||||
def _infer_field(cls, field_name):
|
def _infer_field(cls, field_name, is_instance):
|
||||||
inference_state = cls.inference_state
|
inference_state = cls.inference_state
|
||||||
for field_tree_instance in field_name.infer():
|
result = field_name.infer()
|
||||||
scalar_field = _infer_scalar_field(inference_state, field_name, field_tree_instance)
|
for field_tree_instance in result:
|
||||||
|
scalar_field = _infer_scalar_field(
|
||||||
|
inference_state, field_name, field_tree_instance, is_instance)
|
||||||
if scalar_field is not None:
|
if scalar_field is not None:
|
||||||
return scalar_field
|
return scalar_field
|
||||||
|
|
||||||
name = field_tree_instance.py__name__()
|
name = field_tree_instance.py__name__()
|
||||||
is_many_to_many = name == 'ManyToManyField'
|
is_many_to_many = name == 'ManyToManyField'
|
||||||
if name in ('ForeignKey', 'OneToOneField') or is_many_to_many:
|
if name in ('ForeignKey', 'OneToOneField') or is_many_to_many:
|
||||||
|
if not is_instance:
|
||||||
|
return _get_deferred_attributes(inference_state)
|
||||||
|
|
||||||
values = _get_foreign_key_values(cls, field_tree_instance)
|
values = _get_foreign_key_values(cls, field_tree_instance)
|
||||||
if is_many_to_many:
|
if is_many_to_many:
|
||||||
return ValueSet(filter(None, [
|
return ValueSet(filter(None, [
|
||||||
@@ -85,16 +108,17 @@ def _infer_field(cls, field_name):
|
|||||||
|
|
||||||
debug.dbg('django plugin: fail to infer `%s` from class `%s`',
|
debug.dbg('django plugin: fail to infer `%s` from class `%s`',
|
||||||
field_name.string_name, cls.py__name__())
|
field_name.string_name, cls.py__name__())
|
||||||
return field_name.infer()
|
return result
|
||||||
|
|
||||||
|
|
||||||
class DjangoModelName(NameWrapper):
|
class DjangoModelName(NameWrapper):
|
||||||
def __init__(self, cls, name):
|
def __init__(self, cls, name, is_instance):
|
||||||
super(DjangoModelName, self).__init__(name)
|
super(DjangoModelName, self).__init__(name)
|
||||||
self._cls = cls
|
self._cls = cls
|
||||||
|
self._is_instance = is_instance
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return _infer_field(self._cls, self._wrapped_name)
|
return _infer_field(self._cls, self._wrapped_name, self._is_instance)
|
||||||
|
|
||||||
|
|
||||||
def _create_manager_for(cls, manager_cls='BaseManager'):
|
def _create_manager_for(cls, manager_cls='BaseManager'):
|
||||||
@@ -109,25 +133,163 @@ def _create_manager_for(cls, manager_cls='BaseManager'):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _new_dict_filter(cls):
|
def _new_dict_filter(cls, is_instance):
|
||||||
filters = cls.get_filters(is_instance=True, include_metaclasses=False)
|
filters = list(cls.get_filters(
|
||||||
|
is_instance=is_instance,
|
||||||
|
include_metaclasses=False,
|
||||||
|
include_type_when_class=False)
|
||||||
|
)
|
||||||
dct = {
|
dct = {
|
||||||
name.string_name: DjangoModelName(cls, name)
|
name.string_name: DjangoModelName(cls, name, is_instance)
|
||||||
for filter_ in reversed(list(filters))
|
for filter_ in reversed(filters)
|
||||||
for name in filter_.values()
|
for name in filter_.values()
|
||||||
}
|
}
|
||||||
manager = _create_manager_for(cls)
|
if is_instance:
|
||||||
if manager:
|
# Replace the objects with a name that amounts to nothing when accessed
|
||||||
dct['objects'] = manager.name
|
# in an instance. This is not perfect and still completes "objects" in
|
||||||
|
# that case, but it at least not inferes stuff like `.objects.filter`.
|
||||||
|
# It would be nicer to do that in a better way, so that it also doesn't
|
||||||
|
# show up in completions, but it's probably just not worth doing that
|
||||||
|
# for the extra amount of work.
|
||||||
|
dct['objects'] = EmptyCompiledName(cls.inference_state, 'objects')
|
||||||
|
|
||||||
return DictFilter(dct)
|
return DictFilter(dct)
|
||||||
|
|
||||||
|
|
||||||
|
def is_django_model_base(value):
|
||||||
|
return value.py__name__() == 'ModelBase' \
|
||||||
|
and value.get_root_context().py__name__() == 'django.db.models.base'
|
||||||
|
|
||||||
|
|
||||||
def get_metaclass_filters(func):
|
def get_metaclass_filters(func):
|
||||||
|
def wrapper(cls, metaclasses, is_instance):
|
||||||
|
for metaclass in metaclasses:
|
||||||
|
if is_django_model_base(metaclass):
|
||||||
|
return [_new_dict_filter(cls, is_instance)]
|
||||||
|
|
||||||
|
return func(cls, metaclasses, is_instance)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def tree_name_to_values(func):
|
||||||
|
def wrapper(inference_state, context, tree_name):
|
||||||
|
result = func(inference_state, context, tree_name)
|
||||||
|
if tree_name.value in _FILTER_LIKE_METHODS:
|
||||||
|
# Here we try to overwrite stuff like User.objects.filter. We need
|
||||||
|
# this to make sure that keyword param completion works on these
|
||||||
|
# kind of methods.
|
||||||
|
for v in result:
|
||||||
|
if v.get_qualified_names() == ('_BaseQuerySet', tree_name.value) \
|
||||||
|
and v.parent_context.is_module() \
|
||||||
|
and v.parent_context.py__name__() == 'django.db.models.query':
|
||||||
|
qs = context.get_value()
|
||||||
|
generics = qs.get_generics()
|
||||||
|
if len(generics) >= 1:
|
||||||
|
return ValueSet(QuerySetMethodWrapper(v, model)
|
||||||
|
for model in generics[0])
|
||||||
|
|
||||||
|
elif tree_name.value == 'BaseManager' and context.is_module() \
|
||||||
|
and context.py__name__() == 'django.db.models.manager':
|
||||||
|
return ValueSet(ManagerWrapper(r) for r in result)
|
||||||
|
|
||||||
|
elif tree_name.value == 'Field' and context.is_module() \
|
||||||
|
and context.py__name__() == 'django.db.models.fields':
|
||||||
|
return ValueSet(FieldWrapper(r) for r in result)
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def _find_fields(cls):
|
||||||
|
for name in _new_dict_filter(cls, is_instance=False).values():
|
||||||
|
for value in name.infer():
|
||||||
|
if value.name.get_qualified_names(include_module_names=True) \
|
||||||
|
== ('django', 'db', 'models', 'query_utils', 'DeferredAttribute'):
|
||||||
|
yield name
|
||||||
|
|
||||||
|
|
||||||
|
def _get_signatures(cls):
|
||||||
|
return [DjangoModelSignature(cls, field_names=list(_find_fields(cls)))]
|
||||||
|
|
||||||
|
|
||||||
|
def get_metaclass_signatures(func):
|
||||||
def wrapper(cls, metaclasses):
|
def wrapper(cls, metaclasses):
|
||||||
for metaclass in metaclasses:
|
for metaclass in metaclasses:
|
||||||
if metaclass.py__name__() == 'ModelBase' \
|
if is_django_model_base(metaclass):
|
||||||
and metaclass.get_root_context().py__name__() == 'django.db.models.base':
|
return _get_signatures(cls)
|
||||||
return [_new_dict_filter(cls)]
|
return func(cls, metaclass)
|
||||||
|
|
||||||
return func(cls, metaclasses)
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class ManagerWrapper(ValueWrapper):
|
||||||
|
def py__getitem__(self, index_value_set, contextualized_node):
|
||||||
|
return ValueSet(
|
||||||
|
GenericManagerWrapper(generic)
|
||||||
|
for generic in self._wrapped_value.py__getitem__(
|
||||||
|
index_value_set, contextualized_node)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GenericManagerWrapper(AttributeOverwrite, ClassMixin):
|
||||||
|
def py__get__on_class(self, calling_instance, instance, class_value):
|
||||||
|
return calling_instance.class_value.with_generics(
|
||||||
|
(ValueSet({class_value}),)
|
||||||
|
).py__call__(calling_instance._arguments)
|
||||||
|
|
||||||
|
def with_generics(self, generics_tuple):
|
||||||
|
return self._wrapped_value.with_generics(generics_tuple)
|
||||||
|
|
||||||
|
|
||||||
|
class FieldWrapper(ValueWrapper):
|
||||||
|
def py__getitem__(self, index_value_set, contextualized_node):
|
||||||
|
return ValueSet(
|
||||||
|
GenericFieldWrapper(generic)
|
||||||
|
for generic in self._wrapped_value.py__getitem__(
|
||||||
|
index_value_set, contextualized_node)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GenericFieldWrapper(AttributeOverwrite, ClassMixin):
|
||||||
|
def py__get__on_class(self, calling_instance, instance, class_value):
|
||||||
|
# This is mostly an optimization to avoid Jedi aborting inference,
|
||||||
|
# because of too many function executions of Field.__get__.
|
||||||
|
return ValueSet({calling_instance})
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoModelSignature(AbstractSignature):
|
||||||
|
def __init__(self, value, field_names):
|
||||||
|
super(DjangoModelSignature, self).__init__(value)
|
||||||
|
self._field_names = field_names
|
||||||
|
|
||||||
|
def get_param_names(self, resolve_stars=False):
|
||||||
|
return [DjangoParamName(name) for name in self._field_names]
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoParamName(BaseTreeParamName):
|
||||||
|
def __init__(self, field_name):
|
||||||
|
super(DjangoParamName, self).__init__(field_name.parent_context, field_name.tree_name)
|
||||||
|
self._field_name = field_name
|
||||||
|
|
||||||
|
def get_kind(self):
|
||||||
|
return Parameter.KEYWORD_ONLY
|
||||||
|
|
||||||
|
def infer(self):
|
||||||
|
return self._field_name.infer()
|
||||||
|
|
||||||
|
|
||||||
|
class QuerySetMethodWrapper(ValueWrapper):
|
||||||
|
def __init__(self, method, model_cls):
|
||||||
|
super(QuerySetMethodWrapper, self).__init__(method)
|
||||||
|
self._model_cls = model_cls
|
||||||
|
|
||||||
|
def py__get__(self, instance, class_value):
|
||||||
|
return ValueSet({QuerySetBoundMethodWrapper(v, self._model_cls)
|
||||||
|
for v in self._wrapped_value.py__get__(instance, class_value)})
|
||||||
|
|
||||||
|
|
||||||
|
class QuerySetBoundMethodWrapper(ValueWrapper):
|
||||||
|
def __init__(self, method, model_cls):
|
||||||
|
super(QuerySetBoundMethodWrapper, self).__init__(method)
|
||||||
|
self._model_cls = model_cls
|
||||||
|
|
||||||
|
def get_signatures(self):
|
||||||
|
return _get_signatures(self._model_cls)
|
||||||
|
|||||||
@@ -260,13 +260,12 @@ class ReversedObject(AttributeOverwrite):
|
|||||||
super(ReversedObject, self).__init__(reversed_obj)
|
super(ReversedObject, self).__init__(reversed_obj)
|
||||||
self._iter_list = iter_list
|
self._iter_list = iter_list
|
||||||
|
|
||||||
@publish_method('__iter__')
|
def py__iter__(self, contextualized_node):
|
||||||
def py__iter__(self, contextualized_node=None):
|
|
||||||
return self._iter_list
|
return self._iter_list
|
||||||
|
|
||||||
@publish_method('next', python_version_match=2)
|
@publish_method('next', python_version_match=2)
|
||||||
@publish_method('__next__', python_version_match=3)
|
@publish_method('__next__', python_version_match=3)
|
||||||
def py__next__(self):
|
def py__next__(self, arguments):
|
||||||
return ValueSet.from_sets(
|
return ValueSet.from_sets(
|
||||||
lazy_value.infer() for lazy_value in self._iter_list
|
lazy_value.infer() for lazy_value in self._iter_list
|
||||||
)
|
)
|
||||||
@@ -395,13 +394,13 @@ class PropertyObject(AttributeOverwrite, ValueWrapper):
|
|||||||
|
|
||||||
def py__get__(self, instance, class_value):
|
def py__get__(self, instance, class_value):
|
||||||
if instance is None:
|
if instance is None:
|
||||||
return NO_VALUES
|
return ValueSet([self])
|
||||||
return self._function.execute_with_values(instance)
|
return self._function.execute_with_values(instance)
|
||||||
|
|
||||||
@publish_method('deleter')
|
@publish_method('deleter')
|
||||||
@publish_method('getter')
|
@publish_method('getter')
|
||||||
@publish_method('setter')
|
@publish_method('setter')
|
||||||
def _return_self(self):
|
def _return_self(self, arguments):
|
||||||
return ValueSet({self})
|
return ValueSet({self})
|
||||||
|
|
||||||
|
|
||||||
@@ -518,6 +517,8 @@ class PartialObject(ValueWrapper):
|
|||||||
|
|
||||||
class PartialMethodObject(PartialObject):
|
class PartialMethodObject(PartialObject):
|
||||||
def py__get__(self, instance, class_value):
|
def py__get__(self, instance, class_value):
|
||||||
|
if instance is None:
|
||||||
|
return ValueSet([self])
|
||||||
return ValueSet([PartialObject(self._actual_value, self._arguments, instance)])
|
return ValueSet([PartialObject(self._actual_value, self._arguments, instance)])
|
||||||
|
|
||||||
|
|
||||||
@@ -802,7 +803,7 @@ _implemented = {
|
|||||||
|
|
||||||
|
|
||||||
def get_metaclass_filters(func):
|
def get_metaclass_filters(func):
|
||||||
def wrapper(cls, metaclasses):
|
def wrapper(cls, metaclasses, is_instance):
|
||||||
for metaclass in metaclasses:
|
for metaclass in metaclasses:
|
||||||
if metaclass.py__name__() == 'EnumMeta' \
|
if metaclass.py__name__() == 'EnumMeta' \
|
||||||
and metaclass.get_root_context().py__name__() == 'enum':
|
and metaclass.get_root_context().py__name__() == 'enum':
|
||||||
@@ -810,7 +811,7 @@ def get_metaclass_filters(func):
|
|||||||
return [DictFilter({
|
return [DictFilter({
|
||||||
name.string_name: EnumInstance(cls, name).name for name in filter_.values()
|
name.string_name: EnumInstance(cls, name).name for name in filter_.values()
|
||||||
})]
|
})]
|
||||||
return func(cls, metaclasses)
|
return func(cls, metaclasses, is_instance)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,21 @@ import uuid
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models.query_utils import DeferredAttribute
|
||||||
|
|
||||||
|
|
||||||
|
class TagManager(models.Manager):
|
||||||
|
def specially_filtered_tags(self):
|
||||||
|
return self.all()
|
||||||
|
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
tag_name = models.CharField()
|
tag_name = models.CharField()
|
||||||
|
|
||||||
|
objects = TagManager()
|
||||||
|
|
||||||
|
custom_objects = TagManager()
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
category_name = models.CharField()
|
category_name = models.CharField()
|
||||||
@@ -49,10 +59,23 @@ class BusinessModel(models.Model):
|
|||||||
|
|
||||||
unidentifiable = NOT_FOUND
|
unidentifiable = NOT_FOUND
|
||||||
|
|
||||||
|
#? models.IntegerField()
|
||||||
|
integer_field
|
||||||
|
|
||||||
|
def method(self):
|
||||||
|
return 42
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# Model attribute inference
|
# Model attribute inference
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
|
#? DeferredAttribute()
|
||||||
|
BusinessModel.integer_field
|
||||||
|
#? DeferredAttribute()
|
||||||
|
BusinessModel.tags_m2m
|
||||||
|
#? DeferredAttribute()
|
||||||
|
BusinessModel.email_field
|
||||||
|
|
||||||
model_instance = BusinessModel()
|
model_instance = BusinessModel()
|
||||||
|
|
||||||
#? int()
|
#? int()
|
||||||
@@ -132,20 +155,45 @@ model_instance.unidentifiable
|
|||||||
#! ['unidentifiable = NOT_FOUND']
|
#! ['unidentifiable = NOT_FOUND']
|
||||||
model_instance.unidentifiable
|
model_instance.unidentifiable
|
||||||
|
|
||||||
|
#? int()
|
||||||
|
model_instance.method()
|
||||||
|
#! ['def method']
|
||||||
|
model_instance.method
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# Queries
|
# Queries
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
#? models.query.QuerySet.filter
|
#? ['objects']
|
||||||
|
model_instance.object
|
||||||
|
#?
|
||||||
|
model_instance.objects
|
||||||
|
#?
|
||||||
model_instance.objects.filter
|
model_instance.objects.filter
|
||||||
|
#? models.query.QuerySet.filter
|
||||||
|
BusinessModel.objects.filter
|
||||||
#? BusinessModel() None
|
#? BusinessModel() None
|
||||||
model_instance.objects.filter().first()
|
BusinessModel.objects.filter().first()
|
||||||
#? str()
|
#? str()
|
||||||
model_instance.objects.get().char_field
|
BusinessModel.objects.get().char_field
|
||||||
#? int()
|
#? int()
|
||||||
model_instance.objects.update(x='')
|
BusinessModel.objects.update(x='')
|
||||||
#? BusinessModel()
|
#? BusinessModel()
|
||||||
model_instance.objects.create()
|
BusinessModel.objects.create()
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# Custom object manager
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
#? TagManager()
|
||||||
|
Tag.objects
|
||||||
|
#? Tag() None
|
||||||
|
Tag.objects.filter().first()
|
||||||
|
|
||||||
|
#? TagManager()
|
||||||
|
Tag.custom_objects
|
||||||
|
#? Tag() None
|
||||||
|
Tag.custom_objects.filter().first()
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# Inheritance
|
# Inheritance
|
||||||
@@ -163,14 +211,27 @@ inherited.char_field
|
|||||||
#? float()
|
#? float()
|
||||||
inherited.new_field
|
inherited.new_field
|
||||||
|
|
||||||
|
#?
|
||||||
|
Inherited.category_fk2.category_name
|
||||||
#? str()
|
#? str()
|
||||||
inherited.category_fk2.category_name
|
inherited.category_fk2.category_name
|
||||||
#? str()
|
#? str()
|
||||||
inherited.objects.get().char_field
|
Inherited.objects.get().char_field
|
||||||
#? int()
|
#? int()
|
||||||
inherited.objects.get().text_field
|
Inherited.objects.get().text_field
|
||||||
#? float()
|
#? float()
|
||||||
inherited.objects.get().new_field
|
Inherited.objects.get().new_field
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# Model methods
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
#? ['from_db']
|
||||||
|
Inherited.from_db
|
||||||
|
#? ['validate_unique']
|
||||||
|
Inherited.validate_uniqu
|
||||||
|
#? ['validate_unique']
|
||||||
|
Inherited().validate_unique
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# Django Auth
|
# Django Auth
|
||||||
@@ -186,8 +247,46 @@ User.objects.get().email
|
|||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
#?
|
#?
|
||||||
model_instance.objects.values_list('char_field')[0]
|
BusinessModel.objects.values_list('char_field')[0]
|
||||||
#? dict()
|
#? dict()
|
||||||
model_instance.objects.values('char_field')[0]
|
BusinessModel.objects.values('char_field')[0]
|
||||||
#?
|
#?
|
||||||
model_instance.objects.values('char_field')[0]['char_field']
|
BusinessModel.objects.values('char_field')[0]['char_field']
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# Completion
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
#? 19 ['text_field=']
|
||||||
|
Inherited(text_fiel)
|
||||||
|
#? 18 ['new_field=']
|
||||||
|
Inherited(new_fiel)
|
||||||
|
#? 19 ['char_field=']
|
||||||
|
Inherited(char_fiel)
|
||||||
|
#? 19 ['email_field=']
|
||||||
|
Inherited(email_fie)
|
||||||
|
#? 19 []
|
||||||
|
Inherited(unidentif)
|
||||||
|
#? 21 ['category_fk=', 'category_fk2=', 'category_fk3=', 'category_fk4=', 'category_fk5=']
|
||||||
|
Inherited(category_fk)
|
||||||
|
#? 21 ['attached_o2o=']
|
||||||
|
Inherited(attached_o2)
|
||||||
|
#? 18 ['tags_m2m=']
|
||||||
|
Inherited(tags_m2m)
|
||||||
|
|
||||||
|
#? 32 ['tags_m2m=']
|
||||||
|
Inherited.objects.create(tags_m2)
|
||||||
|
#? 32 ['tags_m2m=']
|
||||||
|
Inherited.objects.filter(tags_m2)
|
||||||
|
#? 35 ['char_field=']
|
||||||
|
Inherited.objects.exclude(char_fiel)
|
||||||
|
#? 34 ['char_field=']
|
||||||
|
Inherited.objects.update(char_fiel)
|
||||||
|
#? 32 ['email_field=']
|
||||||
|
Inherited.objects.get(email_fiel)
|
||||||
|
#? 44 ['category_fk2=']
|
||||||
|
Inherited.objects.get_or_create(category_fk2)
|
||||||
|
#? 44 ['uuid_field=']
|
||||||
|
Inherited.objects.update_or_create(uuid_fiel)
|
||||||
|
#? 48 ['char_field=']
|
||||||
|
Inherited.objects.exclude(pk=3).filter(char_fiel)
|
||||||
|
|||||||
Reference in New Issue
Block a user