]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | import inspect |
2 | import types | |
3 | import traceback | |
4 | import sys | |
5 | import operator as op | |
6 | from collections import namedtuple | |
7 | import warnings | |
8 | import re | |
9 | import builtins | |
10 | import typing | |
11 | from pathlib import Path | |
12 | from typing import Optional, Tuple | |
13 | ||
14 | from jedi.inference.compiled.getattr_static import getattr_static | |
15 | ||
16 | ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict) | |
17 | ||
18 | MethodDescriptorType = type(str.replace) | |
19 | # These are not considered classes and access is granted even though they have | |
20 | # a __class__ attribute. | |
21 | NOT_CLASS_TYPES = ( | |
22 | types.BuiltinFunctionType, | |
23 | types.CodeType, | |
24 | types.FrameType, | |
25 | types.FunctionType, | |
26 | types.GeneratorType, | |
27 | types.GetSetDescriptorType, | |
28 | types.LambdaType, | |
29 | types.MemberDescriptorType, | |
30 | types.MethodType, | |
31 | types.ModuleType, | |
32 | types.TracebackType, | |
33 | MethodDescriptorType, | |
34 | types.MappingProxyType, | |
35 | types.SimpleNamespace, | |
36 | types.DynamicClassAttribute, | |
37 | ) | |
38 | ||
39 | # Those types don't exist in typing. | |
40 | MethodDescriptorType = type(str.replace) | |
41 | WrapperDescriptorType = type(set.__iter__) | |
42 | # `object.__subclasshook__` is an already executed descriptor. | |
43 | object_class_dict = type.__dict__["__dict__"].__get__(object) # type: ignore[index] | |
44 | ClassMethodDescriptorType = type(object_class_dict['__subclasshook__']) | |
45 | ||
46 | _sentinel = object() | |
47 | ||
48 | # Maps Python syntax to the operator module. | |
49 | COMPARISON_OPERATORS = { | |
50 | '==': op.eq, | |
51 | '!=': op.ne, | |
52 | 'is': op.is_, | |
53 | 'is not': op.is_not, | |
54 | '<': op.lt, | |
55 | '<=': op.le, | |
56 | '>': op.gt, | |
57 | '>=': op.ge, | |
58 | } | |
59 | ||
60 | _OPERATORS = { | |
61 | '+': op.add, | |
62 | '-': op.sub, | |
63 | } | |
64 | _OPERATORS.update(COMPARISON_OPERATORS) | |
65 | ||
66 | ALLOWED_DESCRIPTOR_ACCESS = ( | |
67 | types.FunctionType, | |
68 | types.GetSetDescriptorType, | |
69 | types.MemberDescriptorType, | |
70 | MethodDescriptorType, | |
71 | WrapperDescriptorType, | |
72 | ClassMethodDescriptorType, | |
73 | staticmethod, | |
74 | classmethod, | |
75 | ) | |
76 | ||
77 | ||
78 | def safe_getattr(obj, name, default=_sentinel): | |
79 | try: | |
80 | attr, is_get_descriptor = getattr_static(obj, name) | |
81 | except AttributeError: | |
82 | if default is _sentinel: | |
83 | raise | |
84 | return default | |
85 | else: | |
86 | if isinstance(attr, ALLOWED_DESCRIPTOR_ACCESS): | |
87 | # In case of descriptors that have get methods we cannot return | |
88 | # it's value, because that would mean code execution. | |
89 | # Since it's an isinstance call, code execution is still possible, | |
90 | # but this is not really a security feature, but much more of a | |
91 | # safety feature. Code execution is basically always possible when | |
92 | # a module is imported. This is here so people don't shoot | |
93 | # themselves in the foot. | |
94 | return getattr(obj, name) | |
95 | return attr | |
96 | ||
97 | ||
98 | SignatureParam = namedtuple( | |
99 | 'SignatureParam', | |
100 | 'name has_default default default_string has_annotation annotation annotation_string kind_name' | |
101 | ) | |
102 | ||
103 | ||
104 | def shorten_repr(func): | |
105 | def wrapper(self): | |
106 | r = func(self) | |
107 | if len(r) > 50: | |
108 | r = r[:50] + '..' | |
109 | return r | |
110 | return wrapper | |
111 | ||
112 | ||
113 | def create_access(inference_state, obj): | |
114 | return inference_state.compiled_subprocess.get_or_create_access_handle(obj) | |
115 | ||
116 | ||
117 | def load_module(inference_state, dotted_name, sys_path): | |
118 | temp, sys.path = sys.path, sys_path | |
119 | try: | |
120 | __import__(dotted_name) | |
121 | except ImportError: | |
122 | # If a module is "corrupt" or not really a Python module or whatever. | |
123 | warnings.warn( | |
124 | "Module %s not importable in path %s." % (dotted_name, sys_path), | |
125 | UserWarning, | |
126 | stacklevel=2, | |
127 | ) | |
128 | return None | |
129 | except Exception: | |
130 | # Since __import__ pretty much makes code execution possible, just | |
131 | # catch any error here and print it. | |
132 | warnings.warn( | |
133 | "Cannot import:\n%s" % traceback.format_exc(), UserWarning, stacklevel=2 | |
134 | ) | |
135 | return None | |
136 | finally: | |
137 | sys.path = temp | |
138 | ||
139 | # Just access the cache after import, because of #59 as well as the very | |
140 | # complicated import structure of Python. | |
141 | module = sys.modules[dotted_name] | |
142 | return create_access_path(inference_state, module) | |
143 | ||
144 | ||
145 | class AccessPath: | |
146 | def __init__(self, accesses): | |
147 | self.accesses = accesses | |
148 | ||
149 | ||
150 | def create_access_path(inference_state, obj) -> AccessPath: | |
151 | access = create_access(inference_state, obj) | |
152 | return AccessPath(access.get_access_path_tuples()) | |
153 | ||
154 | ||
155 | def get_api_type(obj): | |
156 | if inspect.isclass(obj): | |
157 | return 'class' | |
158 | elif inspect.ismodule(obj): | |
159 | return 'module' | |
160 | elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \ | |
161 | or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj): | |
162 | return 'function' | |
163 | # Everything else... | |
164 | return 'instance' | |
165 | ||
166 | ||
167 | class DirectObjectAccess: | |
168 | def __init__(self, inference_state, obj): | |
169 | self._inference_state = inference_state | |
170 | self._obj = obj | |
171 | ||
172 | def __repr__(self): | |
173 | return '%s(%s)' % (self.__class__.__name__, self.get_repr()) | |
174 | ||
175 | def _create_access(self, obj): | |
176 | return create_access(self._inference_state, obj) | |
177 | ||
178 | def _create_access_path(self, obj) -> AccessPath: | |
179 | return create_access_path(self._inference_state, obj) | |
180 | ||
181 | def py__bool__(self): | |
182 | return bool(self._obj) | |
183 | ||
184 | def py__file__(self) -> Optional[Path]: | |
185 | try: | |
186 | return Path(self._obj.__file__) | |
187 | except AttributeError: | |
188 | return None | |
189 | ||
190 | def py__doc__(self): | |
191 | return inspect.getdoc(self._obj) or '' | |
192 | ||
193 | def py__name__(self): | |
194 | if not _is_class_instance(self._obj) or \ | |
195 | inspect.ismethoddescriptor(self._obj): # slots | |
196 | cls = self._obj | |
197 | else: | |
198 | try: | |
199 | cls = self._obj.__class__ | |
200 | except AttributeError: | |
201 | # happens with numpy.core.umath._UFUNC_API (you get it | |
202 | # automatically by doing `import numpy`. | |
203 | return None | |
204 | ||
205 | try: | |
206 | return cls.__name__ | |
207 | except AttributeError: | |
208 | return None | |
209 | ||
210 | def py__mro__accesses(self): | |
211 | return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:]) | |
212 | ||
213 | def py__getitem__all_values(self): | |
214 | if isinstance(self._obj, dict): | |
215 | return [self._create_access_path(v) for v in self._obj.values()] | |
216 | if isinstance(self._obj, (list, tuple)): | |
217 | return [self._create_access_path(v) for v in self._obj] | |
218 | ||
219 | if self.is_instance(): | |
220 | cls = DirectObjectAccess(self._inference_state, self._obj.__class__) | |
221 | return cls.py__getitem__all_values() | |
222 | ||
223 | try: | |
224 | getitem = self._obj.__getitem__ | |
225 | except AttributeError: | |
226 | pass | |
227 | else: | |
228 | annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation() | |
229 | if annotation is not None: | |
230 | return [annotation] | |
231 | return None | |
232 | ||
233 | def py__simple_getitem__(self, index, *, safe=True): | |
234 | if safe and type(self._obj) not in ALLOWED_GETITEM_TYPES: | |
235 | # Get rid of side effects, we won't call custom `__getitem__`s. | |
236 | return None | |
237 | ||
238 | return self._create_access_path(self._obj[index]) | |
239 | ||
240 | def py__iter__list(self): | |
241 | try: | |
242 | iter_method = self._obj.__iter__ | |
243 | except AttributeError: | |
244 | return None | |
245 | else: | |
246 | p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation() | |
247 | if p is not None: | |
248 | return [p] | |
249 | ||
250 | if type(self._obj) not in ALLOWED_GETITEM_TYPES: | |
251 | # Get rid of side effects, we won't call custom `__getitem__`s. | |
252 | return [] | |
253 | ||
254 | lst = [] | |
255 | for i, part in enumerate(self._obj): | |
256 | if i > 20: | |
257 | # Should not go crazy with large iterators | |
258 | break | |
259 | lst.append(self._create_access_path(part)) | |
260 | return lst | |
261 | ||
262 | def py__class__(self): | |
263 | return self._create_access_path(self._obj.__class__) | |
264 | ||
265 | def py__bases__(self): | |
266 | return [self._create_access_path(base) for base in self._obj.__bases__] | |
267 | ||
268 | def py__path__(self): | |
269 | paths = getattr(self._obj, '__path__', None) | |
270 | # Avoid some weird hacks that would just fail, because they cannot be | |
271 | # used by pickle. | |
272 | if not isinstance(paths, list) \ | |
273 | or not all(isinstance(p, str) for p in paths): | |
274 | return None | |
275 | return paths | |
276 | ||
277 | @shorten_repr | |
278 | def get_repr(self): | |
279 | if inspect.ismodule(self._obj): | |
280 | return repr(self._obj) | |
281 | # Try to avoid execution of the property. | |
282 | if safe_getattr(self._obj, '__module__', default='') == 'builtins': | |
283 | return repr(self._obj) | |
284 | ||
285 | type_ = type(self._obj) | |
286 | if type_ == type: | |
287 | return type.__repr__(self._obj) | |
288 | ||
289 | if safe_getattr(type_, '__module__', default='') == 'builtins': | |
290 | # Allow direct execution of repr for builtins. | |
291 | return repr(self._obj) | |
292 | return object.__repr__(self._obj) | |
293 | ||
294 | def is_class(self): | |
295 | return inspect.isclass(self._obj) | |
296 | ||
297 | def is_function(self): | |
298 | return inspect.isfunction(self._obj) or inspect.ismethod(self._obj) | |
299 | ||
300 | def is_module(self): | |
301 | return inspect.ismodule(self._obj) | |
302 | ||
303 | def is_instance(self): | |
304 | return _is_class_instance(self._obj) | |
305 | ||
306 | def ismethoddescriptor(self): | |
307 | return inspect.ismethoddescriptor(self._obj) | |
308 | ||
309 | def get_qualified_names(self): | |
310 | def try_to_get_name(obj): | |
311 | return getattr(obj, '__qualname__', getattr(obj, '__name__', None)) | |
312 | ||
313 | if self.is_module(): | |
314 | return () | |
315 | name = try_to_get_name(self._obj) | |
316 | if name is None: | |
317 | name = try_to_get_name(type(self._obj)) | |
318 | if name is None: | |
319 | return () | |
320 | return tuple(name.split('.')) | |
321 | ||
322 | def dir(self): | |
323 | return dir(self._obj) | |
324 | ||
325 | def has_iter(self): | |
326 | try: | |
327 | iter(self._obj) | |
328 | return True | |
329 | except TypeError: | |
330 | return False | |
331 | ||
332 | def is_allowed_getattr(self, name, safe=True) -> Tuple[bool, bool, Optional[AccessPath]]: | |
333 | # TODO this API is ugly. | |
334 | try: | |
335 | attr, is_get_descriptor = getattr_static(self._obj, name) | |
336 | except AttributeError: | |
337 | if not safe: | |
338 | # Unsafe is mostly used to check for __getattr__/__getattribute__. | |
339 | # getattr_static works for properties, but the underscore methods | |
340 | # are just ignored (because it's safer and avoids more code | |
341 | # execution). See also GH #1378. | |
342 | ||
343 | # Avoid warnings, see comment in the next function. | |
344 | with warnings.catch_warnings(record=True): | |
345 | warnings.simplefilter("always") | |
346 | try: | |
347 | return hasattr(self._obj, name), False, None | |
348 | except Exception: | |
349 | # Obviously has an attribute (probably a property) that | |
350 | # gets executed, so just avoid all exceptions here. | |
351 | pass | |
352 | return False, False, None | |
353 | else: | |
354 | if is_get_descriptor and type(attr) not in ALLOWED_DESCRIPTOR_ACCESS: | |
355 | if isinstance(attr, property): | |
356 | if hasattr(attr.fget, '__annotations__'): | |
357 | a = DirectObjectAccess(self._inference_state, attr.fget) | |
358 | return True, True, a.get_return_annotation() | |
359 | # In case of descriptors that have get methods we cannot return | |
360 | # it's value, because that would mean code execution. | |
361 | return True, True, None | |
362 | return True, False, None | |
363 | ||
364 | def getattr_paths(self, name, default=_sentinel): | |
365 | try: | |
366 | # Make sure no warnings are printed here, this is autocompletion, | |
367 | # warnings should not be shown. See also GH #1383. | |
368 | with warnings.catch_warnings(record=True): | |
369 | warnings.simplefilter("always") | |
370 | return_obj = getattr(self._obj, name) | |
371 | except Exception as e: | |
372 | if default is _sentinel: | |
373 | if isinstance(e, AttributeError): | |
374 | # Happens e.g. in properties of | |
375 | # PyQt4.QtGui.QStyleOptionComboBox.currentText | |
376 | # -> just set it to None | |
377 | raise | |
378 | # Just in case anything happens, return an AttributeError. It | |
379 | # should not crash. | |
380 | raise AttributeError | |
381 | return_obj = default | |
382 | access = self._create_access(return_obj) | |
383 | if inspect.ismodule(return_obj): | |
384 | return [access] | |
385 | ||
386 | try: | |
387 | module = return_obj.__module__ | |
388 | except AttributeError: | |
389 | pass | |
390 | else: | |
391 | if module is not None and isinstance(module, str): | |
392 | try: | |
393 | __import__(module) | |
394 | # For some modules like _sqlite3, the __module__ for classes is | |
395 | # different, in this case it's sqlite3. So we have to try to | |
396 | # load that "original" module, because it's not loaded yet. If | |
397 | # we don't do that, we don't really have a "parent" module and | |
398 | # we would fall back to builtins. | |
399 | except ImportError: | |
400 | pass | |
401 | ||
402 | module = inspect.getmodule(return_obj) | |
403 | if module is None: | |
404 | module = inspect.getmodule(type(return_obj)) | |
405 | if module is None: | |
406 | module = builtins | |
407 | return [self._create_access(module), access] | |
408 | ||
409 | def get_safe_value(self): | |
410 | if type(self._obj) in (bool, bytes, float, int, str, slice) or self._obj is None: | |
411 | return self._obj | |
412 | raise ValueError("Object is type %s and not simple" % type(self._obj)) | |
413 | ||
414 | def get_api_type(self): | |
415 | return get_api_type(self._obj) | |
416 | ||
417 | def get_array_type(self): | |
418 | if isinstance(self._obj, dict): | |
419 | return 'dict' | |
420 | return None | |
421 | ||
422 | def get_key_paths(self): | |
423 | def iter_partial_keys(): | |
424 | # We could use list(keys()), but that might take a lot more memory. | |
425 | for (i, k) in enumerate(self._obj.keys()): | |
426 | # Limit key listing at some point. This is artificial, but this | |
427 | # way we don't get stalled because of slow completions | |
428 | if i > 50: | |
429 | break | |
430 | yield k | |
431 | ||
432 | return [self._create_access_path(k) for k in iter_partial_keys()] | |
433 | ||
434 | def get_access_path_tuples(self): | |
435 | accesses = [create_access(self._inference_state, o) for o in self._get_objects_path()] | |
436 | return [(access.py__name__(), access) for access in accesses] | |
437 | ||
438 | def _get_objects_path(self): | |
439 | def get(): | |
440 | obj = self._obj | |
441 | yield obj | |
442 | try: | |
443 | obj = obj.__objclass__ | |
444 | except AttributeError: | |
445 | pass | |
446 | else: | |
447 | yield obj | |
448 | ||
449 | try: | |
450 | # Returns a dotted string path. | |
451 | imp_plz = obj.__module__ | |
452 | except AttributeError: | |
453 | # Unfortunately in some cases like `int` there's no __module__ | |
454 | if not inspect.ismodule(obj): | |
455 | yield builtins | |
456 | else: | |
457 | if imp_plz is None: | |
458 | # Happens for example in `(_ for _ in []).send.__module__`. | |
459 | yield builtins | |
460 | else: | |
461 | try: | |
462 | yield sys.modules[imp_plz] | |
463 | except KeyError: | |
464 | # __module__ can be something arbitrary that doesn't exist. | |
465 | yield builtins | |
466 | ||
467 | return list(reversed(list(get()))) | |
468 | ||
469 | def execute_operation(self, other_access_handle, operator): | |
470 | other_access = other_access_handle.access | |
471 | op = _OPERATORS[operator] | |
472 | return self._create_access_path(op(self._obj, other_access._obj)) | |
473 | ||
474 | def get_annotation_name_and_args(self): | |
475 | """ | |
476 | Returns Tuple[Optional[str], Tuple[AccessPath, ...]] | |
477 | """ | |
478 | name = None | |
479 | args = () | |
480 | if safe_getattr(self._obj, '__module__', default='') == 'typing': | |
481 | m = re.match(r'typing.(\w+)\[', repr(self._obj)) | |
482 | if m is not None: | |
483 | name = m.group(1) | |
484 | ||
485 | import typing | |
486 | if sys.version_info >= (3, 8): | |
487 | args = typing.get_args(self._obj) | |
488 | else: | |
489 | args = safe_getattr(self._obj, '__args__', default=None) | |
490 | return name, tuple(self._create_access_path(arg) for arg in args) | |
491 | ||
492 | def needs_type_completions(self): | |
493 | return inspect.isclass(self._obj) and self._obj != type | |
494 | ||
495 | def _annotation_to_str(self, annotation): | |
496 | return inspect.formatannotation(annotation) | |
497 | ||
498 | def get_signature_params(self): | |
499 | return [ | |
500 | SignatureParam( | |
501 | name=p.name, | |
502 | has_default=p.default is not p.empty, | |
503 | default=self._create_access_path(p.default), | |
504 | default_string=repr(p.default), | |
505 | has_annotation=p.annotation is not p.empty, | |
506 | annotation=self._create_access_path(p.annotation), | |
507 | annotation_string=self._annotation_to_str(p.annotation), | |
508 | kind_name=str(p.kind) | |
509 | ) for p in self._get_signature().parameters.values() | |
510 | ] | |
511 | ||
512 | def _get_signature(self): | |
513 | obj = self._obj | |
514 | try: | |
515 | return inspect.signature(obj) | |
516 | except (RuntimeError, TypeError): | |
517 | # Reading the code of the function in Python 3.6 implies there are | |
518 | # at least these errors that might occur if something is wrong with | |
519 | # the signature. In that case we just want a simple escape for now. | |
520 | raise ValueError | |
521 | ||
522 | def get_return_annotation(self) -> Optional[AccessPath]: | |
523 | try: | |
524 | o = self._obj.__annotations__.get('return') | |
525 | except AttributeError: | |
526 | return None | |
527 | ||
528 | if o is None: | |
529 | return None | |
530 | ||
531 | try: | |
532 | o = typing.get_type_hints(self._obj).get('return') | |
533 | except Exception: | |
534 | pass | |
535 | ||
536 | return self._create_access_path(o) | |
537 | ||
538 | def negate(self): | |
539 | return self._create_access_path(-self._obj) | |
540 | ||
541 | def get_dir_infos(self): | |
542 | """ | |
543 | Used to return a couple of infos that are needed when accessing the sub | |
544 | objects of an objects | |
545 | """ | |
546 | tuples = dict( | |
547 | (name, self.is_allowed_getattr(name)) | |
548 | for name in self.dir() | |
549 | ) | |
550 | return self.needs_type_completions(), tuples | |
551 | ||
552 | ||
553 | def _is_class_instance(obj): | |
554 | """Like inspect.* methods.""" | |
555 | try: | |
556 | cls = obj.__class__ | |
557 | except AttributeError: | |
558 | return False | |
559 | else: | |
560 | # The isinstance check for cls is just there so issubclass doesn't | |
561 | # raise an exception. | |
562 | return cls != type and isinstance(cls, type) and not issubclass(cls, NOT_CLASS_TYPES) |