]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/jedi/plugins/django.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / plugins / django.py
CommitLineData
53e6db90
DC
1"""
2Module is used to infer Django model fields.
3"""
4from inspect import Parameter
5
6from jedi import debug
7from jedi.inference.cache import inference_state_function_cache
8from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper
9from jedi.inference.filters import DictFilter, AttributeOverwrite
10from jedi.inference.names import NameWrapper, BaseTreeParamName
11from jedi.inference.compiled.value import EmptyCompiledName
12from jedi.inference.value.instance import TreeInstance
13from jedi.inference.value.klass import ClassMixin
14from jedi.inference.gradual.base import GenericClass
15from jedi.inference.gradual.generics import TupleGenericManager
16from jedi.inference.signature import AbstractSignature
17
18
19mapping = {
20 'IntegerField': (None, 'int'),
21 'BigIntegerField': (None, 'int'),
22 'PositiveIntegerField': (None, 'int'),
23 'SmallIntegerField': (None, 'int'),
24 'CharField': (None, 'str'),
25 'TextField': (None, 'str'),
26 'EmailField': (None, 'str'),
27 'GenericIPAddressField': (None, 'str'),
28 'URLField': (None, 'str'),
29 'FloatField': (None, 'float'),
30 'BinaryField': (None, 'bytes'),
31 'BooleanField': (None, 'bool'),
32 'DecimalField': ('decimal', 'Decimal'),
33 'TimeField': ('datetime', 'time'),
34 'DurationField': ('datetime', 'timedelta'),
35 'DateField': ('datetime', 'date'),
36 'DateTimeField': ('datetime', 'datetime'),
37 'UUIDField': ('uuid', 'UUID'),
38}
39
40_FILTER_LIKE_METHODS = ('create', 'filter', 'exclude', 'update', 'get',
41 'get_or_create', 'update_or_create')
42
43
44@inference_state_function_cache()
45def _get_deferred_attributes(inference_state):
46 return inference_state.import_module(
47 ('django', 'db', 'models', 'query_utils')
48 ).py__getattribute__('DeferredAttribute').execute_annotation()
49
50
51def _infer_scalar_field(inference_state, field_name, field_tree_instance, is_instance):
52 try:
53 module_name, attribute_name = mapping[field_tree_instance.py__name__()]
54 except KeyError:
55 return None
56
57 if not is_instance:
58 return _get_deferred_attributes(inference_state)
59
60 if module_name is None:
61 module = inference_state.builtins_module
62 else:
63 module = inference_state.import_module((module_name,))
64
65 for attribute in module.py__getattribute__(attribute_name):
66 return attribute.execute_with_values()
67
68
69@iterator_to_value_set
70def _get_foreign_key_values(cls, field_tree_instance):
71 if isinstance(field_tree_instance, TreeInstance):
72 # TODO private access..
73 argument_iterator = field_tree_instance._arguments.unpack()
74 key, lazy_values = next(argument_iterator, (None, None))
75 if key is None and lazy_values is not None:
76 for value in lazy_values.infer():
77 if value.py__name__() == 'str':
78 foreign_key_class_name = value.get_safe_value()
79 module = cls.get_root_context()
80 for v in module.py__getattribute__(foreign_key_class_name):
81 if v.is_class():
82 yield v
83 elif value.is_class():
84 yield value
85
86
87def _infer_field(cls, field_name, is_instance):
88 inference_state = cls.inference_state
89 result = field_name.infer()
90 for field_tree_instance in result:
91 scalar_field = _infer_scalar_field(
92 inference_state, field_name, field_tree_instance, is_instance)
93 if scalar_field is not None:
94 return scalar_field
95
96 name = field_tree_instance.py__name__()
97 is_many_to_many = name == 'ManyToManyField'
98 if name in ('ForeignKey', 'OneToOneField') or is_many_to_many:
99 if not is_instance:
100 return _get_deferred_attributes(inference_state)
101
102 values = _get_foreign_key_values(cls, field_tree_instance)
103 if is_many_to_many:
104 return ValueSet(filter(None, [
105 _create_manager_for(v, 'RelatedManager') for v in values
106 ]))
107 else:
108 return values.execute_with_values()
109
110 debug.dbg('django plugin: fail to infer `%s` from class `%s`',
111 field_name.string_name, cls.py__name__())
112 return result
113
114
115class DjangoModelName(NameWrapper):
116 def __init__(self, cls, name, is_instance):
117 super().__init__(name)
118 self._cls = cls
119 self._is_instance = is_instance
120
121 def infer(self):
122 return _infer_field(self._cls, self._wrapped_name, self._is_instance)
123
124
125def _create_manager_for(cls, manager_cls='BaseManager'):
126 managers = cls.inference_state.import_module(
127 ('django', 'db', 'models', 'manager')
128 ).py__getattribute__(manager_cls)
129 for m in managers:
130 if m.is_class_mixin():
131 generics_manager = TupleGenericManager((ValueSet([cls]),))
132 for c in GenericClass(m, generics_manager).execute_annotation():
133 return c
134 return None
135
136
137def _new_dict_filter(cls, is_instance):
138 filters = list(cls.get_filters(
139 is_instance=is_instance,
140 include_metaclasses=False,
141 include_type_when_class=False)
142 )
143 dct = {
144 name.string_name: DjangoModelName(cls, name, is_instance)
145 for filter_ in reversed(filters)
146 for name in filter_.values()
147 }
148 if is_instance:
149 # Replace the objects with a name that amounts to nothing when accessed
150 # in an instance. This is not perfect and still completes "objects" in
151 # that case, but it at least not inferes stuff like `.objects.filter`.
152 # It would be nicer to do that in a better way, so that it also doesn't
153 # show up in completions, but it's probably just not worth doing that
154 # for the extra amount of work.
155 dct['objects'] = EmptyCompiledName(cls.inference_state, 'objects')
156
157 return DictFilter(dct)
158
159
160def is_django_model_base(value):
161 return value.py__name__() == 'ModelBase' \
162 and value.get_root_context().py__name__() == 'django.db.models.base'
163
164
165def get_metaclass_filters(func):
166 def wrapper(cls, metaclasses, is_instance):
167 for metaclass in metaclasses:
168 if is_django_model_base(metaclass):
169 return [_new_dict_filter(cls, is_instance)]
170
171 return func(cls, metaclasses, is_instance)
172 return wrapper
173
174
175def tree_name_to_values(func):
176 def wrapper(inference_state, context, tree_name):
177 result = func(inference_state, context, tree_name)
178 if tree_name.value in _FILTER_LIKE_METHODS:
179 # Here we try to overwrite stuff like User.objects.filter. We need
180 # this to make sure that keyword param completion works on these
181 # kind of methods.
182 for v in result:
183 if v.get_qualified_names() == ('_BaseQuerySet', tree_name.value) \
184 and v.parent_context.is_module() \
185 and v.parent_context.py__name__() == 'django.db.models.query':
186 qs = context.get_value()
187 generics = qs.get_generics()
188 if len(generics) >= 1:
189 return ValueSet(QuerySetMethodWrapper(v, model)
190 for model in generics[0])
191
192 elif tree_name.value == 'BaseManager' and context.is_module() \
193 and context.py__name__() == 'django.db.models.manager':
194 return ValueSet(ManagerWrapper(r) for r in result)
195
196 elif tree_name.value == 'Field' and context.is_module() \
197 and context.py__name__() == 'django.db.models.fields':
198 return ValueSet(FieldWrapper(r) for r in result)
199 return result
200 return wrapper
201
202
203def _find_fields(cls):
204 for name in _new_dict_filter(cls, is_instance=False).values():
205 for value in name.infer():
206 if value.name.get_qualified_names(include_module_names=True) \
207 == ('django', 'db', 'models', 'query_utils', 'DeferredAttribute'):
208 yield name
209
210
211def _get_signatures(cls):
212 return [DjangoModelSignature(cls, field_names=list(_find_fields(cls)))]
213
214
215def get_metaclass_signatures(func):
216 def wrapper(cls, metaclasses):
217 for metaclass in metaclasses:
218 if is_django_model_base(metaclass):
219 return _get_signatures(cls)
220 return func(cls, metaclass)
221 return wrapper
222
223
224class ManagerWrapper(ValueWrapper):
225 def py__getitem__(self, index_value_set, contextualized_node):
226 return ValueSet(
227 GenericManagerWrapper(generic)
228 for generic in self._wrapped_value.py__getitem__(
229 index_value_set, contextualized_node)
230 )
231
232
233class GenericManagerWrapper(AttributeOverwrite, ClassMixin):
234 def py__get__on_class(self, calling_instance, instance, class_value):
235 return calling_instance.class_value.with_generics(
236 (ValueSet({class_value}),)
237 ).py__call__(calling_instance._arguments)
238
239 def with_generics(self, generics_tuple):
240 return self._wrapped_value.with_generics(generics_tuple)
241
242
243class FieldWrapper(ValueWrapper):
244 def py__getitem__(self, index_value_set, contextualized_node):
245 return ValueSet(
246 GenericFieldWrapper(generic)
247 for generic in self._wrapped_value.py__getitem__(
248 index_value_set, contextualized_node)
249 )
250
251
252class GenericFieldWrapper(AttributeOverwrite, ClassMixin):
253 def py__get__on_class(self, calling_instance, instance, class_value):
254 # This is mostly an optimization to avoid Jedi aborting inference,
255 # because of too many function executions of Field.__get__.
256 return ValueSet({calling_instance})
257
258
259class DjangoModelSignature(AbstractSignature):
260 def __init__(self, value, field_names):
261 super().__init__(value)
262 self._field_names = field_names
263
264 def get_param_names(self, resolve_stars=False):
265 return [DjangoParamName(name) for name in self._field_names]
266
267
268class DjangoParamName(BaseTreeParamName):
269 def __init__(self, field_name):
270 super().__init__(field_name.parent_context, field_name.tree_name)
271 self._field_name = field_name
272
273 def get_kind(self):
274 return Parameter.KEYWORD_ONLY
275
276 def infer(self):
277 return self._field_name.infer()
278
279
280class QuerySetMethodWrapper(ValueWrapper):
281 def __init__(self, method, model_cls):
282 super().__init__(method)
283 self._model_cls = model_cls
284
285 def py__get__(self, instance, class_value):
286 return ValueSet({QuerySetBoundMethodWrapper(v, self._model_cls)
287 for v in self._wrapped_value.py__get__(instance, class_value)})
288
289
290class QuerySetBoundMethodWrapper(ValueWrapper):
291 def __init__(self, method, model_cls):
292 super().__init__(method)
293 self._model_cls = model_cls
294
295 def get_signatures(self):
296 return _get_signatures(self._model_cls)