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
« 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
6import abc
7from typing import TYPE_CHECKING
9from pandas.errors import NumExprClobberingError
11from pandas.core.computation.align import (
12 align_terms,
13 reconstruct_object,
14)
15from pandas.core.computation.ops import (
16 MATHOPS,
17 REDUCTIONS,
18)
20import pandas.io.formats.printing as printing
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
25_ne_builtins = frozenset(MATHOPS + REDUCTIONS)
28def _check_ne_builtin_clash(expr: Expr) -> None:
29 """
30 Attempt to prevent foot-shooting in a helpful way.
32 Parameters
33 ----------
34 expr : Expr
35 Terms can contain
36 """
37 names = expr.names
38 overlap = names & _ne_builtins
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 )
47class AbstractEngine(metaclass=abc.ABCMeta):
48 """Object serving as a base class for all engines."""
50 has_neg_frac = False
52 def __init__(self, expr) -> None:
53 self.expr = expr
54 self.aligned_axes = None
55 self.result_type = None
57 def convert(self) -> str:
58 """
59 Convert an expression for evaluation.
61 Defaults to return the expression as a string.
62 """
63 return printing.pprint_thing(self.expr)
65 def evaluate(self) -> object:
66 """
67 Run the engine on the expression.
69 This method performs alignment which is necessary no matter what engine
70 is being used, thus its implementation is in the base class.
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)
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 )
86 @property
87 def _is_aligned(self) -> bool:
88 return self.aligned_axes is not None and self.result_type is not None
90 @abc.abstractmethod
91 def _evaluate(self):
92 """
93 Return an evaluated expression.
95 Parameters
96 ----------
97 env : Scope
98 The local and global environment in which to evaluate an
99 expression.
101 Notes
102 -----
103 Must be implemented by subclasses.
104 """
105 pass
108class NumExprEngine(AbstractEngine):
109 """NumExpr engine class"""
111 has_neg_frac = True
113 def _evaluate(self):
114 import numexpr as ne
116 # convert the expression to a valid numexpr expression
117 s = self.convert()
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)
125class PythonEngine(AbstractEngine):
126 """
127 Evaluate an expression in Python space.
129 Mostly for testing purposes.
130 """
132 has_neg_frac = False
134 def evaluate(self):
135 return self.expr()
137 def _evaluate(self) -> None:
138 pass
141ENGINES: dict[str, type[AbstractEngine]] = {
142 "numexpr": NumExprEngine,
143 "python": PythonEngine,
144}