Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/compiled/access.py: 24%

330 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1import inspect 

2import types 

3import traceback 

4import sys 

5import operator as op 

6from collections import namedtuple 

7import warnings 

8import re 

9import builtins 

10import typing 

11from pathlib import Path 

12from typing import Optional, Tuple 

13 

14from jedi.inference.compiled.getattr_static import getattr_static 

15 

16ALLOWED_GETITEM_TYPES = (str, list, tuple, bytes, bytearray, dict) 

17 

18MethodDescriptorType = type(str.replace) 

19# These are not considered classes and access is granted even though they have 

20# a __class__ attribute. 

21NOT_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. 

40MethodDescriptorType = type(str.replace) 

41WrapperDescriptorType = type(set.__iter__) 

42# `object.__subclasshook__` is an already executed descriptor. 

43object_class_dict = type.__dict__["__dict__"].__get__(object) # type: ignore[index] 

44ClassMethodDescriptorType = type(object_class_dict['__subclasshook__']) 

45 

46_sentinel = object() 

47 

48# Maps Python syntax to the operator module. 

49COMPARISON_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 

66ALLOWED_DESCRIPTOR_ACCESS = ( 

67 types.FunctionType, 

68 types.GetSetDescriptorType, 

69 types.MemberDescriptorType, 

70 MethodDescriptorType, 

71 WrapperDescriptorType, 

72 ClassMethodDescriptorType, 

73 staticmethod, 

74 classmethod, 

75) 

76 

77 

78def 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 

98SignatureParam = namedtuple( 

99 'SignatureParam', 

100 'name has_default default default_string has_annotation annotation annotation_string kind_name' 

101) 

102 

103 

104def 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 

113def create_access(inference_state, obj): 

114 return inference_state.compiled_subprocess.get_or_create_access_handle(obj) 

115 

116 

117def 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 

145class AccessPath: 

146 def __init__(self, accesses): 

147 self.accesses = accesses 

148 

149 

150def create_access_path(inference_state, obj) -> AccessPath: 

151 access = create_access(inference_state, obj) 

152 return AccessPath(access.get_access_path_tuples()) 

153 

154 

155def 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 

167class 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 

553def _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)