Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions pandas/core/computation/pytables.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@


class PyTablesScope(_scope.Scope):
__slots__ = ("queryables",)
__slots__ = ("queryables", )

queryables: dict[str, Any]

Expand All @@ -49,7 +49,9 @@ def __init__(
local_dict=None,
queryables: dict[str, Any] | None = None,
):
super().__init__(level + 1, global_dict=global_dict, local_dict=local_dict)
super().__init__(level + 1,
global_dict=global_dict,
local_dict=local_dict)
self.queryables = queryables or {}


Expand Down Expand Up @@ -87,6 +89,7 @@ def value(self):


class Constant(Term):

def __init__(self, value, env: PyTablesScope, side=None, encoding=None):
assert isinstance(env, PyTablesScope), type(env)
super().__init__(value, env, side=side, encoding=encoding)
Expand All @@ -103,7 +106,8 @@ class BinOp(ops.BinOp):
queryables: dict[str, Any]
condition: str | None

def __init__(self, op: str, lhs, rhs, queryables: dict[str, Any], encoding):
def __init__(self, op: str, lhs, rhs, queryables: dict[str, Any],
encoding):
super().__init__(op, lhs, rhs)
self.queryables = queryables
self.encoding = encoding
Expand All @@ -113,6 +117,7 @@ def _disallow_scalar_only_bool_ops(self):
pass

def prune(self, klass):

def pr(left, right):
"""create and return a new specialized BinOp from myself"""
if left is None:
Expand All @@ -137,9 +142,11 @@ def pr(left, right):
elif isinstance(right, k):
return right

return k(
self.op, left, right, queryables=self.queryables, encoding=self.encoding
).evaluate()
return k(self.op,
left,
right,
queryables=self.queryables,
encoding=self.encoding).evaluate()

left, right = self.lhs, self.rhs

Expand Down Expand Up @@ -256,7 +263,8 @@ def stringify(value):
# string quoting
return TermValue(v, stringify(v), "string")
else:
raise TypeError(f"Cannot compare {v} of type {type(v)} to {kind} column")
raise TypeError(
f"Cannot compare {v} of type {type(v)} to {kind} column")

def convert_values(self):
pass
Expand All @@ -268,7 +276,8 @@ class FilterBinOp(BinOp):
def __repr__(self) -> str:
if self.filter is None:
return "Filter: Not Initialized"
return pprint_thing(f"[Filter : [{self.filter[0]}] -> [{self.filter[1]}]")
return pprint_thing(
f"[Filter : [{self.filter[0]}] -> [{self.filter[1]}]")

def invert(self):
"""invert the filter"""
Expand Down Expand Up @@ -324,6 +333,7 @@ def generate_filter_op(self, invert: bool = False):


class JointFilterBinOp(FilterBinOp):

def format(self):
raise NotImplementedError("unable to collapse Joint Filters")

Expand All @@ -332,6 +342,7 @@ def evaluate(self):


class ConditionBinOp(BinOp):

def __repr__(self) -> str:
return pprint_thing(f"[Condition : [{self.condition}]]")

Expand All @@ -341,8 +352,7 @@ def invert(self):
# self.condition = "~(%s)" % self.condition
# return self
raise NotImplementedError(
"cannot use an invert condition when passing to numexpr"
)
"cannot use an invert condition when passing to numexpr")

def format(self):
"""return the actual ne format"""
Expand Down Expand Up @@ -378,12 +388,14 @@ def evaluate(self):


class JointConditionBinOp(ConditionBinOp):

def evaluate(self):
self.condition = f"({self.lhs.condition} {self.op} {self.rhs.condition})"
return self


class UnaryOp(ops.UnaryOp):

def prune(self, klass):

if self.op != "~":
Expand All @@ -392,13 +404,11 @@ def prune(self, klass):
operand = self.operand
operand = operand.prune(klass)

if operand is not None and (
issubclass(klass, ConditionBinOp)
and operand.condition is not None
or not issubclass(klass, ConditionBinOp)
and issubclass(klass, FilterBinOp)
and operand.filter is not None
):
if operand is not None and (issubclass(klass, ConditionBinOp)
and operand.condition is not None
or not issubclass(klass, ConditionBinOp)
and issubclass(klass, FilterBinOp)
and operand.filter is not None):
return operand.invert()
return None

Expand Down Expand Up @@ -429,9 +439,9 @@ def visit_Index(self, node, **kwargs):
return self.visit(node.value).value

def visit_Assign(self, node, **kwargs):
cmpr = ast.Compare(
ops=[ast.Eq()], left=node.targets[0], comparators=[node.value]
)
cmpr = ast.Compare(ops=[ast.Eq()],
left=node.targets[0],
comparators=[node.value])
return self.visit(cmpr)

def visit_Subscript(self, node, **kwargs):
Expand All @@ -452,8 +462,7 @@ def visit_Subscript(self, node, **kwargs):
return self.const_type(value[slobj], self.env)
except TypeError as err:
raise ValueError(
f"cannot subscript {repr(value)} with {repr(slobj)}"
) from err
f"cannot subscript {repr(value)} with {repr(slobj)}") from err

def visit_Attribute(self, node, **kwargs):
attr = node.attr
Expand Down Expand Up @@ -506,10 +515,8 @@ def _validate_where(w):
TypeError : An invalid data type was passed in for w (e.g. dict).
"""
if not (isinstance(w, (PyTablesExpr, str)) or is_list_like(w)):
raise TypeError(
"where must be passed as a string, PyTablesExpr, "
"or list-like of PyTablesExpr"
)
raise TypeError("where must be passed as a string, PyTablesExpr, "
"or list-like of PyTablesExpr")

return w

Expand Down Expand Up @@ -608,15 +615,13 @@ def evaluate(self):
except AttributeError as err:
raise ValueError(
f"cannot process expression [{self.expr}], [{self}] "
"is not a valid condition"
) from err
"is not a valid condition") from err
try:
self.filter = self.terms.prune(FilterBinOp)
except AttributeError as err:
raise ValueError(
f"cannot process expression [{self.expr}], [{self}] "
"is not a valid filter"
) from err
"is not a valid filter") from err

return self.condition, self.filter

Expand Down Expand Up @@ -647,7 +652,8 @@ def maybe_expression(s) -> bool:
"""loose checking if s is a pytables-acceptable expression"""
if not isinstance(s, str):
return False
ops = PyTablesExprVisitor.binary_ops + PyTablesExprVisitor.unary_ops + ("=",)
ops = PyTablesExprVisitor.binary_ops + PyTablesExprVisitor.unary_ops + (
"=", )

# make sure we have an op at least
return any(op in s for op in ops)
Loading