1
- """:func:`~pandas.eval` parsers
1
+ """
2
+ :func:`~pandas.eval` parsers.
2
3
"""
3
4
4
5
import ast
5
6
from functools import partial , reduce
6
7
from keyword import iskeyword
7
8
import tokenize
8
- from typing import Optional , Type
9
+ from typing import Callable , Optional , Set , Tuple , Type , TypeVar
9
10
10
11
import numpy as np
11
12
34
35
import pandas .io .formats .printing as printing
35
36
36
37
37
- def _rewrite_assign (tok ):
38
- """Rewrite the assignment operator for PyTables expressions that use ``=``
38
+ def _rewrite_assign (tok : Tuple [int , str ]) -> Tuple [int , str ]:
39
+ """
40
+ Rewrite the assignment operator for PyTables expressions that use ``=``
39
41
as a substitute for ``==``.
40
42
41
43
Parameters
@@ -45,15 +47,16 @@ def _rewrite_assign(tok):
45
47
46
48
Returns
47
49
-------
48
- t : tuple of int, str
50
+ tuple of int, str
49
51
Either the input or token or the replacement values
50
52
"""
51
53
toknum , tokval = tok
52
54
return toknum , "==" if tokval == "=" else tokval
53
55
54
56
55
- def _replace_booleans (tok ):
56
- """Replace ``&`` with ``and`` and ``|`` with ``or`` so that bitwise
57
+ def _replace_booleans (tok : Tuple [int , str ]) -> Tuple [int , str ]:
58
+ """
59
+ Replace ``&`` with ``and`` and ``|`` with ``or`` so that bitwise
57
60
precedence is changed to boolean precedence.
58
61
59
62
Parameters
@@ -63,7 +66,7 @@ def _replace_booleans(tok):
63
66
64
67
Returns
65
68
-------
66
- t : tuple of int, str
69
+ tuple of int, str
67
70
Either the input or token or the replacement values
68
71
"""
69
72
toknum , tokval = tok
@@ -76,8 +79,9 @@ def _replace_booleans(tok):
76
79
return toknum , tokval
77
80
78
81
79
- def _replace_locals (tok ):
80
- """Replace local variables with a syntactically valid name.
82
+ def _replace_locals (tok : Tuple [int , str ]) -> Tuple [int , str ]:
83
+ """
84
+ Replace local variables with a syntactically valid name.
81
85
82
86
Parameters
83
87
----------
@@ -86,7 +90,7 @@ def _replace_locals(tok):
86
90
87
91
Returns
88
92
-------
89
- t : tuple of int, str
93
+ tuple of int, str
90
94
Either the input or token or the replacement values
91
95
92
96
Notes
@@ -102,12 +106,16 @@ def _replace_locals(tok):
102
106
103
107
104
108
def _compose2 (f , g ):
105
- """Compose 2 callables"""
109
+ """
110
+ Compose 2 callables.
111
+ """
106
112
return lambda * args , ** kwargs : f (g (* args , ** kwargs ))
107
113
108
114
109
115
def _compose (* funcs ):
110
- """Compose 2 or more callables"""
116
+ """
117
+ Compose 2 or more callables.
118
+ """
111
119
assert len (funcs ) > 1 , "At least 2 callables must be passed to compose"
112
120
return reduce (_compose2 , funcs )
113
121
@@ -117,8 +125,9 @@ def _preparse(
117
125
f = _compose (
118
126
_replace_locals , _replace_booleans , _rewrite_assign , clean_backtick_quoted_toks
119
127
),
120
- ):
121
- """Compose a collection of tokenization functions
128
+ ) -> str :
129
+ """
130
+ Compose a collection of tokenization functions.
122
131
123
132
Parameters
124
133
----------
@@ -132,7 +141,7 @@ def _preparse(
132
141
133
142
Returns
134
143
-------
135
- s : str
144
+ str
136
145
Valid Python source code
137
146
138
147
Notes
@@ -146,7 +155,9 @@ def _preparse(
146
155
147
156
148
157
def _is_type (t ):
149
- """Factory for a type checking function of type ``t`` or tuple of types."""
158
+ """
159
+ Factory for a type checking function of type ``t`` or tuple of types.
160
+ """
150
161
return lambda x : isinstance (x .value , t )
151
162
152
163
@@ -164,7 +175,9 @@ def _is_type(t):
164
175
165
176
166
177
def _filter_nodes (superclass , all_nodes = _all_nodes ):
167
- """Filter out AST nodes that are subclasses of ``superclass``."""
178
+ """
179
+ Filter out AST nodes that are subclasses of ``superclass``.
180
+ """
168
181
node_names = (node .__name__ for node in all_nodes if issubclass (node , superclass ))
169
182
return frozenset (node_names )
170
183
@@ -227,30 +240,35 @@ def _filter_nodes(superclass, all_nodes=_all_nodes):
227
240
assert not intersection , _msg
228
241
229
242
230
- def _node_not_implemented (node_name , cls ):
231
- """Return a function that raises a NotImplementedError with a passed node
232
- name.
243
+ # TODO: Python 3.6.2: replace Callable[..., None] with Callable[..., NoReturn]
244
+ def _node_not_implemented (node_name : str ) -> Callable [..., None ]:
245
+ """
246
+ Return a function that raises a NotImplementedError with a passed node name.
233
247
"""
234
248
235
249
def f (self , * args , ** kwargs ):
236
- raise NotImplementedError (f"{ repr ( node_name ) } nodes are not implemented" )
250
+ raise NotImplementedError (f"' { node_name } ' nodes are not implemented" )
237
251
238
252
return f
239
253
240
254
241
- def disallow (nodes ):
242
- """Decorator to disallow certain nodes from parsing. Raises a
255
+ _T = TypeVar ("_T" , bound = "BaseExprVisitor" )
256
+
257
+
258
+ def disallow (nodes : Set [str ]) -> Callable [[Type [_T ]], Type [_T ]]:
259
+ """
260
+ Decorator to disallow certain nodes from parsing. Raises a
243
261
NotImplementedError instead.
244
262
245
263
Returns
246
264
-------
247
- disallowed : callable
265
+ callable
248
266
"""
249
267
250
- def disallowed (cls ) :
268
+ def disallowed (cls : Type [ _T ]) -> Type [ _T ] :
251
269
cls .unsupported_nodes = ()
252
270
for node in nodes :
253
- new_method = _node_not_implemented (node , cls )
271
+ new_method = _node_not_implemented (node )
254
272
name = f"visit_{ node } "
255
273
cls .unsupported_nodes += (name ,)
256
274
setattr (cls , name , new_method )
@@ -260,20 +278,21 @@ def disallowed(cls):
260
278
261
279
262
280
def _op_maker (op_class , op_symbol ):
263
- """Return a function to create an op class with its symbol already passed.
281
+ """
282
+ Return a function to create an op class with its symbol already passed.
264
283
265
284
Returns
266
285
-------
267
- f : callable
286
+ callable
268
287
"""
269
288
270
289
def f (self , node , * args , ** kwargs ):
271
- """Return a partial function with an Op subclass with an operator
272
- already passed.
290
+ """
291
+ Return a partial function with an Op subclass with an operator already passed.
273
292
274
293
Returns
275
294
-------
276
- f : callable
295
+ callable
277
296
"""
278
297
return partial (op_class , op_symbol , * args , ** kwargs )
279
298
@@ -284,7 +303,9 @@ def f(self, node, *args, **kwargs):
284
303
285
304
286
305
def add_ops (op_classes ):
287
- """Decorator to add default implementation of ops."""
306
+ """
307
+ Decorator to add default implementation of ops.
308
+ """
288
309
289
310
def f (cls ):
290
311
for op_attr_name , op_class in op_classes .items ():
@@ -353,6 +374,8 @@ class BaseExprVisitor(ast.NodeVisitor):
353
374
ast .NotIn : ast .NotIn ,
354
375
}
355
376
377
+ unsupported_nodes : Tuple [str , ...]
378
+
356
379
def __init__ (self , env , engine , parser , preparser = _preparse ):
357
380
self .env = env
358
381
self .engine = engine
@@ -647,7 +670,7 @@ def visit_Call(self, node, side=None, **kwargs):
647
670
f'Function "{ res .name } " does not support keyword arguments'
648
671
)
649
672
650
- return res (* new_args , ** kwargs )
673
+ return res (* new_args )
651
674
652
675
else :
653
676
@@ -777,12 +800,16 @@ def __len__(self) -> int:
777
800
return len (self .expr )
778
801
779
802
def parse (self ):
780
- """Parse an expression"""
803
+ """
804
+ Parse an expression.
805
+ """
781
806
return self ._visitor .visit (self .expr )
782
807
783
808
@property
784
809
def names (self ):
785
- """Get the names in an expression"""
810
+ """
811
+ Get the names in an expression.
812
+ """
786
813
if is_term (self .terms ):
787
814
return frozenset ([self .terms .name ])
788
815
return frozenset (term .name for term in com .flatten (self .terms ))
0 commit comments