Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/api/completion.py: 14%

379 statements  

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

1import re 

2from textwrap import dedent 

3from inspect import Parameter 

4 

5from parso.python.token import PythonTokenTypes 

6from parso.python import tree 

7from parso.tree import search_ancestor, Leaf 

8from parso import split_lines 

9 

10from jedi import debug 

11from jedi import settings 

12from jedi.api import classes 

13from jedi.api import helpers 

14from jedi.api import keywords 

15from jedi.api.strings import complete_dict 

16from jedi.api.file_name import complete_file_name 

17from jedi.inference import imports 

18from jedi.inference.base_value import ValueSet 

19from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names 

20from jedi.inference.context import get_global_filters 

21from jedi.inference.value import TreeInstance 

22from jedi.inference.docstring_utils import DocstringModule 

23from jedi.inference.names import ParamNameWrapper, SubModuleName 

24from jedi.inference.gradual.conversion import convert_values, convert_names 

25from jedi.parser_utils import cut_value_at_position 

26from jedi.plugins import plugin_manager 

27 

28 

29class ParamNameWithEquals(ParamNameWrapper): 

30 def get_public_name(self): 

31 return self.string_name + '=' 

32 

33 

34def _get_signature_param_names(signatures, positional_count, used_kwargs): 

35 # Add named params 

36 for call_sig in signatures: 

37 for i, p in enumerate(call_sig.params): 

38 kind = p.kind 

39 if i < positional_count and kind == Parameter.POSITIONAL_OR_KEYWORD: 

40 continue 

41 if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) \ 

42 and p.name not in used_kwargs: 

43 yield ParamNameWithEquals(p._name) 

44 

45 

46def _must_be_kwarg(signatures, positional_count, used_kwargs): 

47 if used_kwargs: 

48 return True 

49 

50 must_be_kwarg = True 

51 for signature in signatures: 

52 for i, p in enumerate(signature.params): 

53 kind = p.kind 

54 if kind is Parameter.VAR_POSITIONAL: 

55 # In case there were not already kwargs, the next param can 

56 # always be a normal argument. 

57 return False 

58 

59 if i >= positional_count and kind in (Parameter.POSITIONAL_OR_KEYWORD, 

60 Parameter.POSITIONAL_ONLY): 

61 must_be_kwarg = False 

62 break 

63 if not must_be_kwarg: 

64 break 

65 return must_be_kwarg 

66 

67 

68def filter_names(inference_state, completion_names, stack, like_name, fuzzy, cached_name): 

69 comp_dct = set() 

70 if settings.case_insensitive_completion: 

71 like_name = like_name.lower() 

72 for name in completion_names: 

73 string = name.string_name 

74 if settings.case_insensitive_completion: 

75 string = string.lower() 

76 if helpers.match(string, like_name, fuzzy=fuzzy): 

77 new = classes.Completion( 

78 inference_state, 

79 name, 

80 stack, 

81 len(like_name), 

82 is_fuzzy=fuzzy, 

83 cached_name=cached_name, 

84 ) 

85 k = (new.name, new.complete) # key 

86 if k not in comp_dct: 

87 comp_dct.add(k) 

88 tree_name = name.tree_name 

89 if tree_name is not None: 

90 definition = tree_name.get_definition() 

91 if definition is not None and definition.type == 'del_stmt': 

92 continue 

93 yield new 

94 

95 

96def _remove_duplicates(completions, other_completions): 

97 names = {d.name for d in other_completions} 

98 return [c for c in completions if c.name not in names] 

99 

100 

101def get_user_context(module_context, position): 

102 """ 

103 Returns the scope in which the user resides. This includes flows. 

104 """ 

105 leaf = module_context.tree_node.get_leaf_for_position(position, include_prefixes=True) 

106 return module_context.create_context(leaf) 

107 

108 

109def get_flow_scope_node(module_node, position): 

110 node = module_node.get_leaf_for_position(position, include_prefixes=True) 

111 while not isinstance(node, (tree.Scope, tree.Flow)): 

112 node = node.parent 

113 

114 return node 

115 

116 

117@plugin_manager.decorate() 

118def complete_param_names(context, function_name, decorator_nodes): 

119 # Basically there's no way to do param completion. The plugins are 

120 # responsible for this. 

121 return [] 

122 

123 

124class Completion: 

125 def __init__(self, inference_state, module_context, code_lines, position, 

126 signatures_callback, fuzzy=False): 

127 self._inference_state = inference_state 

128 self._module_context = module_context 

129 self._module_node = module_context.tree_node 

130 self._code_lines = code_lines 

131 

132 # The first step of completions is to get the name 

133 self._like_name = helpers.get_on_completion_name(self._module_node, code_lines, position) 

134 # The actual cursor position is not what we need to calculate 

135 # everything. We want the start of the name we're on. 

136 self._original_position = position 

137 self._signatures_callback = signatures_callback 

138 

139 self._fuzzy = fuzzy 

140 

141 def complete(self): 

142 leaf = self._module_node.get_leaf_for_position( 

143 self._original_position, 

144 include_prefixes=True 

145 ) 

146 string, start_leaf, quote = _extract_string_while_in_string(leaf, self._original_position) 

147 

148 prefixed_completions = complete_dict( 

149 self._module_context, 

150 self._code_lines, 

151 start_leaf or leaf, 

152 self._original_position, 

153 None if string is None else quote + string, 

154 fuzzy=self._fuzzy, 

155 ) 

156 

157 if string is not None and not prefixed_completions: 

158 prefixed_completions = list(complete_file_name( 

159 self._inference_state, self._module_context, start_leaf, quote, string, 

160 self._like_name, self._signatures_callback, 

161 self._code_lines, self._original_position, 

162 self._fuzzy 

163 )) 

164 if string is not None: 

165 if not prefixed_completions and '\n' in string: 

166 # Complete only multi line strings 

167 prefixed_completions = self._complete_in_string(start_leaf, string) 

168 return prefixed_completions 

169 

170 cached_name, completion_names = self._complete_python(leaf) 

171 

172 completions = list(filter_names(self._inference_state, completion_names, 

173 self.stack, self._like_name, 

174 self._fuzzy, cached_name=cached_name)) 

175 

176 return ( 

177 # Removing duplicates mostly to remove False/True/None duplicates. 

178 _remove_duplicates(prefixed_completions, completions) 

179 + sorted(completions, key=lambda x: (x.name.startswith('__'), 

180 x.name.startswith('_'), 

181 x.name.lower())) 

182 ) 

183 

184 def _complete_python(self, leaf): 

185 """ 

186 Analyzes the current context of a completion and decides what to 

187 return. 

188 

189 Technically this works by generating a parser stack and analysing the 

190 current stack for possible grammar nodes. 

191 

192 Possible enhancements: 

193 - global/nonlocal search global 

194 - yield from / raise from <- could be only exceptions/generators 

195 - In args: */**: no completion 

196 - In params (also lambda): no completion before = 

197 """ 

198 grammar = self._inference_state.grammar 

199 self.stack = stack = None 

200 self._position = ( 

201 self._original_position[0], 

202 self._original_position[1] - len(self._like_name) 

203 ) 

204 cached_name = None 

205 

206 try: 

207 self.stack = stack = helpers.get_stack_at_position( 

208 grammar, self._code_lines, leaf, self._position 

209 ) 

210 except helpers.OnErrorLeaf as e: 

211 value = e.error_leaf.value 

212 if value == '.': 

213 # After ErrorLeaf's that are dots, we will not do any 

214 # completions since this probably just confuses the user. 

215 return cached_name, [] 

216 

217 # If we don't have a value, just use global completion. 

218 return cached_name, self._complete_global_scope() 

219 

220 allowed_transitions = \ 

221 list(stack._allowed_transition_names_and_token_types()) 

222 

223 if 'if' in allowed_transitions: 

224 leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True) 

225 previous_leaf = leaf.get_previous_leaf() 

226 

227 indent = self._position[1] 

228 if not (leaf.start_pos <= self._position <= leaf.end_pos): 

229 indent = leaf.start_pos[1] 

230 

231 if previous_leaf is not None: 

232 stmt = previous_leaf 

233 while True: 

234 stmt = search_ancestor( 

235 stmt, 'if_stmt', 'for_stmt', 'while_stmt', 'try_stmt', 

236 'error_node', 

237 ) 

238 if stmt is None: 

239 break 

240 

241 type_ = stmt.type 

242 if type_ == 'error_node': 

243 first = stmt.children[0] 

244 if isinstance(first, Leaf): 

245 type_ = first.value + '_stmt' 

246 # Compare indents 

247 if stmt.start_pos[1] == indent: 

248 if type_ == 'if_stmt': 

249 allowed_transitions += ['elif', 'else'] 

250 elif type_ == 'try_stmt': 

251 allowed_transitions += ['except', 'finally', 'else'] 

252 elif type_ == 'for_stmt': 

253 allowed_transitions.append('else') 

254 

255 completion_names = [] 

256 

257 kwargs_only = False 

258 if any(t in allowed_transitions for t in (PythonTokenTypes.NAME, 

259 PythonTokenTypes.INDENT)): 

260 # This means that we actually have to do type inference. 

261 

262 nonterminals = [stack_node.nonterminal for stack_node in stack] 

263 

264 nodes = _gather_nodes(stack) 

265 if nodes and nodes[-1] in ('as', 'def', 'class'): 

266 # No completions for ``with x as foo`` and ``import x as foo``. 

267 # Also true for defining names as a class or function. 

268 return cached_name, list(self._complete_inherited(is_function=True)) 

269 elif "import_stmt" in nonterminals: 

270 level, names = parse_dotted_names(nodes, "import_from" in nonterminals) 

271 

272 only_modules = not ("import_from" in nonterminals and 'import' in nodes) 

273 completion_names += self._get_importer_names( 

274 names, 

275 level, 

276 only_modules=only_modules, 

277 ) 

278 elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.': 

279 dot = self._module_node.get_leaf_for_position(self._position) 

280 if dot.type == "endmarker": 

281 # This is a bit of a weird edge case, maybe we can somehow 

282 # generalize this. 

283 dot = leaf.get_previous_leaf() 

284 cached_name, n = self._complete_trailer(dot.get_previous_leaf()) 

285 completion_names += n 

286 elif self._is_parameter_completion(): 

287 completion_names += self._complete_params(leaf) 

288 else: 

289 # Apparently this looks like it's good enough to filter most cases 

290 # so that signature completions don't randomly appear. 

291 # To understand why this works, three things are important: 

292 # 1. trailer with a `,` in it is either a subscript or an arglist. 

293 # 2. If there's no `,`, it's at the start and only signatures start 

294 # with `(`. Other trailers could start with `.` or `[`. 

295 # 3. Decorators are very primitive and have an optional `(` with 

296 # optional arglist in them. 

297 if nodes[-1] in ['(', ','] \ 

298 and nonterminals[-1] in ('trailer', 'arglist', 'decorator'): 

299 signatures = self._signatures_callback(*self._position) 

300 if signatures: 

301 call_details = signatures[0]._call_details 

302 used_kwargs = list(call_details.iter_used_keyword_arguments()) 

303 positional_count = call_details.count_positional_arguments() 

304 

305 completion_names += _get_signature_param_names( 

306 signatures, 

307 positional_count, 

308 used_kwargs, 

309 ) 

310 

311 kwargs_only = _must_be_kwarg(signatures, positional_count, used_kwargs) 

312 

313 if not kwargs_only: 

314 completion_names += self._complete_global_scope() 

315 completion_names += self._complete_inherited(is_function=False) 

316 

317 if not kwargs_only: 

318 current_line = self._code_lines[self._position[0] - 1][:self._position[1]] 

319 completion_names += self._complete_keywords( 

320 allowed_transitions, 

321 only_values=not (not current_line or current_line[-1] in ' \t.;' 

322 and current_line[-3:] != '...') 

323 ) 

324 

325 return cached_name, completion_names 

326 

327 def _is_parameter_completion(self): 

328 tos = self.stack[-1] 

329 if tos.nonterminal == 'lambdef' and len(tos.nodes) == 1: 

330 # We are at the position `lambda `, where basically the next node 

331 # is a param. 

332 return True 

333 if tos.nonterminal in 'parameters': 

334 # Basically we are at the position `foo(`, there's nothing there 

335 # yet, so we have no `typedargslist`. 

336 return True 

337 # var args is for lambdas and typed args for normal functions 

338 return tos.nonterminal in ('typedargslist', 'varargslist') and tos.nodes[-1] == ',' 

339 

340 def _complete_params(self, leaf): 

341 stack_node = self.stack[-2] 

342 if stack_node.nonterminal == 'parameters': 

343 stack_node = self.stack[-3] 

344 if stack_node.nonterminal == 'funcdef': 

345 context = get_user_context(self._module_context, self._position) 

346 node = search_ancestor(leaf, 'error_node', 'funcdef') 

347 if node is not None: 

348 if node.type == 'error_node': 

349 n = node.children[0] 

350 if n.type == 'decorators': 

351 decorators = n.children 

352 elif n.type == 'decorator': 

353 decorators = [n] 

354 else: 

355 decorators = [] 

356 else: 

357 decorators = node.get_decorators() 

358 function_name = stack_node.nodes[1] 

359 

360 return complete_param_names(context, function_name.value, decorators) 

361 return [] 

362 

363 def _complete_keywords(self, allowed_transitions, only_values): 

364 for k in allowed_transitions: 

365 if isinstance(k, str) and k.isalpha(): 

366 if not only_values or k in ('True', 'False', 'None'): 

367 yield keywords.KeywordName(self._inference_state, k) 

368 

369 def _complete_global_scope(self): 

370 context = get_user_context(self._module_context, self._position) 

371 debug.dbg('global completion scope: %s', context) 

372 flow_scope_node = get_flow_scope_node(self._module_node, self._position) 

373 filters = get_global_filters( 

374 context, 

375 self._position, 

376 flow_scope_node 

377 ) 

378 completion_names = [] 

379 for filter in filters: 

380 completion_names += filter.values() 

381 return completion_names 

382 

383 def _complete_trailer(self, previous_leaf): 

384 inferred_context = self._module_context.create_context(previous_leaf) 

385 values = infer_call_of_leaf(inferred_context, previous_leaf) 

386 debug.dbg('trailer completion values: %s', values, color='MAGENTA') 

387 

388 # The cached name simply exists to make speed optimizations for certain 

389 # modules. 

390 cached_name = None 

391 if len(values) == 1: 

392 v, = values 

393 if v.is_module(): 

394 if len(v.string_names) == 1: 

395 module_name = v.string_names[0] 

396 if module_name in ('numpy', 'tensorflow', 'matplotlib', 'pandas'): 

397 cached_name = module_name 

398 

399 return cached_name, self._complete_trailer_for_values(values) 

400 

401 def _complete_trailer_for_values(self, values): 

402 user_context = get_user_context(self._module_context, self._position) 

403 

404 return complete_trailer(user_context, values) 

405 

406 def _get_importer_names(self, names, level=0, only_modules=True): 

407 names = [n.value for n in names] 

408 i = imports.Importer(self._inference_state, names, self._module_context, level) 

409 return i.completion_names(self._inference_state, only_modules=only_modules) 

410 

411 def _complete_inherited(self, is_function=True): 

412 """ 

413 Autocomplete inherited methods when overriding in child class. 

414 """ 

415 leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True) 

416 cls = tree.search_ancestor(leaf, 'classdef') 

417 if cls is None: 

418 return 

419 

420 # Complete the methods that are defined in the super classes. 

421 class_value = self._module_context.create_value(cls) 

422 

423 if cls.start_pos[1] >= leaf.start_pos[1]: 

424 return 

425 

426 filters = class_value.get_filters(is_instance=True) 

427 # The first dict is the dictionary of class itself. 

428 next(filters) 

429 for filter in filters: 

430 for name in filter.values(): 

431 # TODO we should probably check here for properties 

432 if (name.api_type == 'function') == is_function: 

433 yield name 

434 

435 def _complete_in_string(self, start_leaf, string): 

436 """ 

437 To make it possible for people to have completions in doctests or 

438 generally in "Python" code in docstrings, we use the following 

439 heuristic: 

440 

441 - Having an indented block of code 

442 - Having some doctest code that starts with `>>>` 

443 - Having backticks that doesn't have whitespace inside it 

444 """ 

445 def iter_relevant_lines(lines): 

446 include_next_line = False 

447 for l in code_lines: 

448 if include_next_line or l.startswith('>>>') or l.startswith(' '): 

449 yield re.sub(r'^( *>>> ?| +)', '', l) 

450 else: 

451 yield None 

452 

453 include_next_line = bool(re.match(' *>>>', l)) 

454 

455 string = dedent(string) 

456 code_lines = split_lines(string, keepends=True) 

457 relevant_code_lines = list(iter_relevant_lines(code_lines)) 

458 if relevant_code_lines[-1] is not None: 

459 # Some code lines might be None, therefore get rid of that. 

460 relevant_code_lines = ['\n' if c is None else c for c in relevant_code_lines] 

461 return self._complete_code_lines(relevant_code_lines) 

462 match = re.search(r'`([^`\s]+)', code_lines[-1]) 

463 if match: 

464 return self._complete_code_lines([match.group(1)]) 

465 return [] 

466 

467 def _complete_code_lines(self, code_lines): 

468 module_node = self._inference_state.grammar.parse(''.join(code_lines)) 

469 module_value = DocstringModule( 

470 in_module_context=self._module_context, 

471 inference_state=self._inference_state, 

472 module_node=module_node, 

473 code_lines=code_lines, 

474 ) 

475 return Completion( 

476 self._inference_state, 

477 module_value.as_context(), 

478 code_lines=code_lines, 

479 position=module_node.end_pos, 

480 signatures_callback=lambda *args, **kwargs: [], 

481 fuzzy=self._fuzzy 

482 ).complete() 

483 

484 

485def _gather_nodes(stack): 

486 nodes = [] 

487 for stack_node in stack: 

488 if stack_node.dfa.from_rule == 'small_stmt': 

489 nodes = [] 

490 else: 

491 nodes += stack_node.nodes 

492 return nodes 

493 

494 

495_string_start = re.compile(r'^\w*(\'{3}|"{3}|\'|")') 

496 

497 

498def _extract_string_while_in_string(leaf, position): 

499 def return_part_of_leaf(leaf): 

500 kwargs = {} 

501 if leaf.line == position[0]: 

502 kwargs['endpos'] = position[1] - leaf.column 

503 match = _string_start.match(leaf.value, **kwargs) 

504 if not match: 

505 return None, None, None 

506 start = match.group(0) 

507 if leaf.line == position[0] and position[1] < leaf.column + match.end(): 

508 return None, None, None 

509 return cut_value_at_position(leaf, position)[match.end():], leaf, start 

510 

511 if position < leaf.start_pos: 

512 return None, None, None 

513 

514 if leaf.type == 'string': 

515 return return_part_of_leaf(leaf) 

516 

517 leaves = [] 

518 while leaf is not None: 

519 if leaf.type == 'error_leaf' and ('"' in leaf.value or "'" in leaf.value): 

520 if len(leaf.value) > 1: 

521 return return_part_of_leaf(leaf) 

522 prefix_leaf = None 

523 if not leaf.prefix: 

524 prefix_leaf = leaf.get_previous_leaf() 

525 if prefix_leaf is None or prefix_leaf.type != 'name' \ 

526 or not all(c in 'rubf' for c in prefix_leaf.value.lower()): 

527 prefix_leaf = None 

528 

529 return ( 

530 ''.join(cut_value_at_position(l, position) for l in leaves), 

531 prefix_leaf or leaf, 

532 ('' if prefix_leaf is None else prefix_leaf.value) 

533 + cut_value_at_position(leaf, position), 

534 ) 

535 if leaf.line != position[0]: 

536 # Multi line strings are always simple error leaves and contain the 

537 # whole string, single line error leaves are atherefore important 

538 # now and since the line is different, it's not really a single 

539 # line string anymore. 

540 break 

541 leaves.insert(0, leaf) 

542 leaf = leaf.get_previous_leaf() 

543 return None, None, None 

544 

545 

546def complete_trailer(user_context, values): 

547 completion_names = [] 

548 for value in values: 

549 for filter in value.get_filters(origin_scope=user_context.tree_node): 

550 completion_names += filter.values() 

551 

552 if not value.is_stub() and isinstance(value, TreeInstance): 

553 completion_names += _complete_getattr(user_context, value) 

554 

555 python_values = convert_values(values) 

556 for c in python_values: 

557 if c not in values: 

558 for filter in c.get_filters(origin_scope=user_context.tree_node): 

559 completion_names += filter.values() 

560 return completion_names 

561 

562 

563def _complete_getattr(user_context, instance): 

564 """ 

565 A heuristic to make completion for proxy objects work. This is not 

566 intended to work in all cases. It works exactly in this case: 

567 

568 def __getattr__(self, name): 

569 ... 

570 return getattr(any_object, name) 

571 

572 It is important that the return contains getattr directly, otherwise it 

573 won't work anymore. It's really just a stupid heuristic. It will not 

574 work if you write e.g. `return (getatr(o, name))`, because of the 

575 additional parentheses. It will also not work if you move the getattr 

576 to some other place that is not the return statement itself. 

577 

578 It is intentional that it doesn't work in all cases. Generally it's 

579 really hard to do even this case (as you can see below). Most people 

580 will write it like this anyway and the other ones, well they are just 

581 out of luck I guess :) ~dave. 

582 """ 

583 names = (instance.get_function_slot_names('__getattr__') 

584 or instance.get_function_slot_names('__getattribute__')) 

585 functions = ValueSet.from_sets( 

586 name.infer() 

587 for name in names 

588 ) 

589 for func in functions: 

590 tree_node = func.tree_node 

591 if tree_node is None or tree_node.type != 'funcdef': 

592 continue 

593 

594 for return_stmt in tree_node.iter_return_stmts(): 

595 # Basically until the next comment we just try to find out if a 

596 # return statement looks exactly like `return getattr(x, name)`. 

597 if return_stmt.type != 'return_stmt': 

598 continue 

599 atom_expr = return_stmt.children[1] 

600 if atom_expr.type != 'atom_expr': 

601 continue 

602 atom = atom_expr.children[0] 

603 trailer = atom_expr.children[1] 

604 if len(atom_expr.children) != 2 or atom.type != 'name' \ 

605 or atom.value != 'getattr': 

606 continue 

607 arglist = trailer.children[1] 

608 if arglist.type != 'arglist' or len(arglist.children) < 3: 

609 continue 

610 context = func.as_context() 

611 object_node = arglist.children[0] 

612 

613 # Make sure it's a param: foo in __getattr__(self, foo) 

614 name_node = arglist.children[2] 

615 name_list = context.goto(name_node, name_node.start_pos) 

616 if not any(n.api_type == 'param' for n in name_list): 

617 continue 

618 

619 # Now that we know that these are most probably completion 

620 # objects, we just infer the object and return them as 

621 # completions. 

622 objects = context.infer_node(object_node) 

623 return complete_trailer(user_context, objects) 

624 return [] 

625 

626 

627def search_in_module(inference_state, module_context, names, wanted_names, 

628 wanted_type, complete=False, fuzzy=False, 

629 ignore_imports=False, convert=False): 

630 for s in wanted_names[:-1]: 

631 new_names = [] 

632 for n in names: 

633 if s == n.string_name: 

634 if n.tree_name is not None and n.api_type in ('module', 'namespace') \ 

635 and ignore_imports: 

636 continue 

637 new_names += complete_trailer( 

638 module_context, 

639 n.infer() 

640 ) 

641 debug.dbg('dot lookup on search %s from %s', new_names, names[:10]) 

642 names = new_names 

643 

644 last_name = wanted_names[-1].lower() 

645 for n in names: 

646 string = n.string_name.lower() 

647 if complete and helpers.match(string, last_name, fuzzy=fuzzy) \ 

648 or not complete and string == last_name: 

649 if isinstance(n, SubModuleName): 

650 names = [v.name for v in n.infer()] 

651 else: 

652 names = [n] 

653 if convert: 

654 names = convert_names(names) 

655 for n2 in names: 

656 if complete: 

657 def_ = classes.Completion( 

658 inference_state, n2, 

659 stack=None, 

660 like_name_length=len(last_name), 

661 is_fuzzy=fuzzy, 

662 ) 

663 else: 

664 def_ = classes.Name(inference_state, n2) 

665 if not wanted_type or wanted_type == def_.type: 

666 yield def_