3
3
"""Top level ``eval`` module.
4
4
"""
5
5
6
- import warnings
7
6
import tokenize
8
7
from pandas .io .formats .printing import pprint_thing
9
8
from pandas .core .computation import _NUMEXPR_INSTALLED
@@ -148,7 +147,7 @@ def _check_for_locals(expr, stack_level, parser):
148
147
149
148
def eval (expr , parser = 'pandas' , engine = None , truediv = True ,
150
149
local_dict = None , global_dict = None , resolvers = (), level = 0 ,
151
- target = None , inplace = None ):
150
+ target = None , inplace = False ):
152
151
"""Evaluate a Python expression as a string using various backends.
153
152
154
153
The following arithmetic operations are supported: ``+``, ``-``, ``*``,
@@ -207,18 +206,24 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
207
206
scope. Most users will **not** need to change this parameter.
208
207
target : a target object for assignment, optional, default is None
209
208
essentially this is a passed in resolver
210
- inplace : bool, default True
211
- If expression mutates, whether to modify object inplace or return
212
- copy with mutation.
209
+ inplace : bool, default False
210
+ If `target` is provided, and the expression mutates `target`, whether
211
+ to modify `target` inplace. Otherwise, return a copy of `target` with
212
+ the mutation.
213
213
214
- WARNING: inplace=None currently falls back to to True, but
215
- in a future version, will default to False. Use inplace=True
216
- explicitly rather than relying on the default.
214
+ If `inplace=True`, but `target` cannot be modified inplace, a
215
+ ValueError will be raised. Examples of targets that cannot be
216
+ modified inplace are integers and strings. Examples of targets
217
+ that can be modified inplace are lists and class instances.
217
218
218
219
Returns
219
220
-------
220
221
ndarray, numeric scalar, DataFrame, Series
221
222
223
+ Raises
224
+ ------
225
+ ValueError : `inplace=True`, but the provided `target` could not be
226
+ modified inplace.
222
227
Notes
223
228
-----
224
229
The ``dtype`` of any objects involved in an arithmetic ``%`` operation are
@@ -232,8 +237,13 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
232
237
pandas.DataFrame.query
233
238
pandas.DataFrame.eval
234
239
"""
235
- inplace = validate_bool_kwarg (inplace , 'inplace' )
236
- first_expr = True
240
+
241
+ inplace = validate_bool_kwarg (inplace , "inplace" )
242
+ modifiable = hasattr (target , "__setitem__" )
243
+
244
+ if inplace and not modifiable :
245
+ raise ValueError ("Cannot modify the provided target inplace" )
246
+
237
247
if isinstance (expr , string_types ):
238
248
_check_expression (expr )
239
249
exprs = [e .strip () for e in expr .splitlines () if e .strip () != '' ]
@@ -245,7 +255,10 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
245
255
raise ValueError ("multi-line expressions are only valid in the "
246
256
"context of data, use DataFrame.eval" )
247
257
258
+ ret = None
248
259
first_expr = True
260
+ target_modified = False
261
+
249
262
for expr in exprs :
250
263
expr = _convert_expression (expr )
251
264
engine = _check_engine (engine )
@@ -269,21 +282,21 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
269
282
if parsed_expr .assigner is None and multi_line :
270
283
raise ValueError ("Multi-line expressions are only valid"
271
284
" if all expressions contain an assignment" )
285
+ elif not (parsed_expr .assigner is None
286
+ or target is None or modifiable ):
287
+ raise ValueError ("Cannot assign expression output to target" )
272
288
273
289
# assign if needed
274
290
if env .target is not None and parsed_expr .assigner is not None :
275
- if inplace is None :
276
- warnings .warn (
277
- "eval expressions containing an assignment currently"
278
- "default to operating inplace.\n This will change in "
279
- "a future version of pandas, use inplace=True to "
280
- "avoid this warning." ,
281
- FutureWarning , stacklevel = 3 )
282
- inplace = True
291
+ target_modified = True
283
292
293
+ # Cannot assign to the target if it is not assignable.
284
294
# if returning a copy, copy only on the first assignment
285
295
if not inplace and first_expr :
286
- target = env .target .copy ()
296
+ try :
297
+ target = env .target .copy ()
298
+ except AttributeError :
299
+ raise ValueError ("Cannot return a copy of the target" )
287
300
else :
288
301
target = env .target
289
302
@@ -304,7 +317,6 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
304
317
ret = None
305
318
first_expr = False
306
319
307
- if not inplace and inplace is not None :
308
- return target
309
-
310
- return ret
320
+ # We want to exclude `inplace=None` as being False.
321
+ if inplace is False :
322
+ return target if target_modified else ret
0 commit comments