]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | """ |
2 | Implementations of standard library functions, because it's not possible to | |
3 | understand them with Jedi. | |
4 | ||
5 | To add a new implementation, create a function and add it to the | |
6 | ``_implemented`` dict at the bottom of this module. | |
7 | ||
8 | Note that this module exists only to implement very specific functionality in | |
9 | the standard library. The usual way to understand the standard library is the | |
10 | compiled module that returns the types for C-builtins. | |
11 | """ | |
12 | import parso | |
13 | import os | |
14 | from inspect import Parameter | |
15 | ||
16 | from jedi import debug | |
17 | from jedi.inference.utils import safe_property | |
18 | from jedi.inference.helpers import get_str_or_none | |
19 | from jedi.inference.arguments import iterate_argument_clinic, ParamIssue, \ | |
20 | repack_with_argument_clinic, AbstractArguments, TreeArgumentsWrapper | |
21 | from jedi.inference import analysis | |
22 | from jedi.inference import compiled | |
23 | from jedi.inference.value.instance import \ | |
24 | AnonymousMethodExecutionContext, MethodExecutionContext | |
25 | from jedi.inference.base_value import ContextualizedNode, \ | |
26 | NO_VALUES, ValueSet, ValueWrapper, LazyValueWrapper | |
27 | from jedi.inference.value import ClassValue, ModuleValue | |
28 | from jedi.inference.value.klass import ClassMixin | |
29 | from jedi.inference.value.function import FunctionMixin | |
30 | from jedi.inference.value import iterable | |
31 | from jedi.inference.lazy_value import LazyTreeValue, LazyKnownValue, \ | |
32 | LazyKnownValues | |
33 | from jedi.inference.names import ValueName, BaseTreeParamName | |
34 | from jedi.inference.filters import AttributeOverwrite, publish_method, \ | |
35 | ParserTreeFilter, DictFilter | |
36 | from jedi.inference.signature import AbstractSignature, SignatureWrapper | |
37 | ||
38 | ||
39 | # Copied from Python 3.6's stdlib. | |
40 | _NAMEDTUPLE_CLASS_TEMPLATE = """\ | |
41 | _property = property | |
42 | _tuple = tuple | |
43 | from operator import itemgetter as _itemgetter | |
44 | from collections import OrderedDict | |
45 | ||
46 | class {typename}(tuple): | |
47 | __slots__ = () | |
48 | ||
49 | _fields = {field_names!r} | |
50 | ||
51 | def __new__(_cls, {arg_list}): | |
52 | 'Create new instance of {typename}({arg_list})' | |
53 | return _tuple.__new__(_cls, ({arg_list})) | |
54 | ||
55 | @classmethod | |
56 | def _make(cls, iterable, new=tuple.__new__, len=len): | |
57 | 'Make a new {typename} object from a sequence or iterable' | |
58 | result = new(cls, iterable) | |
59 | if len(result) != {num_fields:d}: | |
60 | raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result)) | |
61 | return result | |
62 | ||
63 | def _replace(_self, **kwds): | |
64 | 'Return a new {typename} object replacing specified fields with new values' | |
65 | result = _self._make(map(kwds.pop, {field_names!r}, _self)) | |
66 | if kwds: | |
67 | raise ValueError('Got unexpected field names: %r' % list(kwds)) | |
68 | return result | |
69 | ||
70 | def __repr__(self): | |
71 | 'Return a nicely formatted representation string' | |
72 | return self.__class__.__name__ + '({repr_fmt})' % self | |
73 | ||
74 | def _asdict(self): | |
75 | 'Return a new OrderedDict which maps field names to their values.' | |
76 | return OrderedDict(zip(self._fields, self)) | |
77 | ||
78 | def __getnewargs__(self): | |
79 | 'Return self as a plain tuple. Used by copy and pickle.' | |
80 | return tuple(self) | |
81 | ||
82 | # These methods were added by Jedi. | |
83 | # __new__ doesn't really work with Jedi. So adding this to nametuples seems | |
84 | # like the easiest way. | |
85 | def __init__(self, {arg_list}): | |
86 | 'A helper function for namedtuple.' | |
87 | self.__iterable = ({arg_list}) | |
88 | ||
89 | def __iter__(self): | |
90 | for i in self.__iterable: | |
91 | yield i | |
92 | ||
93 | def __getitem__(self, y): | |
94 | return self.__iterable[y] | |
95 | ||
96 | {field_defs} | |
97 | """ | |
98 | ||
99 | _NAMEDTUPLE_FIELD_TEMPLATE = '''\ | |
100 | {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}') | |
101 | ''' | |
102 | ||
103 | ||
104 | def execute(callback): | |
105 | def wrapper(value, arguments): | |
106 | def call(): | |
107 | return callback(value, arguments=arguments) | |
108 | ||
109 | try: | |
110 | obj_name = value.name.string_name | |
111 | except AttributeError: | |
112 | pass | |
113 | else: | |
114 | p = value.parent_context | |
115 | if p is not None and p.is_builtins_module(): | |
116 | module_name = 'builtins' | |
117 | elif p is not None and p.is_module(): | |
118 | module_name = p.py__name__() | |
119 | else: | |
120 | return call() | |
121 | ||
122 | if value.is_bound_method() or value.is_instance(): | |
123 | # value can be an instance for example if it is a partial | |
124 | # object. | |
125 | return call() | |
126 | ||
127 | # for now we just support builtin functions. | |
128 | try: | |
129 | func = _implemented[module_name][obj_name] | |
130 | except KeyError: | |
131 | pass | |
132 | else: | |
133 | return func(value, arguments=arguments, callback=call) | |
134 | return call() | |
135 | ||
136 | return wrapper | |
137 | ||
138 | ||
139 | def _follow_param(inference_state, arguments, index): | |
140 | try: | |
141 | key, lazy_value = list(arguments.unpack())[index] | |
142 | except IndexError: | |
143 | return NO_VALUES | |
144 | else: | |
145 | return lazy_value.infer() | |
146 | ||
147 | ||
148 | def argument_clinic(clinic_string, want_value=False, want_context=False, | |
149 | want_arguments=False, want_inference_state=False, | |
150 | want_callback=False): | |
151 | """ | |
152 | Works like Argument Clinic (PEP 436), to validate function params. | |
153 | """ | |
154 | ||
155 | def f(func): | |
156 | def wrapper(value, arguments, callback): | |
157 | try: | |
158 | args = tuple(iterate_argument_clinic( | |
159 | value.inference_state, arguments, clinic_string)) | |
160 | except ParamIssue: | |
161 | return NO_VALUES | |
162 | ||
163 | debug.dbg('builtin start %s' % value, color='MAGENTA') | |
164 | kwargs = {} | |
165 | if want_context: | |
166 | kwargs['context'] = arguments.context | |
167 | if want_value: | |
168 | kwargs['value'] = value | |
169 | if want_inference_state: | |
170 | kwargs['inference_state'] = value.inference_state | |
171 | if want_arguments: | |
172 | kwargs['arguments'] = arguments | |
173 | if want_callback: | |
174 | kwargs['callback'] = callback | |
175 | result = func(*args, **kwargs) | |
176 | debug.dbg('builtin end: %s', result, color='MAGENTA') | |
177 | return result | |
178 | ||
179 | return wrapper | |
180 | return f | |
181 | ||
182 | ||
183 | @argument_clinic('iterator[, default], /', want_inference_state=True) | |
184 | def builtins_next(iterators, defaults, inference_state): | |
185 | # TODO theoretically we have to check here if something is an iterator. | |
186 | # That is probably done by checking if it's not a class. | |
187 | return defaults | iterators.py__getattribute__('__next__').execute_with_values() | |
188 | ||
189 | ||
190 | @argument_clinic('iterator[, default], /') | |
191 | def builtins_iter(iterators_or_callables, defaults): | |
192 | # TODO implement this if it's a callable. | |
193 | return iterators_or_callables.py__getattribute__('__iter__').execute_with_values() | |
194 | ||
195 | ||
196 | @argument_clinic('object, name[, default], /') | |
197 | def builtins_getattr(objects, names, defaults=None): | |
198 | # follow the first param | |
199 | for value in objects: | |
200 | for name in names: | |
201 | string = get_str_or_none(name) | |
202 | if string is None: | |
203 | debug.warning('getattr called without str') | |
204 | continue | |
205 | else: | |
206 | return value.py__getattribute__(string) | |
207 | return NO_VALUES | |
208 | ||
209 | ||
210 | @argument_clinic('object[, bases, dict], /') | |
211 | def builtins_type(objects, bases, dicts): | |
212 | if bases or dicts: | |
213 | # It's a type creation... maybe someday... | |
214 | return NO_VALUES | |
215 | else: | |
216 | return objects.py__class__() | |
217 | ||
218 | ||
219 | class SuperInstance(LazyValueWrapper): | |
220 | """To be used like the object ``super`` returns.""" | |
221 | def __init__(self, inference_state, instance): | |
222 | self.inference_state = inference_state | |
223 | self._instance = instance # Corresponds to super().__self__ | |
224 | ||
225 | def _get_bases(self): | |
226 | return self._instance.py__class__().py__bases__() | |
227 | ||
228 | def _get_wrapped_value(self): | |
229 | objs = self._get_bases()[0].infer().execute_with_values() | |
230 | if not objs: | |
231 | # This is just a fallback and will only be used, if it's not | |
232 | # possible to find a class | |
233 | return self._instance | |
234 | return next(iter(objs)) | |
235 | ||
236 | def get_filters(self, origin_scope=None): | |
237 | for b in self._get_bases(): | |
238 | for value in b.infer().execute_with_values(): | |
239 | for f in value.get_filters(): | |
240 | yield f | |
241 | ||
242 | ||
243 | @argument_clinic('[type[, value]], /', want_context=True) | |
244 | def builtins_super(types, objects, context): | |
245 | instance = None | |
246 | if isinstance(context, AnonymousMethodExecutionContext): | |
247 | instance = context.instance | |
248 | elif isinstance(context, MethodExecutionContext): | |
249 | instance = context.instance | |
250 | if instance is None: | |
251 | return NO_VALUES | |
252 | return ValueSet({SuperInstance(instance.inference_state, instance)}) | |
253 | ||
254 | ||
255 | class ReversedObject(AttributeOverwrite): | |
256 | def __init__(self, reversed_obj, iter_list): | |
257 | super().__init__(reversed_obj) | |
258 | self._iter_list = iter_list | |
259 | ||
260 | def py__iter__(self, contextualized_node=None): | |
261 | return self._iter_list | |
262 | ||
263 | @publish_method('__next__') | |
264 | def _next(self, arguments): | |
265 | return ValueSet.from_sets( | |
266 | lazy_value.infer() for lazy_value in self._iter_list | |
267 | ) | |
268 | ||
269 | ||
270 | @argument_clinic('sequence, /', want_value=True, want_arguments=True) | |
271 | def builtins_reversed(sequences, value, arguments): | |
272 | # While we could do without this variable (just by using sequences), we | |
273 | # want static analysis to work well. Therefore we need to generated the | |
274 | # values again. | |
275 | key, lazy_value = next(arguments.unpack()) | |
276 | cn = None | |
277 | if isinstance(lazy_value, LazyTreeValue): | |
278 | cn = ContextualizedNode(lazy_value.context, lazy_value.data) | |
279 | ordered = list(sequences.iterate(cn)) | |
280 | ||
281 | # Repack iterator values and then run it the normal way. This is | |
282 | # necessary, because `reversed` is a function and autocompletion | |
283 | # would fail in certain cases like `reversed(x).__iter__` if we | |
284 | # just returned the result directly. | |
285 | seq, = value.inference_state.typing_module.py__getattribute__('Iterator').execute_with_values() | |
286 | return ValueSet([ReversedObject(seq, list(reversed(ordered)))]) | |
287 | ||
288 | ||
289 | @argument_clinic('value, type, /', want_arguments=True, want_inference_state=True) | |
290 | def builtins_isinstance(objects, types, arguments, inference_state): | |
291 | bool_results = set() | |
292 | for o in objects: | |
293 | cls = o.py__class__() | |
294 | try: | |
295 | cls.py__bases__ | |
296 | except AttributeError: | |
297 | # This is temporary. Everything should have a class attribute in | |
298 | # Python?! Maybe we'll leave it here, because some numpy objects or | |
299 | # whatever might not. | |
300 | bool_results = set([True, False]) | |
301 | break | |
302 | ||
303 | mro = list(cls.py__mro__()) | |
304 | ||
305 | for cls_or_tup in types: | |
306 | if cls_or_tup.is_class(): | |
307 | bool_results.add(cls_or_tup in mro) | |
308 | elif cls_or_tup.name.string_name == 'tuple' \ | |
309 | and cls_or_tup.get_root_context().is_builtins_module(): | |
310 | # Check for tuples. | |
311 | classes = ValueSet.from_sets( | |
312 | lazy_value.infer() | |
313 | for lazy_value in cls_or_tup.iterate() | |
314 | ) | |
315 | bool_results.add(any(cls in mro for cls in classes)) | |
316 | else: | |
317 | _, lazy_value = list(arguments.unpack())[1] | |
318 | if isinstance(lazy_value, LazyTreeValue): | |
319 | node = lazy_value.data | |
320 | message = 'TypeError: isinstance() arg 2 must be a ' \ | |
321 | 'class, type, or tuple of classes and types, ' \ | |
322 | 'not %s.' % cls_or_tup | |
323 | analysis.add(lazy_value.context, 'type-error-isinstance', node, message) | |
324 | ||
325 | return ValueSet( | |
326 | compiled.builtin_from_name(inference_state, str(b)) | |
327 | for b in bool_results | |
328 | ) | |
329 | ||
330 | ||
331 | class StaticMethodObject(ValueWrapper): | |
332 | def py__get__(self, instance, class_value): | |
333 | return ValueSet([self._wrapped_value]) | |
334 | ||
335 | ||
336 | @argument_clinic('sequence, /') | |
337 | def builtins_staticmethod(functions): | |
338 | return ValueSet(StaticMethodObject(f) for f in functions) | |
339 | ||
340 | ||
341 | class ClassMethodObject(ValueWrapper): | |
342 | def __init__(self, class_method_obj, function): | |
343 | super().__init__(class_method_obj) | |
344 | self._function = function | |
345 | ||
346 | def py__get__(self, instance, class_value): | |
347 | return ValueSet([ | |
348 | ClassMethodGet(__get__, class_value, self._function) | |
349 | for __get__ in self._wrapped_value.py__getattribute__('__get__') | |
350 | ]) | |
351 | ||
352 | ||
353 | class ClassMethodGet(ValueWrapper): | |
354 | def __init__(self, get_method, klass, function): | |
355 | super().__init__(get_method) | |
356 | self._class = klass | |
357 | self._function = function | |
358 | ||
359 | def get_signatures(self): | |
360 | return [sig.bind(self._function) for sig in self._function.get_signatures()] | |
361 | ||
362 | def py__call__(self, arguments): | |
363 | return self._function.execute(ClassMethodArguments(self._class, arguments)) | |
364 | ||
365 | ||
366 | class ClassMethodArguments(TreeArgumentsWrapper): | |
367 | def __init__(self, klass, arguments): | |
368 | super().__init__(arguments) | |
369 | self._class = klass | |
370 | ||
371 | def unpack(self, func=None): | |
372 | yield None, LazyKnownValue(self._class) | |
373 | for values in self._wrapped_arguments.unpack(func): | |
374 | yield values | |
375 | ||
376 | ||
377 | @argument_clinic('sequence, /', want_value=True, want_arguments=True) | |
378 | def builtins_classmethod(functions, value, arguments): | |
379 | return ValueSet( | |
380 | ClassMethodObject(class_method_object, function) | |
381 | for class_method_object in value.py__call__(arguments=arguments) | |
382 | for function in functions | |
383 | ) | |
384 | ||
385 | ||
386 | class PropertyObject(AttributeOverwrite, ValueWrapper): | |
387 | api_type = 'property' | |
388 | ||
389 | def __init__(self, property_obj, function): | |
390 | super().__init__(property_obj) | |
391 | self._function = function | |
392 | ||
393 | def py__get__(self, instance, class_value): | |
394 | if instance is None: | |
395 | return ValueSet([self]) | |
396 | return self._function.execute_with_values(instance) | |
397 | ||
398 | @publish_method('deleter') | |
399 | @publish_method('getter') | |
400 | @publish_method('setter') | |
401 | def _return_self(self, arguments): | |
402 | return ValueSet({self}) | |
403 | ||
404 | ||
405 | @argument_clinic('func, /', want_callback=True) | |
406 | def builtins_property(functions, callback): | |
407 | return ValueSet( | |
408 | PropertyObject(property_value, function) | |
409 | for property_value in callback() | |
410 | for function in functions | |
411 | ) | |
412 | ||
413 | ||
414 | def collections_namedtuple(value, arguments, callback): | |
415 | """ | |
416 | Implementation of the namedtuple function. | |
417 | ||
418 | This has to be done by processing the namedtuple class template and | |
419 | inferring the result. | |
420 | ||
421 | """ | |
422 | inference_state = value.inference_state | |
423 | ||
424 | # Process arguments | |
425 | name = 'jedi_unknown_namedtuple' | |
426 | for c in _follow_param(inference_state, arguments, 0): | |
427 | x = get_str_or_none(c) | |
428 | if x is not None: | |
429 | name = x | |
430 | break | |
431 | ||
432 | # TODO here we only use one of the types, we should use all. | |
433 | param_values = _follow_param(inference_state, arguments, 1) | |
434 | if not param_values: | |
435 | return NO_VALUES | |
436 | _fields = list(param_values)[0] | |
437 | string = get_str_or_none(_fields) | |
438 | if string is not None: | |
439 | fields = string.replace(',', ' ').split() | |
440 | elif isinstance(_fields, iterable.Sequence): | |
441 | fields = [ | |
442 | get_str_or_none(v) | |
443 | for lazy_value in _fields.py__iter__() | |
444 | for v in lazy_value.infer() | |
445 | ] | |
446 | fields = [f for f in fields if f is not None] | |
447 | else: | |
448 | return NO_VALUES | |
449 | ||
450 | # Build source code | |
451 | code = _NAMEDTUPLE_CLASS_TEMPLATE.format( | |
452 | typename=name, | |
453 | field_names=tuple(fields), | |
454 | num_fields=len(fields), | |
455 | arg_list=repr(tuple(fields)).replace("'", "")[1:-1], | |
456 | repr_fmt='', | |
457 | field_defs='\n'.join(_NAMEDTUPLE_FIELD_TEMPLATE.format(index=index, name=name) | |
458 | for index, name in enumerate(fields)) | |
459 | ) | |
460 | ||
461 | # Parse source code | |
462 | module = inference_state.grammar.parse(code) | |
463 | generated_class = next(module.iter_classdefs()) | |
464 | parent_context = ModuleValue( | |
465 | inference_state, module, | |
466 | code_lines=parso.split_lines(code, keepends=True), | |
467 | ).as_context() | |
468 | ||
469 | return ValueSet([ClassValue(inference_state, parent_context, generated_class)]) | |
470 | ||
471 | ||
472 | class PartialObject(ValueWrapper): | |
473 | def __init__(self, actual_value, arguments, instance=None): | |
474 | super().__init__(actual_value) | |
475 | self._arguments = arguments | |
476 | self._instance = instance | |
477 | ||
478 | def _get_functions(self, unpacked_arguments): | |
479 | key, lazy_value = next(unpacked_arguments, (None, None)) | |
480 | if key is not None or lazy_value is None: | |
481 | debug.warning("Partial should have a proper function %s", self._arguments) | |
482 | return None | |
483 | return lazy_value.infer() | |
484 | ||
485 | def get_signatures(self): | |
486 | unpacked_arguments = self._arguments.unpack() | |
487 | funcs = self._get_functions(unpacked_arguments) | |
488 | if funcs is None: | |
489 | return [] | |
490 | ||
491 | arg_count = 0 | |
492 | if self._instance is not None: | |
493 | arg_count = 1 | |
494 | keys = set() | |
495 | for key, _ in unpacked_arguments: | |
496 | if key is None: | |
497 | arg_count += 1 | |
498 | else: | |
499 | keys.add(key) | |
500 | return [PartialSignature(s, arg_count, keys) for s in funcs.get_signatures()] | |
501 | ||
502 | def py__call__(self, arguments): | |
503 | funcs = self._get_functions(self._arguments.unpack()) | |
504 | if funcs is None: | |
505 | return NO_VALUES | |
506 | ||
507 | return funcs.execute( | |
508 | MergedPartialArguments(self._arguments, arguments, self._instance) | |
509 | ) | |
510 | ||
511 | def py__doc__(self): | |
512 | """ | |
513 | In CPython partial does not replace the docstring. However we are still | |
514 | imitating it here, because we want this docstring to be worth something | |
515 | for the user. | |
516 | """ | |
517 | callables = self._get_functions(self._arguments.unpack()) | |
518 | if callables is None: | |
519 | return '' | |
520 | for callable_ in callables: | |
521 | return callable_.py__doc__() | |
522 | return '' | |
523 | ||
524 | def py__get__(self, instance, class_value): | |
525 | return ValueSet([self]) | |
526 | ||
527 | ||
528 | class PartialMethodObject(PartialObject): | |
529 | def py__get__(self, instance, class_value): | |
530 | if instance is None: | |
531 | return ValueSet([self]) | |
532 | return ValueSet([PartialObject(self._wrapped_value, self._arguments, instance)]) | |
533 | ||
534 | ||
535 | class PartialSignature(SignatureWrapper): | |
536 | def __init__(self, wrapped_signature, skipped_arg_count, skipped_arg_set): | |
537 | super().__init__(wrapped_signature) | |
538 | self._skipped_arg_count = skipped_arg_count | |
539 | self._skipped_arg_set = skipped_arg_set | |
540 | ||
541 | def get_param_names(self, resolve_stars=False): | |
542 | names = self._wrapped_signature.get_param_names()[self._skipped_arg_count:] | |
543 | return [n for n in names if n.string_name not in self._skipped_arg_set] | |
544 | ||
545 | ||
546 | class MergedPartialArguments(AbstractArguments): | |
547 | def __init__(self, partial_arguments, call_arguments, instance=None): | |
548 | self._partial_arguments = partial_arguments | |
549 | self._call_arguments = call_arguments | |
550 | self._instance = instance | |
551 | ||
552 | def unpack(self, funcdef=None): | |
553 | unpacked = self._partial_arguments.unpack(funcdef) | |
554 | # Ignore this one, it's the function. It was checked before that it's | |
555 | # there. | |
556 | next(unpacked, None) | |
557 | if self._instance is not None: | |
558 | yield None, LazyKnownValue(self._instance) | |
559 | for key_lazy_value in unpacked: | |
560 | yield key_lazy_value | |
561 | for key_lazy_value in self._call_arguments.unpack(funcdef): | |
562 | yield key_lazy_value | |
563 | ||
564 | ||
565 | def functools_partial(value, arguments, callback): | |
566 | return ValueSet( | |
567 | PartialObject(instance, arguments) | |
568 | for instance in value.py__call__(arguments) | |
569 | ) | |
570 | ||
571 | ||
572 | def functools_partialmethod(value, arguments, callback): | |
573 | return ValueSet( | |
574 | PartialMethodObject(instance, arguments) | |
575 | for instance in value.py__call__(arguments) | |
576 | ) | |
577 | ||
578 | ||
579 | @argument_clinic('first, /') | |
580 | def _return_first_param(firsts): | |
581 | return firsts | |
582 | ||
583 | ||
584 | @argument_clinic('seq') | |
585 | def _random_choice(sequences): | |
586 | return ValueSet.from_sets( | |
587 | lazy_value.infer() | |
588 | for sequence in sequences | |
589 | for lazy_value in sequence.py__iter__() | |
590 | ) | |
591 | ||
592 | ||
593 | def _dataclass(value, arguments, callback): | |
594 | for c in _follow_param(value.inference_state, arguments, 0): | |
595 | if c.is_class(): | |
596 | return ValueSet([DataclassWrapper(c)]) | |
597 | else: | |
598 | return ValueSet([value]) | |
599 | return NO_VALUES | |
600 | ||
601 | ||
602 | class DataclassWrapper(ValueWrapper, ClassMixin): | |
603 | def get_signatures(self): | |
604 | param_names = [] | |
605 | for cls in reversed(list(self.py__mro__())): | |
606 | if isinstance(cls, DataclassWrapper): | |
607 | filter_ = cls.as_context().get_global_filter() | |
608 | # .values ordering is not guaranteed, at least not in | |
609 | # Python < 3.6, when dicts where not ordered, which is an | |
610 | # implementation detail anyway. | |
611 | for name in sorted(filter_.values(), key=lambda name: name.start_pos): | |
612 | d = name.tree_name.get_definition() | |
613 | annassign = d.children[1] | |
614 | if d.type == 'expr_stmt' and annassign.type == 'annassign': | |
615 | if len(annassign.children) < 4: | |
616 | default = None | |
617 | else: | |
618 | default = annassign.children[3] | |
619 | param_names.append(DataclassParamName( | |
620 | parent_context=cls.parent_context, | |
621 | tree_name=name.tree_name, | |
622 | annotation_node=annassign.children[1], | |
623 | default_node=default, | |
624 | )) | |
625 | return [DataclassSignature(cls, param_names)] | |
626 | ||
627 | ||
628 | class DataclassSignature(AbstractSignature): | |
629 | def __init__(self, value, param_names): | |
630 | super().__init__(value) | |
631 | self._param_names = param_names | |
632 | ||
633 | def get_param_names(self, resolve_stars=False): | |
634 | return self._param_names | |
635 | ||
636 | ||
637 | class DataclassParamName(BaseTreeParamName): | |
638 | def __init__(self, parent_context, tree_name, annotation_node, default_node): | |
639 | super().__init__(parent_context, tree_name) | |
640 | self.annotation_node = annotation_node | |
641 | self.default_node = default_node | |
642 | ||
643 | def get_kind(self): | |
644 | return Parameter.POSITIONAL_OR_KEYWORD | |
645 | ||
646 | def infer(self): | |
647 | if self.annotation_node is None: | |
648 | return NO_VALUES | |
649 | else: | |
650 | return self.parent_context.infer_node(self.annotation_node) | |
651 | ||
652 | ||
653 | class ItemGetterCallable(ValueWrapper): | |
654 | def __init__(self, instance, args_value_set): | |
655 | super().__init__(instance) | |
656 | self._args_value_set = args_value_set | |
657 | ||
658 | @repack_with_argument_clinic('item, /') | |
659 | def py__call__(self, item_value_set): | |
660 | value_set = NO_VALUES | |
661 | for args_value in self._args_value_set: | |
662 | lazy_values = list(args_value.py__iter__()) | |
663 | if len(lazy_values) == 1: | |
664 | # TODO we need to add the contextualized value. | |
665 | value_set |= item_value_set.get_item(lazy_values[0].infer(), None) | |
666 | else: | |
667 | value_set |= ValueSet([iterable.FakeList( | |
668 | self._wrapped_value.inference_state, | |
669 | [ | |
670 | LazyKnownValues(item_value_set.get_item(lazy_value.infer(), None)) | |
671 | for lazy_value in lazy_values | |
672 | ], | |
673 | )]) | |
674 | return value_set | |
675 | ||
676 | ||
677 | @argument_clinic('func, /') | |
678 | def _functools_wraps(funcs): | |
679 | return ValueSet(WrapsCallable(func) for func in funcs) | |
680 | ||
681 | ||
682 | class WrapsCallable(ValueWrapper): | |
683 | # XXX this is not the correct wrapped value, it should be a weird | |
684 | # partials object, but it doesn't matter, because it's always used as a | |
685 | # decorator anyway. | |
686 | @repack_with_argument_clinic('func, /') | |
687 | def py__call__(self, funcs): | |
688 | return ValueSet({Wrapped(func, self._wrapped_value) for func in funcs}) | |
689 | ||
690 | ||
691 | class Wrapped(ValueWrapper, FunctionMixin): | |
692 | def __init__(self, func, original_function): | |
693 | super().__init__(func) | |
694 | self._original_function = original_function | |
695 | ||
696 | @property | |
697 | def name(self): | |
698 | return self._original_function.name | |
699 | ||
700 | def get_signature_functions(self): | |
701 | return [self] | |
702 | ||
703 | ||
704 | @argument_clinic('*args, /', want_value=True, want_arguments=True) | |
705 | def _operator_itemgetter(args_value_set, value, arguments): | |
706 | return ValueSet([ | |
707 | ItemGetterCallable(instance, args_value_set) | |
708 | for instance in value.py__call__(arguments) | |
709 | ]) | |
710 | ||
711 | ||
712 | def _create_string_input_function(func): | |
713 | @argument_clinic('string, /', want_value=True, want_arguments=True) | |
714 | def wrapper(strings, value, arguments): | |
715 | def iterate(): | |
716 | for value in strings: | |
717 | s = get_str_or_none(value) | |
718 | if s is not None: | |
719 | s = func(s) | |
720 | yield compiled.create_simple_object(value.inference_state, s) | |
721 | values = ValueSet(iterate()) | |
722 | if values: | |
723 | return values | |
724 | return value.py__call__(arguments) | |
725 | return wrapper | |
726 | ||
727 | ||
728 | @argument_clinic('*args, /', want_callback=True) | |
729 | def _os_path_join(args_set, callback): | |
730 | if len(args_set) == 1: | |
731 | string = '' | |
732 | sequence, = args_set | |
733 | is_first = True | |
734 | for lazy_value in sequence.py__iter__(): | |
735 | string_values = lazy_value.infer() | |
736 | if len(string_values) != 1: | |
737 | break | |
738 | s = get_str_or_none(next(iter(string_values))) | |
739 | if s is None: | |
740 | break | |
741 | if not is_first: | |
742 | string += os.path.sep | |
743 | string += s | |
744 | is_first = False | |
745 | else: | |
746 | return ValueSet([compiled.create_simple_object(sequence.inference_state, string)]) | |
747 | return callback() | |
748 | ||
749 | ||
750 | _implemented = { | |
751 | 'builtins': { | |
752 | 'getattr': builtins_getattr, | |
753 | 'type': builtins_type, | |
754 | 'super': builtins_super, | |
755 | 'reversed': builtins_reversed, | |
756 | 'isinstance': builtins_isinstance, | |
757 | 'next': builtins_next, | |
758 | 'iter': builtins_iter, | |
759 | 'staticmethod': builtins_staticmethod, | |
760 | 'classmethod': builtins_classmethod, | |
761 | 'property': builtins_property, | |
762 | }, | |
763 | 'copy': { | |
764 | 'copy': _return_first_param, | |
765 | 'deepcopy': _return_first_param, | |
766 | }, | |
767 | 'json': { | |
768 | 'load': lambda value, arguments, callback: NO_VALUES, | |
769 | 'loads': lambda value, arguments, callback: NO_VALUES, | |
770 | }, | |
771 | 'collections': { | |
772 | 'namedtuple': collections_namedtuple, | |
773 | }, | |
774 | 'functools': { | |
775 | 'partial': functools_partial, | |
776 | 'partialmethod': functools_partialmethod, | |
777 | 'wraps': _functools_wraps, | |
778 | }, | |
779 | '_weakref': { | |
780 | 'proxy': _return_first_param, | |
781 | }, | |
782 | 'random': { | |
783 | 'choice': _random_choice, | |
784 | }, | |
785 | 'operator': { | |
786 | 'itemgetter': _operator_itemgetter, | |
787 | }, | |
788 | 'abc': { | |
789 | # Not sure if this is necessary, but it's used a lot in typeshed and | |
790 | # it's for now easier to just pass the function. | |
791 | 'abstractmethod': _return_first_param, | |
792 | }, | |
793 | 'typing': { | |
794 | # The _alias function just leads to some annoying type inference. | |
795 | # Therefore, just make it return nothing, which leads to the stubs | |
796 | # being used instead. This only matters for 3.7+. | |
797 | '_alias': lambda value, arguments, callback: NO_VALUES, | |
798 | # runtime_checkable doesn't really change anything and is just | |
799 | # adding logs for infering stuff, so we can safely ignore it. | |
800 | 'runtime_checkable': lambda value, arguments, callback: NO_VALUES, | |
801 | }, | |
802 | 'dataclasses': { | |
803 | # For now this works at least better than Jedi trying to understand it. | |
804 | 'dataclass': _dataclass | |
805 | }, | |
806 | # attrs exposes declaration interface roughly compatible with dataclasses | |
807 | # via attrs.define, attrs.frozen and attrs.mutable | |
808 | # https://www.attrs.org/en/stable/names.html | |
809 | 'attr': { | |
810 | 'define': _dataclass, | |
811 | 'frozen': _dataclass, | |
812 | }, | |
813 | 'attrs': { | |
814 | 'define': _dataclass, | |
815 | 'frozen': _dataclass, | |
816 | }, | |
817 | 'os.path': { | |
818 | 'dirname': _create_string_input_function(os.path.dirname), | |
819 | 'abspath': _create_string_input_function(os.path.abspath), | |
820 | 'relpath': _create_string_input_function(os.path.relpath), | |
821 | 'join': _os_path_join, | |
822 | } | |
823 | } | |
824 | ||
825 | ||
826 | def get_metaclass_filters(func): | |
827 | def wrapper(cls, metaclasses, is_instance): | |
828 | for metaclass in metaclasses: | |
829 | if metaclass.py__name__() == 'EnumMeta' \ | |
830 | and metaclass.get_root_context().py__name__() == 'enum': | |
831 | filter_ = ParserTreeFilter(parent_context=cls.as_context()) | |
832 | return [DictFilter({ | |
833 | name.string_name: EnumInstance(cls, name).name | |
834 | for name in filter_.values() | |
835 | })] | |
836 | return func(cls, metaclasses, is_instance) | |
837 | return wrapper | |
838 | ||
839 | ||
840 | class EnumInstance(LazyValueWrapper): | |
841 | def __init__(self, cls, name): | |
842 | self.inference_state = cls.inference_state | |
843 | self._cls = cls # Corresponds to super().__self__ | |
844 | self._name = name | |
845 | self.tree_node = self._name.tree_name | |
846 | ||
847 | @safe_property | |
848 | def name(self): | |
849 | return ValueName(self, self._name.tree_name) | |
850 | ||
851 | def _get_wrapped_value(self): | |
852 | n = self._name.string_name | |
853 | if n.startswith('__') and n.endswith('__') or self._name.api_type == 'function': | |
854 | inferred = self._name.infer() | |
855 | if inferred: | |
856 | return next(iter(inferred)) | |
857 | o, = self.inference_state.builtins_module.py__getattribute__('object') | |
858 | return o | |
859 | ||
860 | value, = self._cls.execute_with_values() | |
861 | return value | |
862 | ||
863 | def get_filters(self, origin_scope=None): | |
864 | yield DictFilter(dict( | |
865 | name=compiled.create_simple_object(self.inference_state, self._name.string_name).name, | |
866 | value=self._name, | |
867 | )) | |
868 | for f in self._get_wrapped_value().get_filters(): | |
869 | yield f | |
870 | ||
871 | ||
872 | def tree_name_to_values(func): | |
873 | def wrapper(inference_state, context, tree_name): | |
874 | if tree_name.value == 'sep' and context.is_module() and context.py__name__() == 'os.path': | |
875 | return ValueSet({ | |
876 | compiled.create_simple_object(inference_state, os.path.sep), | |
877 | }) | |
878 | return func(inference_state, context, tree_name) | |
879 | return wrapper |