Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/computation/engines.py: 52%

51 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Engine classes for :func:`~pandas.eval` 

3""" 

4from __future__ import annotations 

5 

6import abc 

7from typing import TYPE_CHECKING 

8 

9from pandas.errors import NumExprClobberingError 

10 

11from pandas.core.computation.align import ( 

12 align_terms, 

13 reconstruct_object, 

14) 

15from pandas.core.computation.ops import ( 

16 MATHOPS, 

17 REDUCTIONS, 

18) 

19 

20import pandas.io.formats.printing as printing 

21 

22if TYPE_CHECKING: 22 ↛ 23line 22 didn't jump to line 23, because the condition on line 22 was never true

23 from pandas.core.computation.expr import Expr 

24 

25_ne_builtins = frozenset(MATHOPS + REDUCTIONS) 

26 

27 

28def _check_ne_builtin_clash(expr: Expr) -> None: 

29 """ 

30 Attempt to prevent foot-shooting in a helpful way. 

31 

32 Parameters 

33 ---------- 

34 expr : Expr 

35 Terms can contain 

36 """ 

37 names = expr.names 

38 overlap = names & _ne_builtins 

39 

40 if overlap: 

41 s = ", ".join([repr(x) for x in overlap]) 

42 raise NumExprClobberingError( 

43 f'Variables in expression "{expr}" overlap with builtins: ({s})' 

44 ) 

45 

46 

47class AbstractEngine(metaclass=abc.ABCMeta): 

48 """Object serving as a base class for all engines.""" 

49 

50 has_neg_frac = False 

51 

52 def __init__(self, expr) -> None: 

53 self.expr = expr 

54 self.aligned_axes = None 

55 self.result_type = None 

56 

57 def convert(self) -> str: 

58 """ 

59 Convert an expression for evaluation. 

60 

61 Defaults to return the expression as a string. 

62 """ 

63 return printing.pprint_thing(self.expr) 

64 

65 def evaluate(self) -> object: 

66 """ 

67 Run the engine on the expression. 

68 

69 This method performs alignment which is necessary no matter what engine 

70 is being used, thus its implementation is in the base class. 

71 

72 Returns 

73 ------- 

74 object 

75 The result of the passed expression. 

76 """ 

77 if not self._is_aligned: 

78 self.result_type, self.aligned_axes = align_terms(self.expr.terms) 

79 

80 # make sure no names in resolvers and locals/globals clash 

81 res = self._evaluate() 

82 return reconstruct_object( 

83 self.result_type, res, self.aligned_axes, self.expr.terms.return_type 

84 ) 

85 

86 @property 

87 def _is_aligned(self) -> bool: 

88 return self.aligned_axes is not None and self.result_type is not None 

89 

90 @abc.abstractmethod 

91 def _evaluate(self): 

92 """ 

93 Return an evaluated expression. 

94 

95 Parameters 

96 ---------- 

97 env : Scope 

98 The local and global environment in which to evaluate an 

99 expression. 

100 

101 Notes 

102 ----- 

103 Must be implemented by subclasses. 

104 """ 

105 pass 

106 

107 

108class NumExprEngine(AbstractEngine): 

109 """NumExpr engine class""" 

110 

111 has_neg_frac = True 

112 

113 def _evaluate(self): 

114 import numexpr as ne 

115 

116 # convert the expression to a valid numexpr expression 

117 s = self.convert() 

118 

119 env = self.expr.env 

120 scope = env.full_scope 

121 _check_ne_builtin_clash(self.expr) 

122 return ne.evaluate(s, local_dict=scope) 

123 

124 

125class PythonEngine(AbstractEngine): 

126 """ 

127 Evaluate an expression in Python space. 

128 

129 Mostly for testing purposes. 

130 """ 

131 

132 has_neg_frac = False 

133 

134 def evaluate(self): 

135 return self.expr() 

136 

137 def _evaluate(self) -> None: 

138 pass 

139 

140 

141ENGINES: dict[str, type[AbstractEngine]] = { 

142 "numexpr": NumExprEngine, 

143 "python": PythonEngine, 

144}