Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/inference_tip.py: 47%

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

47 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"""Transform utilities (filters and decorator).""" 

6 

7from __future__ import annotations 

8 

9from collections import OrderedDict 

10from collections.abc import Generator 

11from typing import Any, TypeVar 

12 

13from astroid.context import InferenceContext 

14from astroid.exceptions import InferenceOverwriteError, UseInferenceDefault 

15from astroid.nodes import NodeNG 

16from astroid.typing import ( 

17 InferenceResult, 

18 InferFn, 

19 TransformFn, 

20) 

21 

22_cache: OrderedDict[ 

23 tuple[InferFn[Any], NodeNG, InferenceContext | None], list[InferenceResult] 

24] = OrderedDict() 

25 

26_CURRENTLY_INFERRING: set[tuple[InferFn[Any], NodeNG]] = set() 

27 

28_NodesT = TypeVar("_NodesT", bound=NodeNG) 

29 

30 

31def clear_inference_tip_cache() -> None: 

32 """Clear the inference tips cache.""" 

33 _cache.clear() 

34 

35 

36def _inference_tip_cached(func: InferFn[_NodesT]) -> InferFn[_NodesT]: 

37 """Cache decorator used for inference tips.""" 

38 

39 def inner( 

40 node: _NodesT, 

41 context: InferenceContext | None = None, 

42 ) -> Generator[InferenceResult]: 

43 partial_cache_key = (func, node) 

44 if partial_cache_key in _CURRENTLY_INFERRING: 

45 # If through recursion we end up trying to infer the same 

46 # func + node we raise here. 

47 _CURRENTLY_INFERRING.remove(partial_cache_key) 

48 raise UseInferenceDefault 

49 if context is not None and context.is_empty(): 

50 # Fresh, empty contexts will defeat the cache. 

51 context = None 

52 try: 

53 yield from _cache[func, node, context] 

54 return 

55 except KeyError: 

56 # Recursion guard with a partial cache key. 

57 # Using the full key causes a recursion error on PyPy. 

58 # It's a pragmatic compromise to avoid so much recursive inference 

59 # with slightly different contexts while still passing the simple 

60 # test cases included with this commit. 

61 _CURRENTLY_INFERRING.add(partial_cache_key) 

62 try: 

63 # May raise UseInferenceDefault 

64 result = _cache[func, node, context] = list(func(node, context)) 

65 except Exception as e: 

66 # Suppress the KeyError from the cache miss. 

67 raise e from None 

68 finally: 

69 # Remove recursion guard. 

70 try: 

71 _CURRENTLY_INFERRING.remove(partial_cache_key) 

72 except KeyError: 

73 pass # Recursion may beat us to the punch. 

74 

75 if len(_cache) > 64: 

76 _cache.popitem(last=False) 

77 

78 # https://github.com/pylint-dev/pylint/issues/8686 

79 yield from result # pylint: disable=used-before-assignment 

80 

81 return inner 

82 

83 

84def inference_tip( 

85 infer_function: InferFn[_NodesT], raise_on_overwrite: bool = False 

86) -> TransformFn[_NodesT]: 

87 """Given an instance specific inference function, return a function to be 

88 given to AstroidManager().register_transform to set this inference function. 

89 

90 :param bool raise_on_overwrite: Raise an `InferenceOverwriteError` 

91 if the inference tip will overwrite another. Used for debugging 

92 

93 Typical usage 

94 

95 .. sourcecode:: python 

96 

97 AstroidManager().register_transform(Call, inference_tip(infer_named_tuple), 

98 predicate) 

99 

100 .. Note:: 

101 

102 Using an inference tip will override 

103 any previously set inference tip for the given 

104 node. Use a predicate in the transform to prevent 

105 excess overwrites. 

106 """ 

107 

108 def transform( 

109 node: _NodesT, infer_function: InferFn[_NodesT] = infer_function 

110 ) -> _NodesT: 

111 if ( 

112 raise_on_overwrite 

113 and node._explicit_inference is not None 

114 and node._explicit_inference is not infer_function 

115 ): 

116 raise InferenceOverwriteError( 

117 f"Inference already set to {infer_function}. " 

118 f"Trying to overwrite with {node._explicit_inference} for {node}" 

119 ) 

120 node._explicit_inference = _inference_tip_cached(infer_function) 

121 return node 

122 

123 return transform