Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pandas/core/computation/engines.py: 54%

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

50 statements  

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 

20from pandas.io.formats import printing 

21 

22if TYPE_CHECKING: 

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 

106 

107class NumExprEngine(AbstractEngine): 

108 """NumExpr engine class""" 

109 

110 has_neg_frac = True 

111 

112 def _evaluate(self): 

113 import numexpr as ne 

114 

115 # convert the expression to a valid numexpr expression 

116 s = self.convert() 

117 

118 env = self.expr.env 

119 scope = env.full_scope 

120 _check_ne_builtin_clash(self.expr) 

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

122 

123 

124class PythonEngine(AbstractEngine): 

125 """ 

126 Evaluate an expression in Python space. 

127 

128 Mostly for testing purposes. 

129 """ 

130 

131 has_neg_frac = False 

132 

133 def evaluate(self): 

134 return self.expr() 

135 

136 def _evaluate(self) -> None: 

137 pass 

138 

139 

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

141 "numexpr": NumExprEngine, 

142 "python": PythonEngine, 

143}