3
3
4
4
import ast
5
5
from functools import partial
6
+ import itertools as it
7
+ import operator
6
8
import tokenize
7
9
8
10
import numpy as np
9
11
10
- from pandas .compat import StringIO , lmap , reduce , string_types , zip
12
+ from pandas .compat import StringIO , lmap , map , reduce , string_types , zip
11
13
12
14
import pandas as pd
13
15
from pandas import compat
14
16
from pandas .core import common as com
15
17
from pandas .core .base import StringMixin
18
+ from pandas .core .computation .common import (
19
+ _BACKTICK_QUOTED_STRING , _remove_spaces_column_name )
16
20
from pandas .core .computation .ops import (
17
21
_LOCAL_TAG , BinOp , Constant , Div , FuncNode , Op , Term , UnaryOp ,
18
22
UndefinedVariableError , _arith_ops_syms , _bool_ops_syms , _cmp_ops_syms ,
@@ -31,7 +35,17 @@ def tokenize_string(source):
31
35
A Python source code string
32
36
"""
33
37
line_reader = StringIO (source ).readline
34
- for toknum , tokval , _ , _ , _ in tokenize .generate_tokens (line_reader ):
38
+ token_generator = tokenize .generate_tokens (line_reader )
39
+
40
+ # Loop over all tokens till a backtick (`) is found.
41
+ # Then, take all tokens till the next backtick to form a backtick quoted
42
+ # string.
43
+ for toknum , tokval , _ , _ , _ in token_generator :
44
+ if tokval == '`' :
45
+ tokval = " " .join (it .takewhile (
46
+ lambda tokval : tokval != '`' ,
47
+ map (operator .itemgetter (1 ), token_generator )))
48
+ toknum = _BACKTICK_QUOTED_STRING
35
49
yield toknum , tokval
36
50
37
51
@@ -102,6 +116,31 @@ def _replace_locals(tok):
102
116
return toknum , tokval
103
117
104
118
119
+ def _clean_spaces_backtick_quoted_names (tok ):
120
+ """Clean up a column name if surrounded by backticks.
121
+
122
+ Backtick quoted string are indicated by a certain tokval value. If a string
123
+ is a backtick quoted token it will processed by
124
+ :func:`_remove_spaces_column_name` so that the parser can find this
125
+ string when the query is executed.
126
+ See also :meth:`NDFrame._get_space_character_free_column_resolver`.
127
+
128
+ Parameters
129
+ ----------
130
+ tok : tuple of int, str
131
+ ints correspond to the all caps constants in the tokenize module
132
+
133
+ Returns
134
+ -------
135
+ t : tuple of int, str
136
+ Either the input or token or the replacement values
137
+ """
138
+ toknum , tokval = tok
139
+ if toknum == _BACKTICK_QUOTED_STRING :
140
+ return tokenize .NAME , _remove_spaces_column_name (tokval )
141
+ return toknum , tokval
142
+
143
+
105
144
def _compose2 (f , g ):
106
145
"""Compose 2 callables"""
107
146
return lambda * args , ** kwargs : f (g (* args , ** kwargs ))
@@ -114,7 +153,8 @@ def _compose(*funcs):
114
153
115
154
116
155
def _preparse (source , f = _compose (_replace_locals , _replace_booleans ,
117
- _rewrite_assign )):
156
+ _rewrite_assign ,
157
+ _clean_spaces_backtick_quoted_names )):
118
158
"""Compose a collection of tokenization functions
119
159
120
160
Parameters
@@ -711,8 +751,9 @@ def visitor(x, y):
711
751
class PandasExprVisitor (BaseExprVisitor ):
712
752
713
753
def __init__ (self , env , engine , parser ,
714
- preparser = partial (_preparse , f = _compose (_replace_locals ,
715
- _replace_booleans ))):
754
+ preparser = partial (_preparse , f = _compose (
755
+ _replace_locals , _replace_booleans ,
756
+ _clean_spaces_backtick_quoted_names ))):
716
757
super (PandasExprVisitor , self ).__init__ (env , engine , parser , preparser )
717
758
718
759
0 commit comments