Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

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

3""" 

4 

5import abc 

6from typing import Dict, Type 

7 

8from pandas.core.computation.align import align_terms, reconstruct_object 

9from pandas.core.computation.ops import _mathops, _reductions 

10 

11import pandas.io.formats.printing as printing 

12 

13_ne_builtins = frozenset(_mathops + _reductions) 

14 

15 

16class NumExprClobberingError(NameError): 

17 pass 

18 

19 

20def _check_ne_builtin_clash(expr): 

21 """ 

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

23 

24 Parameters 

25 ---------- 

26 terms : Term 

27 Terms can contain 

28 """ 

29 names = expr.names 

30 overlap = names & _ne_builtins 

31 

32 if overlap: 

33 s = ", ".join(repr(x) for x in overlap) 

34 raise NumExprClobberingError( 

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

36 ) 

37 

38 

39class AbstractEngine(metaclass=abc.ABCMeta): 

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

41 

42 has_neg_frac = False 

43 

44 def __init__(self, expr): 

45 self.expr = expr 

46 self.aligned_axes = None 

47 self.result_type = None 

48 

49 def convert(self) -> str: 

50 """ 

51 Convert an expression for evaluation. 

52 

53 Defaults to return the expression as a string. 

54 """ 

55 return printing.pprint_thing(self.expr) 

56 

57 def evaluate(self) -> object: 

58 """ 

59 Run the engine on the expression. 

60 

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

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

63 

64 Returns 

65 ------- 

66 object 

67 The result of the passed expression. 

68 """ 

69 if not self._is_aligned: 

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

71 

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

73 res = self._evaluate() 

74 return reconstruct_object( 

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

76 ) 

77 

78 @property 

79 def _is_aligned(self) -> bool: 

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

81 

82 @abc.abstractmethod 

83 def _evaluate(self): 

84 """ 

85 Return an evaluated expression. 

86 

87 Parameters 

88 ---------- 

89 env : Scope 

90 The local and global environment in which to evaluate an 

91 expression. 

92 

93 Notes 

94 ----- 

95 Must be implemented by subclasses. 

96 """ 

97 pass 

98 

99 

100class NumExprEngine(AbstractEngine): 

101 """NumExpr engine class""" 

102 

103 has_neg_frac = True 

104 

105 def _evaluate(self): 

106 import numexpr as ne 

107 

108 # convert the expression to a valid numexpr expression 

109 s = self.convert() 

110 

111 env = self.expr.env 

112 scope = env.full_scope 

113 _check_ne_builtin_clash(self.expr) 

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

115 

116 

117class PythonEngine(AbstractEngine): 

118 """ 

119 Evaluate an expression in Python space. 

120 

121 Mostly for testing purposes. 

122 """ 

123 

124 has_neg_frac = False 

125 

126 def evaluate(self): 

127 return self.expr() 

128 

129 def _evaluate(self) -> None: 

130 pass 

131 

132 

133_engines: Dict[str, Type[AbstractEngine]] = { 

134 "numexpr": NumExprEngine, 

135 "python": PythonEngine, 

136}