7
7
import inspect
8
8
import tokenize
9
9
import datetime
10
- import struct
11
10
12
11
from functools import partial
13
12
16
15
from pandas .compat import StringIO , zip , reduce , string_types
17
16
from pandas .core .base import StringMixin
18
17
from pandas .core import common as com
19
- from pandas .computation .common import NameResolutionError
20
18
from pandas .computation .ops import (_cmp_ops_syms , _bool_ops_syms ,
21
19
_arith_ops_syms , _unary_ops_syms , is_term )
22
20
from pandas .computation .ops import _reductions , _mathops , _LOCAL_TAG
23
21
from pandas .computation .ops import Op , BinOp , UnaryOp , Term , Constant , Div
24
22
from pandas .computation .ops import UndefinedVariableError
25
-
26
-
27
- def _ensure_scope (level = 2 , global_dict = None , local_dict = None , resolvers = None ,
28
- target = None , ** kwargs ):
29
- """Ensure that we are grabbing the correct scope."""
30
- return Scope (gbls = global_dict , lcls = local_dict , level = level ,
31
- resolvers = resolvers , target = target )
32
-
33
-
34
- def _check_disjoint_resolver_names (resolver_keys , local_keys , global_keys ):
35
- """Make sure that variables in resolvers don't overlap with locals or
36
- globals.
37
- """
38
- res_locals = list (com .intersection (resolver_keys , local_keys ))
39
- if res_locals :
40
- msg = "resolvers and locals overlap on names {0}" .format (res_locals )
41
- raise NameResolutionError (msg )
42
-
43
- res_globals = list (com .intersection (resolver_keys , global_keys ))
44
- if res_globals :
45
- msg = "resolvers and globals overlap on names {0}" .format (res_globals )
46
- raise NameResolutionError (msg )
47
-
48
-
49
- def _replacer (x , pad_size ):
50
- """Replace a number with its padded hexadecimal representation. Used to tag
51
- temporary variables with their calling scope's id.
52
- """
53
- # get the hex repr of the binary char and remove 0x and pad by pad_size
54
- # zeros
55
- try :
56
- hexin = ord (x )
57
- except TypeError :
58
- # bytes literals masquerade as ints when iterating in py3
59
- hexin = x
60
-
61
- return hex (hexin ).replace ('0x' , '' ).rjust (pad_size , '0' )
62
-
63
-
64
- def _raw_hex_id (obj , pad_size = 2 ):
65
- """Return the padded hexadecimal id of ``obj``."""
66
- # interpret as a pointer since that's what really what id returns
67
- packed = struct .pack ('@P' , id (obj ))
68
-
69
- return '' .join (_replacer (x , pad_size ) for x in packed )
70
-
71
-
72
- class Scope (StringMixin ):
73
-
74
- """Object to hold scope, with a few bells to deal with some custom syntax
75
- added by pandas.
76
-
77
- Parameters
78
- ----------
79
- gbls : dict or None, optional, default None
80
- lcls : dict or Scope or None, optional, default None
81
- level : int, optional, default 1
82
- resolvers : list-like or None, optional, default None
83
-
84
- Attributes
85
- ----------
86
- globals : dict
87
- locals : dict
88
- level : int
89
- resolvers : tuple
90
- resolver_keys : frozenset
91
- """
92
- __slots__ = ('globals' , 'locals' , 'resolvers' , '_global_resolvers' ,
93
- 'resolver_keys' , '_resolver' , 'level' , 'ntemps' , 'target' )
94
-
95
- def __init__ (self , gbls = None , lcls = None , level = 1 , resolvers = None ,
96
- target = None ):
97
- self .level = level
98
- self .resolvers = tuple (resolvers or [])
99
- self .globals = dict ()
100
- self .locals = dict ()
101
- self .target = target
102
- self .ntemps = 1 # number of temporary variables in this scope
103
-
104
- if isinstance (lcls , Scope ):
105
- ld , lcls = lcls , dict ()
106
- self .locals .update (ld .locals .copy ())
107
- self .globals .update (ld .globals .copy ())
108
- self .resolvers += ld .resolvers
109
- if ld .target is not None :
110
- self .target = ld .target
111
- self .update (ld .level )
112
-
113
- frame = sys ._getframe (level )
114
- try :
115
- self .globals .update (gbls or frame .f_globals )
116
- self .locals .update (lcls or frame .f_locals )
117
- finally :
118
- del frame
119
-
120
- # add some useful defaults
121
- self .globals ['Timestamp' ] = pd .lib .Timestamp
122
- self .globals ['datetime' ] = datetime
123
-
124
- # SUCH a hack
125
- self .globals ['True' ] = True
126
- self .globals ['False' ] = False
127
-
128
- # function defs
129
- self .globals ['list' ] = list
130
- self .globals ['tuple' ] = tuple
131
-
132
- res_keys = (list (o .keys ()) for o in self .resolvers )
133
- self .resolver_keys = frozenset (reduce (operator .add , res_keys , []))
134
- self ._global_resolvers = self .resolvers + (self .locals , self .globals )
135
- self ._resolver = None
136
-
137
- self .resolver_dict = {}
138
- for o in self .resolvers :
139
- self .resolver_dict .update (dict (o ))
140
-
141
- def __unicode__ (self ):
142
- return com .pprint_thing (
143
- 'locals: {0}\n globals: {0}\n resolvers: '
144
- '{0}\n target: {0}' .format (list (self .locals .keys ()),
145
- list (self .globals .keys ()),
146
- list (self .resolver_keys ),
147
- self .target ))
148
-
149
- def __getitem__ (self , key ):
150
- return self .resolve (key , globally = False )
151
-
152
- def resolve (self , key , globally = False ):
153
- resolvers = self .locals , self .globals
154
- if globally :
155
- resolvers = self ._global_resolvers
156
-
157
- for resolver in resolvers :
158
- try :
159
- return resolver [key ]
160
- except KeyError :
161
- pass
162
-
163
- def update (self , level = None ):
164
- """Update the current scope by going back `level` levels.
165
-
166
- Parameters
167
- ----------
168
- level : int or None, optional, default None
169
- """
170
- # we are always 2 levels below the caller
171
- # plus the caller may be below the env level
172
- # in which case we need addtl levels
173
- sl = 2
174
- if level is not None :
175
- sl += level
176
-
177
- # add sl frames to the scope starting with the
178
- # most distant and overwritting with more current
179
- # makes sure that we can capture variable scope
180
- frame = inspect .currentframe ()
181
- try :
182
- frames = []
183
- while sl >= 0 :
184
- frame = frame .f_back
185
- sl -= 1
186
- if frame is None :
187
- break
188
- frames .append (frame )
189
- for f in frames [::- 1 ]:
190
- self .locals .update (f .f_locals )
191
- self .globals .update (f .f_globals )
192
- finally :
193
- del frame , frames
194
-
195
- def add_tmp (self , value , where = 'locals' ):
196
- """Add a temporary variable to the scope.
197
-
198
- Parameters
199
- ----------
200
- value : object
201
- An arbitrary object to be assigned to a temporary variable.
202
- where : basestring, optional, default 'locals', {'locals', 'globals'}
203
- What scope to add the value to.
204
-
205
- Returns
206
- -------
207
- name : basestring
208
- The name of the temporary variable created.
209
- """
210
- d = getattr (self , where , None )
211
-
212
- if d is None :
213
- raise AttributeError ("Cannot add value to non-existent scope "
214
- "{0!r}" .format (where ))
215
- if not isinstance (d , dict ):
216
- raise TypeError ("Cannot add value to object of type {0!r}, "
217
- "scope must be a dictionary"
218
- "" .format (type (d ).__name__ ))
219
- name = 'tmp_var_{0}_{1}_{2}' .format (type (value ).__name__ , self .ntemps ,
220
- _raw_hex_id (self ))
221
- d [name ] = value
222
-
223
- # only increment if the variable gets put in the scope
224
- self .ntemps += 1
225
- return name
226
-
227
- def remove_tmp (self , name , where = 'locals' ):
228
- d = getattr (self , where , None )
229
- if d is None :
230
- raise AttributeError ("Cannot remove value from non-existent scope "
231
- "{0!r}" .format (where ))
232
- if not isinstance (d , dict ):
233
- raise TypeError ("Cannot remove value from object of type {0!r}, "
234
- "scope must be a dictionary"
235
- "" .format (type (d ).__name__ ))
236
- del d [name ]
237
- self .ntemps -= 1
23
+ from pandas .computation .scope import Scope , _ensure_scope
238
24
239
25
240
26
def _rewrite_assign (source ):
@@ -549,8 +335,8 @@ def visit_BinOp(self, node, **kwargs):
549
335
return self ._possibly_evaluate_binop (op , op_class , left , right )
550
336
551
337
def visit_Div (self , node , ** kwargs ):
552
- return lambda lhs , rhs : Div ( lhs , rhs ,
553
- truediv = self . env . locals [ 'truediv' ] )
338
+ truediv = self . env . scope [ 'truediv' ]
339
+ return lambda lhs , rhs : Div ( lhs , rhs , truediv )
554
340
555
341
def visit_UnaryOp (self , node , ** kwargs ):
556
342
op = self .visit (node .op )
@@ -631,15 +417,14 @@ def visit_Assign(self, node, **kwargs):
631
417
632
418
try :
633
419
assigner = self .visit (node .targets [0 ], ** kwargs )
634
- except ( UndefinedVariableError , KeyError ) :
420
+ except UndefinedVariableError :
635
421
assigner = node .targets [0 ].id
636
422
637
423
self .assigner = getattr (assigner , 'name' , assigner )
638
424
if self .assigner is None :
639
425
raise SyntaxError ('left hand side of an assignment must be a '
640
426
'single resolvable name' )
641
427
642
- import ipdb ; ipdb .set_trace ()
643
428
return self .visit (node .value , ** kwargs )
644
429
645
430
def visit_Attribute (self , node , ** kwargs ):
@@ -769,21 +554,20 @@ class Expr(StringMixin):
769
554
"""
770
555
771
556
def __init__ (self , expr , engine = 'numexpr' , parser = 'pandas' , env = None ,
772
- truediv = True , level = 2 ):
557
+ truediv = True , level = 0 ):
773
558
self .expr = expr
774
- self .env = _ensure_scope (level = level , local_dict = env )
559
+ self .env = env or Scope (level = level + 1 )
775
560
self .engine = engine
776
561
self .parser = parser
562
+ self .env .scope ['truediv' ] = truediv
777
563
self ._visitor = _parsers [parser ](self .env , self .engine , self .parser )
778
564
self .terms = self .parse ()
779
- self .truediv = truediv
780
565
781
566
@property
782
567
def assigner (self ):
783
568
return getattr (self ._visitor , 'assigner' , None )
784
569
785
570
def __call__ (self ):
786
- self .env .locals ['truediv' ] = self .truediv
787
571
return self .terms (self .env )
788
572
789
573
def __unicode__ (self ):
@@ -807,34 +591,5 @@ def names(self):
807
591
return frozenset ([self .terms .name ])
808
592
return frozenset (term .name for term in com .flatten (self .terms ))
809
593
810
- def check_name_clashes (self ):
811
- env = self .env
812
- names = self .names
813
- res_keys = frozenset (env .resolver_dict .keys ()) & names
814
- lcl_keys = frozenset (env .locals .keys ()) & names
815
- gbl_keys = frozenset (env .globals .keys ()) & names
816
- _check_disjoint_resolver_names (res_keys , lcl_keys , gbl_keys )
817
-
818
- def add_resolvers_to_locals (self ):
819
- """Add the extra scope (resolvers) to local scope
820
-
821
- Notes
822
- -----
823
- This should be done after parsing and pre-evaluation, otherwise
824
- unnecessary name clashes will occur.
825
- """
826
- self .env .locals .update (self .env .resolver_dict )
827
-
828
-
829
- def isexpr (s , check_names = True ):
830
- """Strict checking for a valid expression."""
831
- try :
832
- Expr (s , env = _ensure_scope () if check_names else None )
833
- except SyntaxError :
834
- return False
835
- except NameError :
836
- return not check_names
837
- return True
838
-
839
594
840
595
_parsers = {'python' : PythonExprVisitor , 'pandas' : PandasExprVisitor }
0 commit comments