mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Use py__get__ for Django Model.objects
This includes the fix in https://github.com/typeddjango/django-stubs/pull/394
This commit is contained in:
@@ -240,6 +240,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
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ class GenericClass(DefineGenericBaseClass, ClassMixin):
|
|||||||
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
|
||||||
@@ -287,6 +290,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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -195,7 +193,7 @@ class ClassMixin(object):
|
|||||||
metaclasses = self.get_metaclasses()
|
metaclasses = self.get_metaclasses()
|
||||||
if metaclasses:
|
if metaclasses:
|
||||||
for f in self.get_metaclass_filters(metaclasses, is_instance):
|
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():
|
||||||
@@ -203,7 +201,7 @@ class ClassMixin(object):
|
|||||||
yield filter
|
yield filter
|
||||||
else:
|
else:
|
||||||
yield ClassFilter(
|
yield ClassFilter(
|
||||||
self, node_context=cls.as_context(),
|
cls, node_context=self.as_context(),
|
||||||
origin_scope=origin_scope,
|
origin_scope=origin_scope,
|
||||||
is_instance=is_instance
|
is_instance=is_instance
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,12 +2,15 @@
|
|||||||
Module is used to infer Django model fields.
|
Module is used to infer Django model fields.
|
||||||
"""
|
"""
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.inference.base_value import ValueSet, iterator_to_value_set
|
from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper
|
||||||
from jedi.inference.filters import ParserTreeFilter, DictFilter
|
from jedi.inference.filters import DictFilter, AttributeOverwrite, publish_method
|
||||||
from jedi.inference.names import NameWrapper
|
from jedi.inference.names import NameWrapper
|
||||||
|
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.arguments import repack_with_argument_clinic
|
||||||
|
|
||||||
|
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -124,36 +127,6 @@ def _create_manager_for(cls, manager_cls='BaseManager'):
|
|||||||
|
|
||||||
|
|
||||||
def _new_dict_filter(cls, is_instance):
|
def _new_dict_filter(cls, is_instance):
|
||||||
def get_manager_name(filters):
|
|
||||||
for f in filters:
|
|
||||||
names = f.get('objects')
|
|
||||||
if not names:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Found a match. Either the model has a custom manager, or we're
|
|
||||||
# now in django.db.models.Model. If the latter we need to use
|
|
||||||
# `_create_manager_for` because the manager we get from the
|
|
||||||
# stubs doesn't work right.
|
|
||||||
|
|
||||||
name = names[0] # The first name should be good enough.
|
|
||||||
|
|
||||||
parent = name.get_defining_qualified_value()
|
|
||||||
if parent.py__name__() == 'Model':
|
|
||||||
|
|
||||||
django_models_model, = cls.inference_state.import_module(
|
|
||||||
('django', 'db', 'models', 'base'),
|
|
||||||
).py__getattribute__('Model')
|
|
||||||
if django_models_model == parent:
|
|
||||||
# Don't want to use the value from the Django stubs, but
|
|
||||||
# we have found the point where they'd take precedence.
|
|
||||||
break
|
|
||||||
|
|
||||||
return name
|
|
||||||
|
|
||||||
manager = _create_manager_for(cls)
|
|
||||||
if manager:
|
|
||||||
return manager.name
|
|
||||||
|
|
||||||
filters = list(cls.get_filters(
|
filters = list(cls.get_filters(
|
||||||
is_instance=is_instance,
|
is_instance=is_instance,
|
||||||
include_metaclasses=False,
|
include_metaclasses=False,
|
||||||
@@ -164,10 +137,14 @@ def _new_dict_filter(cls, is_instance):
|
|||||||
for filter_ in reversed(filters)
|
for filter_ in reversed(filters)
|
||||||
for name in filter_.values()
|
for name in filter_.values()
|
||||||
}
|
}
|
||||||
|
if is_instance:
|
||||||
manager_name = get_manager_name(filters)
|
# Replace the objects with a name that amounts to nothing when accessed
|
||||||
if manager_name:
|
# in an instance. This is not perfect and still completes "objects" in
|
||||||
dct['objects'] = manager_name
|
# 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)
|
||||||
|
|
||||||
@@ -181,3 +158,32 @@ def get_metaclass_filters(func):
|
|||||||
|
|
||||||
return func(cls, metaclasses, is_instance)
|
return func(cls, metaclasses, is_instance)
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
|
def tree_name_to_values(func):
|
||||||
|
def wrapper(inference_state, context, tree_name):
|
||||||
|
result = func(inference_state, context, tree_name)
|
||||||
|
if tree_name.value == 'BaseManager' and context.is_module() \
|
||||||
|
and context.py__name__() == 'django.db.models.manager':
|
||||||
|
return ValueSet(ManagerWrapper(r) for r in result)
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|||||||
@@ -162,16 +162,22 @@ 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
|
# Custom object manager
|
||||||
@@ -179,9 +185,13 @@ model_instance.objects.create()
|
|||||||
|
|
||||||
#? TagManager()
|
#? TagManager()
|
||||||
Tag.objects
|
Tag.objects
|
||||||
|
#? Tag() None
|
||||||
|
Tag.objects.filter().first()
|
||||||
|
|
||||||
#? TagManager()
|
#? TagManager()
|
||||||
Tag.custom_objects
|
Tag.custom_objects
|
||||||
|
#? Tag() None
|
||||||
|
Tag.custom_objects.filter().first()
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# Inheritance
|
# Inheritance
|
||||||
@@ -199,14 +209,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
|
||||||
@@ -222,8 +245,8 @@ 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']
|
||||||
|
|||||||
Reference in New Issue
Block a user