Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/interpreter/dunder_lookup.py: 68%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

38 statements  

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5"""Contains logic for retrieving special methods. 

6 

7This implementation does not rely on the dot attribute access 

8logic, found in ``.getattr()``. The difference between these two 

9is that the dunder methods are looked with the type slots 

10(you can find more about these here 

11http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/) 

12As such, the lookup for the special methods is actually simpler than 

13the dot attribute access. 

14""" 

15 

16from __future__ import annotations 

17 

18import itertools 

19from typing import TYPE_CHECKING 

20 

21import astroid 

22from astroid import nodes 

23from astroid.exceptions import AttributeInferenceError 

24 

25if TYPE_CHECKING: 

26 from astroid.context import InferenceContext 

27 

28 

29def _drop_overloads(values: list) -> list: 

30 """Drop ``typing.overload`` stubs when a real implementation is present. 

31 

32 A ``@overload``-decorated definition only declares a signature; the last, 

33 undecorated definition is the one actually bound at runtime. Callers take 

34 the first returned value, so the stubs must not shadow it. 

35 """ 

36 implementations = [ 

37 value 

38 for value in values 

39 if not ( 

40 isinstance(value, nodes.FunctionDef) 

41 and "typing.overload" in value.decoratornames() 

42 ) 

43 ] 

44 return implementations or values 

45 

46 

47def _lookup_in_mro(node, name) -> list: 

48 attrs = node.locals.get(name, []) 

49 

50 nodes_ = itertools.chain.from_iterable( 

51 ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True) 

52 ) 

53 values = list(itertools.chain(attrs, nodes_)) 

54 if not values: 

55 raise AttributeInferenceError(attribute=name, target=node) 

56 

57 return _drop_overloads(values) 

58 

59 

60def lookup( 

61 node: nodes.NodeNG, name: str, context: InferenceContext | None = None 

62) -> list: 

63 """Lookup the given special method name in the given *node*. 

64 

65 If the special method was found, then a list of attributes 

66 will be returned. Otherwise, `astroid.AttributeInferenceError` 

67 is going to be raised. 

68 """ 

69 if isinstance(node, (nodes.List, nodes.Tuple, nodes.Const, nodes.Dict, nodes.Set)): 

70 return _builtin_lookup(node, name) 

71 if isinstance(node, astroid.Instance): 

72 return _lookup_in_mro(node, name) 

73 if isinstance(node, nodes.ClassDef): 

74 return _class_lookup(node, name, context=context) 

75 

76 raise AttributeInferenceError(attribute=name, target=node) 

77 

78 

79def _class_lookup( 

80 node: nodes.ClassDef, name: str, context: InferenceContext | None = None 

81) -> list: 

82 metaclass = node.metaclass(context=context) 

83 # An explicit metaclass may infer to a non-class node (e.g. a function), 

84 # which has no MRO to look the special method up in. 

85 if not isinstance(metaclass, nodes.ClassDef): 

86 raise AttributeInferenceError(attribute=name, target=node) 

87 

88 return _lookup_in_mro(metaclass, name) 

89 

90 

91def _builtin_lookup(node, name) -> list: 

92 values = node.locals.get(name, []) 

93 if not values: 

94 raise AttributeInferenceError(attribute=name, target=node) 

95 

96 return values