From 2f99926001b60d6386533d15b90dc6734d4b2fc0 Mon Sep 17 00:00:00 2001 From: jschendel Date: Wed, 9 Aug 2017 20:46:56 -0600 Subject: [PATCH 1/2] CLN: replace %s syntax with .format in core.computation Replaced %s syntax is .format in core.computation. Additionally, made some of the existing positional .format more explicit. --- pandas/core/computation/align.py | 11 +++-- pandas/core/computation/engines.py | 5 ++- pandas/core/computation/eval.py | 13 +++--- pandas/core/computation/expr.py | 45 ++++++++++--------- pandas/core/computation/expressions.py | 14 +++--- pandas/core/computation/pytables.py | 61 ++++++++++++++------------ pandas/core/computation/scope.py | 11 +++-- 7 files changed, 87 insertions(+), 73 deletions(-) diff --git a/pandas/core/computation/align.py b/pandas/core/computation/align.py index 1c75301082297..691eaebfd5fc1 100644 --- a/pandas/core/computation/align.py +++ b/pandas/core/computation/align.py @@ -98,12 +98,11 @@ def _align_core(terms): ordm = np.log10(max(1, abs(reindexer_size - term_axis_size))) if ordm >= 1 and reindexer_size >= 10000: - warnings.warn('Alignment difference on axis {0} is larger ' - 'than an order of magnitude on term {1!r}, ' - 'by more than {2:.4g}; performance may ' - 'suffer'.format(axis, terms[i].name, ordm), - category=PerformanceWarning, - stacklevel=6) + w = ('Alignment difference on axis {axis} is larger ' + 'than an order of magnitude on term {term!r}, by ' + 'more than {ordm:.4g}; performance may suffer' + ).format(axis=axis, term=terms[i].name, ordm=ordm) + warnings.warn(w, category=PerformanceWarning, stacklevel=6) if transpose: f = partial(ti.reindex, index=reindexer, copy=False) diff --git a/pandas/core/computation/engines.py b/pandas/core/computation/engines.py index f45d0355e7442..155ff554cf99c 100644 --- a/pandas/core/computation/engines.py +++ b/pandas/core/computation/engines.py @@ -33,8 +33,9 @@ def _check_ne_builtin_clash(expr): if overlap: s = ', '.join(map(repr, overlap)) - raise NumExprClobberingError('Variables in expression "%s" ' - 'overlap with builtins: (%s)' % (expr, s)) + raise NumExprClobberingError('Variables in expression "{expr}" ' + 'overlap with builtins: ({s})' + .format(expr=expr, s=s)) class AbstractEngine(object): diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py index ef15e886fd554..d391764794c1c 100644 --- a/pandas/core/computation/eval.py +++ b/pandas/core/computation/eval.py @@ -40,8 +40,9 @@ def _check_engine(engine): engine = 'python' if engine not in _engines: - raise KeyError('Invalid engine {0!r} passed, valid engines are' - ' {1}'.format(engine, list(_engines.keys()))) + valid = list(_engines.keys()) + raise KeyError('Invalid engine {engine!r} passed, valid engines are' + ' {valid}'.format(engine=engine, valid=valid)) # TODO: validate this in a more general way (thinking of future engines # that won't necessarily be import-able) @@ -69,8 +70,8 @@ def _check_parser(parser): * If an invalid parser is passed """ if parser not in _parsers: - raise KeyError('Invalid parser {0!r} passed, valid parsers are' - ' {1}'.format(parser, _parsers.keys())) + raise KeyError('Invalid parser {parser!r} passed, valid parsers are' + ' {valid}'.format(parser=parser, valid=_parsers.keys())) def _check_resolvers(resolvers): @@ -78,8 +79,8 @@ def _check_resolvers(resolvers): for resolver in resolvers: if not hasattr(resolver, '__getitem__'): name = type(resolver).__name__ - raise TypeError('Resolver of type %r does not implement ' - 'the __getitem__ method' % name) + raise TypeError('Resolver of type {name!r} does not implement ' + 'the __getitem__ method'.format(name=name)) def _check_expression(expr): diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index 73c27f4d772ca..ae956bce11329 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -189,8 +189,8 @@ def _filter_nodes(superclass, all_nodes=_all_nodes): # and we don't want `stmt` and friends in their so get only the class whose # names are capitalized _base_supported_nodes = (_all_node_names - _unsupported_nodes) | _hacked_nodes -_msg = 'cannot both support and not support {0}'.format(_unsupported_nodes & - _base_supported_nodes) +_msg = 'cannot both support and not support {intersection}'.format( + intersection=_unsupported_nodes & _base_supported_nodes) assert not _unsupported_nodes & _base_supported_nodes, _msg @@ -200,8 +200,8 @@ def _node_not_implemented(node_name, cls): """ def f(self, *args, **kwargs): - raise NotImplementedError("{0!r} nodes are not " - "implemented".format(node_name)) + raise NotImplementedError("{name!r} nodes are not " + "implemented".format(name=node_name)) return f @@ -217,7 +217,7 @@ def disallowed(cls): cls.unsupported_nodes = () for node in nodes: new_method = _node_not_implemented(node, cls) - name = 'visit_{0}'.format(node) + name = 'visit_{node}'.format(node=node) cls.unsupported_nodes += (name,) setattr(cls, name, new_method) return cls @@ -251,13 +251,14 @@ def add_ops(op_classes): """Decorator to add default implementation of ops.""" def f(cls): for op_attr_name, op_class in compat.iteritems(op_classes): - ops = getattr(cls, '{0}_ops'.format(op_attr_name)) - ops_map = getattr(cls, '{0}_op_nodes_map'.format(op_attr_name)) + ops = getattr(cls, '{name}_ops'.format(name=op_attr_name)) + ops_map = getattr(cls, '{name}_op_nodes_map'.format( + name=op_attr_name)) for op in ops: op_node = ops_map[op] if op_node is not None: made_op = _op_maker(op_class, op) - setattr(cls, 'visit_{0}'.format(op_node), made_op) + setattr(cls, 'visit_{node}'.format(node=op_node), made_op) return cls return f @@ -388,9 +389,10 @@ def _maybe_evaluate_binop(self, op, op_class, lhs, rhs, res = op(lhs, rhs) if res.has_invalid_return_type: - raise TypeError("unsupported operand type(s) for {0}:" - " '{1}' and '{2}'".format(res.op, lhs.type, - rhs.type)) + raise TypeError("unsupported operand type(s) for {op}:" + " '{lhs}' and '{rhs}'".format(op=res.op, + lhs=lhs.type, + rhs=rhs.type)) if self.engine != 'pytables': if (res.op in _cmp_ops_syms and @@ -527,7 +529,8 @@ def visit_Attribute(self, node, **kwargs): if isinstance(value, ast.Name) and value.id == attr: return resolved - raise ValueError("Invalid Attribute context {0}".format(ctx.__name__)) + raise ValueError("Invalid Attribute context {name}" + .format(name=ctx.__name__)) def visit_Call_35(self, node, side=None, **kwargs): """ in 3.5 the starargs attribute was changed to be more flexible, @@ -549,7 +552,8 @@ def visit_Call_35(self, node, side=None, **kwargs): raise if res is None: - raise ValueError("Invalid function call {0}".format(node.func.id)) + raise ValueError("Invalid function call {func}" + .format(func=node.func.id)) if hasattr(res, 'value'): res = res.value @@ -558,8 +562,8 @@ def visit_Call_35(self, node, side=None, **kwargs): new_args = [self.visit(arg) for arg in node.args] if node.keywords: - raise TypeError("Function \"{0}\" does not support keyword " - "arguments".format(res.name)) + raise TypeError("Function \"{name}\" does not support keyword " + "arguments".format(name=res.name)) return res(*new_args, **kwargs) @@ -570,7 +574,7 @@ def visit_Call_35(self, node, side=None, **kwargs): for key in node.keywords: if not isinstance(key, ast.keyword): raise ValueError("keyword error in function call " - "'{0}'".format(node.func.id)) + "'{func}'".format(func=node.func.id)) if key.arg: # TODO: bug? @@ -598,7 +602,8 @@ def visit_Call_legacy(self, node, side=None, **kwargs): raise if res is None: - raise ValueError("Invalid function call {0}".format(node.func.id)) + raise ValueError("Invalid function call {func}" + .format(func=node.func.id)) if hasattr(res, 'value'): res = res.value @@ -609,8 +614,8 @@ def visit_Call_legacy(self, node, side=None, **kwargs): args += self.visit(node.starargs) if node.keywords or node.kwargs: - raise TypeError("Function \"{0}\" does not support keyword " - "arguments".format(res.name)) + raise TypeError("Function \"{name}\" does not support keyword " + "arguments".format(name=res.name)) return res(*args, **kwargs) @@ -623,7 +628,7 @@ def visit_Call_legacy(self, node, side=None, **kwargs): for key in node.keywords: if not isinstance(key, ast.keyword): raise ValueError("keyword error in function call " - "'{0}'".format(node.func.id)) + "'{func}'".format(func=node.func.id)) keywords[key.arg] = self.visit(key.value).value if node.kwargs is not None: keywords.update(self.visit(node.kwargs).value) diff --git a/pandas/core/computation/expressions.py b/pandas/core/computation/expressions.py index 83d02af65cc85..af068bd1f32b3 100644 --- a/pandas/core/computation/expressions.py +++ b/pandas/core/computation/expressions.py @@ -103,7 +103,7 @@ def _evaluate_numexpr(op, op_str, a, b, raise_on_error=False, truediv=True, a_value = getattr(a, "values", a) b_value = getattr(b, "values", b) - result = ne.evaluate('a_value %s b_value' % op_str, + result = ne.evaluate('a_value {op} b_value'.format(op=op_str), local_dict={'a_value': a_value, 'b_value': b_value}, casting='safe', truediv=truediv, @@ -177,15 +177,15 @@ def _bool_arith_check(op_str, a, b, not_allowed=frozenset(('/', '//', '**')), if _has_bool_dtype(a) and _has_bool_dtype(b): if op_str in unsupported: - warnings.warn("evaluating in Python space because the %r operator" - " is not supported by numexpr for the bool " - "dtype, use %r instead" % (op_str, - unsupported[op_str])) + warnings.warn("evaluating in Python space because the {op!r} " + "operator is not supported by numexpr for " + "the bool dtype, use {alt_op!r} instead" + .format(op=op_str, alt_op=unsupported[op_str])) return False if op_str in not_allowed: - raise NotImplementedError("operator %r not implemented for bool " - "dtypes" % op_str) + raise NotImplementedError("operator {op!r} not implemented for " + "bool dtypes".format(op=op_str)) return True diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index 5870090856ff9..11ff2bc86be0b 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -41,7 +41,8 @@ def _resolve_name(self): # must be a queryables if self.side == 'left': if self.name not in self.env.queryables: - raise NameError('name {0!r} is not defined'.format(self.name)) + raise NameError('name {name!r} is not defined' + .format(name=self.name)) return self.name # resolve the rhs (and allow it to be None) @@ -161,7 +162,7 @@ def metadata(self): def generate(self, v): """ create and return the op string for this TermValue """ val = v.tostring(self.encoding) - return "(%s %s %s)" % (self.lhs, self.op, val) + return "({lhs} {op} {val})".format(lhs=self.lhs, op=self.op, val=val) def convert_value(self, v): """ convert the expression that is in the term to something that is @@ -215,9 +216,8 @@ def stringify(value): # string quoting return TermValue(v, stringify(v), u('string')) else: - raise TypeError(("Cannot compare {v} of type {typ}" - " to {kind} column").format(v=v, typ=type(v), - kind=kind)) + raise TypeError("Cannot compare {v} of type {typ} to {kind} column" + .format(v=v, typ=type(v), kind=kind)) def convert_values(self): pass @@ -226,8 +226,8 @@ def convert_values(self): class FilterBinOp(BinOp): def __unicode__(self): - return pprint_thing("[Filter : [{0}] -> " - "[{1}]".format(self.filter[0], self.filter[1])) + return pprint_thing("[Filter : [{lhs}] -> [{op}]" + .format(lhs=self.filter[0], op=self.filter[1])) def invert(self): """ invert the filter """ @@ -244,7 +244,8 @@ def format(self): def evaluate(self): if not self.is_valid: - raise ValueError("query term is not valid [%s]" % self) + raise ValueError("query term is not valid [{slf}]" + .format(slf=self)) rhs = self.conform(self.rhs) values = [TermValue(v, v, self.kind) for v in rhs] @@ -273,9 +274,8 @@ def evaluate(self): pd.Index([v.value for v in values])) else: - raise TypeError( - "passing a filterable condition to a non-table indexer [%s]" % - self) + raise TypeError("passing a filterable condition to a non-table " + "indexer [{slf}]".format(slf=self)) return self @@ -298,7 +298,8 @@ def evaluate(self): class ConditionBinOp(BinOp): def __unicode__(self): - return pprint_thing("[Condition : [{0}]]".format(self.condition)) + return pprint_thing("[Condition : [{cond}]]" + .format(cond=self.condition)) def invert(self): """ invert the condition """ @@ -315,7 +316,8 @@ def format(self): def evaluate(self): if not self.is_valid: - raise ValueError("query term is not valid [%s]" % self) + raise ValueError("query term is not valid [{slf}]" + .format(slf=self)) # convert values if we are in the table if not self.is_in_table: @@ -330,7 +332,7 @@ def evaluate(self): # too many values to create the expression? if len(values) <= self._max_selectors: vs = [self.generate(v) for v in values] - self.condition = "(%s)" % ' | '.join(vs) + self.condition = "({cond})".format(cond=' | '.join(vs)) # use a filter after reading else: @@ -344,10 +346,9 @@ def evaluate(self): class JointConditionBinOp(ConditionBinOp): def evaluate(self): - self.condition = "(%s %s %s)" % ( - self.lhs.condition, - self.op, - self.rhs.condition) + self.condition = "({lhs} {op} {rhs})".format(lhs=self.lhs.condition, + op=self.op, + rhs=self.rhs.condition) return self @@ -382,7 +383,8 @@ class ExprVisitor(BaseExprVisitor): def __init__(self, env, engine, parser, **kwargs): super(ExprVisitor, self).__init__(env, engine, parser) for bin_op in self.binary_ops: - setattr(self, 'visit_{0}'.format(self.binary_op_nodes_map[bin_op]), + bin_node = self.binary_op_nodes_map[bin_op] + setattr(self, 'visit_{node}'.format(node=bin_node), lambda node, bin_op=bin_op: partial(BinOp, bin_op, **kwargs)) @@ -415,8 +417,8 @@ def visit_Subscript(self, node, **kwargs): try: return self.const_type(value[slobj], self.env) except TypeError: - raise ValueError("cannot subscript {0!r} with " - "{1!r}".format(value, slobj)) + raise ValueError("cannot subscript {value!r} with " + "{slobj!r}".format(value=value, slobj=slobj)) def visit_Attribute(self, node, **kwargs): attr = node.attr @@ -441,7 +443,8 @@ def visit_Attribute(self, node, **kwargs): if isinstance(value, ast.Name) and value.id == attr: return resolved - raise ValueError("Invalid Attribute context {0}".format(ctx.__name__)) + raise ValueError("Invalid Attribute context {name}" + .format(name=ctx.__name__)) def translate_In(self, op): return ast.Eq() if isinstance(op, ast.In) else op @@ -529,7 +532,7 @@ def __init__(self, where, queryables=None, encoding=None, scope_level=0): else: w = _validate_where(w) where[idx] = w - where = ' & ' .join(["(%s)" % w for w in where]) # noqa + where = ' & '.join(map('({})'.format, where)) # noqa self.expr = where self.env = Scope(scope_level + 1, local_dict=local_dict) @@ -552,13 +555,15 @@ def evaluate(self): try: self.condition = self.terms.prune(ConditionBinOp) except AttributeError: - raise ValueError("cannot process expression [{0}], [{1}] is not a " - "valid condition".format(self.expr, self)) + raise ValueError("cannot process expression [{expr}], [{slf}] " + "is not a valid condition".format(expr=self.expr, + slf=self)) try: self.filter = self.terms.prune(FilterBinOp) except AttributeError: - raise ValueError("cannot process expression [{0}], [{1}] is not a " - "valid filter".format(self.expr, self)) + raise ValueError("cannot process expression [{expr}], [{slf}] " + "is not a valid filter".format(expr=self.expr, + slf=self)) return self.condition, self.filter @@ -578,7 +583,7 @@ def tostring(self, encoding): if self.kind == u'string': if encoding is not None: return self.converted - return '"%s"' % self.converted + return '"{converted}"'.format(converted=self.converted) elif self.kind == u'float': # python 2 str(float) is not always # round-trippable so use repr() diff --git a/pandas/core/computation/scope.py b/pandas/core/computation/scope.py index 5a589473f64b7..6a298f5137eb1 100644 --- a/pandas/core/computation/scope.py +++ b/pandas/core/computation/scope.py @@ -137,8 +137,10 @@ def __init__(self, level, global_dict=None, local_dict=None, resolvers=(), def __unicode__(self): scope_keys = _get_pretty_string(list(self.scope.keys())) res_keys = _get_pretty_string(list(self.resolvers.keys())) - return '%s(scope=%s, resolvers=%s)' % (type(self).__name__, scope_keys, - res_keys) + unicode_str = '{name}(scope={scope_keys}, resolvers={res_keys})' + return unicode_str.format(name=type(self).__name__, + scope_keys=scope_keys, + res_keys=res_keys) @property def has_resolvers(self): @@ -269,8 +271,9 @@ def add_tmp(self, value): name : basestring The name of the temporary variable created. """ - name = '{0}_{1}_{2}'.format(type(value).__name__, self.ntemps, - _raw_hex_id(self)) + name = '{name}_{num}_{hex_id}'.format(name=type(value).__name__, + num=self.ntemps, + hex_id=_raw_hex_id(self)) # add to inner most scope assert name not in self.temps From 095c0dea208800085169392eb4e01b01f9d883f1 Mon Sep 17 00:00:00 2001 From: jschendel Date: Wed, 9 Aug 2017 22:14:00 -0600 Subject: [PATCH 2/2] FIX: Use flatten when creating where string. Previous code was using the tuple formatting property of %s which automatically flattened tuples. --- pandas/core/computation/pytables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index 11ff2bc86be0b..4b3c608a88be8 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -532,7 +532,7 @@ def __init__(self, where, queryables=None, encoding=None, scope_level=0): else: w = _validate_where(w) where[idx] = w - where = ' & '.join(map('({})'.format, where)) # noqa + where = ' & '.join(map('({})'.format, com.flatten(where))) # noqa self.expr = where self.env = Scope(scope_level + 1, local_dict=local_dict)