Skip to content

Commit e56411b

Browse files
committed
Merge branch 'query-nested-strings' of https://github.com/cpcloud/pandas into cpcloud-query-nested-strings
2 parents eea2749 + 1e37e21 commit e56411b

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

doc/source/release.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ Bug Fixes
101101
- Bug in TimeGrouper/resample when presented with a non-monotonic DatetimeIndex would return invalid results. (:issue:`4161`)
102102
- Bug in index name propogation in TimeGrouper/resample (:issue:`4161`)
103103
- TimeGrouper has a more compatible API to the rest of the groupers (e.g. ``groups`` was missing) (:issue:`3881`)
104+
- Bug in ``pd.eval`` when parsing strings with possible tokens like ``'&'``
105+
(:issue:`6351`)
104106

105107
pandas 0.13.1
106108
-------------

pandas/computation/expr.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,19 @@ def _replace_booleans(source):
252252
"""Replace ``&`` with ``and`` and ``|`` with ``or`` so that bitwise
253253
precedence is changed to boolean precedence.
254254
"""
255-
return source.replace('|', ' or ').replace('&', ' and ')
255+
res = []
256+
g = tokenize.generate_tokens(StringIO(source).readline)
257+
for toknum, tokval, _, _, _ in g:
258+
if toknum == tokenize.OP:
259+
if tokval == '&':
260+
res.append((tokenize.NAME, 'and'))
261+
elif tokval == '|':
262+
res.append((tokenize.NAME, 'or'))
263+
else:
264+
res.append((toknum, tokval))
265+
else:
266+
res.append((toknum, tokval))
267+
return tokenize.untokenize(res)
256268

257269

258270
def _replace_locals(source, local_symbol='@'):

pandas/io/tests/test_pytables.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,7 +2199,7 @@ def test_remove_startstop(self):
21992199
# GH #4835 and #6177
22002200

22012201
with ensure_clean_store(self.path) as store:
2202-
2202+
22032203
wp = tm.makePanel()
22042204

22052205
# start
@@ -2246,7 +2246,7 @@ def test_remove_startstop(self):
22462246
result = store.select('wp6')
22472247
expected = wp.reindex(major_axis=wp.major_axis)
22482248
assert_panel_equal(result, expected)
2249-
2249+
22502250
# with where
22512251
date = wp.major_axis.take(np.arange(0,30,3))
22522252
crit = Term('major_axis=date')
@@ -2256,7 +2256,7 @@ def test_remove_startstop(self):
22562256
result = store.select('wp7')
22572257
expected = wp.reindex(major_axis=wp.major_axis-wp.major_axis[np.arange(0,20,3)])
22582258
assert_panel_equal(result, expected)
2259-
2259+
22602260

22612261
def test_remove_crit(self):
22622262

@@ -4174,6 +4174,14 @@ def test_append_with_diff_col_name_types_raises_value_error(self):
41744174
with tm.assertRaises(ValueError):
41754175
store.append(name, d)
41764176

4177+
def test_query_with_nested_special_character(self):
4178+
df = DataFrame({'a': ['a', 'a', 'c', 'b', 'test & test', 'c' , 'b', 'e'],
4179+
'b': [1, 2, 3, 4, 5, 6, 7, 8]})
4180+
expected = df[df.a == 'test & test']
4181+
with ensure_clean_store(self.path) as store:
4182+
store.append('test', df, format='table', data_columns=True)
4183+
result = store.select('test', 'a = "test & test"')
4184+
tm.assert_frame_equal(expected, result)
41774185

41784186
def _test_sort(obj):
41794187
if isinstance(obj, DataFrame):

pandas/tests/test_frame.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12875,6 +12875,19 @@ def test_query_with_nested_string(self):
1287512875
for parser, engine in product(PARSERS, ENGINES):
1287612876
yield self.check_query_with_nested_strings, parser, engine
1287712877

12878+
def check_query_with_nested_special_character(self, parser, engine):
12879+
skip_if_no_pandas_parser(parser)
12880+
tm.skip_if_no_ne(engine)
12881+
df = DataFrame({'a': ['a', 'b', 'test & test'],
12882+
'b': [1, 2, 3]})
12883+
res = df.query('a == "test & test"', parser=parser, engine=engine)
12884+
expec = df[df.a == 'test & test']
12885+
tm.assert_frame_equal(res, expec)
12886+
12887+
def test_query_with_nested_special_character(self):
12888+
for parser, engine in product(PARSERS, ENGINES):
12889+
yield self.check_query_with_nested_special_character, parser, engine
12890+
1287812891
def check_query_lex_compare_strings(self, parser, engine):
1287912892
tm.skip_if_no_ne(engine=engine)
1288012893
import operator as opr

0 commit comments

Comments
 (0)