@@ -345,7 +345,7 @@ def get_error(f, *args, **kwargs):
345345
346346# General tasks to eval or exec code, with decorated counterparts -------------
347347
348- # Eval an expression tree or node (with setting envs, pre_code and/or expr_code)
348+
349349@process_task
350350def taskRunEval (
351351 tree ,
@@ -362,41 +362,74 @@ def taskRunEval(
362362 tempname = "_evaluation_object_" ,
363363 call = None ,
364364):
365+ """
366+ Eval an expression tree (with setting envs, pre_code and/or expr_code)
367+ Utility function later wrapped to extract either result (end state of a variable), output (stdout) or error
368+
369+ Args:
370+ tree (ast): current focused ast, used to get code to execute
371+ process: manages shell (see local.py)
372+ shell: link to to get process namespace from execution up until now
373+ env: update value in focused code by name
374+ extra_env: variables to be replaced in focused code by name from extra_env in has_expr
375+ context: sum of set_context in sct chain
376+ context_vals: extra context argument in has_expr
377+ pre_code: argument in has_expr to execute code before evaluating, for example to set a seed
378+ expr_code: code to execute instead of focused code
379+ name: extract value after executing focused expr_code (~post_code)
380+ copy: copy entire env because our expr_code could have side effects
381+ tempname: key for the result when it is added to context, only for v1 sct's
382+ call: only used in v1 sct's
383+
384+ Returns:
385+ str: output of the executed code
386+ """
365387 try :
366388 # Prepare code and mode -----------------------------------------------
367- if (expr_code and name ) or (not expr_code and isinstance (tree , ast .Module )):
389+ # Verify if expr_code is expression code (returning a value) or just runnable code.
390+ if ( # expr_code returns nothing and then we will extract a value
391+ expr_code and name
392+ ) or ( # No expr_code and the tree is of a node type that does not evaluate to have output
393+ not expr_code and isinstance (tree , ast .Module )
394+ ):
395+ # We are not focused on an expression (no output)
368396 mode = "exec"
369397 else :
370398 mode = "eval"
399+ # Wrap the focused node in the tree so it can be run with eval()
371400 if not isinstance (tree , (ast .Module , ast .Expression , ast .Expr )):
372401 tree = ast .Expression (tree )
373402
374403 # Expression code takes precedence over tree code
375404 if expr_code :
376405 code = expr_code
377406 tree = ast .parse (code , mode = mode )
378- else :
407+ else : # Compile the tree to Python code
379408 code = compile (tree , "<script>" , mode )
380409
381410 # Set up environment --------------------------------------------------
382- # avoid deep copy if specified, or if just looking up variable by name
383- # unpack 'container nodes' first
411+ # Unpack 'container nodes' before checking if a deepcopy is needed
384412 if isinstance (tree , ast .Module ):
385413 tree = tree .body
386414 if isinstance (tree , ast .Expression ):
387415 tree = tree .body
388416 if isinstance (tree , ast .Expr ):
389417 tree = tree .value
418+
419+ # Avoid a deep copy if specified or if the ast node type indicates we are looking up a variable by name
420+ # ast.Name, ast.Subscript and ast.Load most of the time do not have side effects in the environment,
421+ # making a deepcopy unnecessary
390422 if not copy or (
391423 isinstance (tree , (ast .Name , ast .Subscript ))
392424 and isinstance (tree .ctx , ast .Load )
393425 ):
394- new_env = dict (get_env (shell .user_ns ))
426+ new_env = dict (get_env (shell .user_ns )) # shallow copy of env
395427 else :
396428 # might raise an error if object refuses pickle interface
397429 # used by deepcopy to restore class
398430 new_env = utils .copy_env (get_env (shell .user_ns ))
399431
432+ # Apply additional env and context variables
400433 if env is not None :
401434 new_env .update (deepcopy (env ))
402435 if extra_env is not None :
@@ -422,13 +455,15 @@ def taskRunEval(
422455 except NameError :
423456 return UndefinedValue ()
424457
425- # call object if dict with args and kwargs was passed
458+ # Backwards compatibility with v1 SCT's
459+ # If object is callable, args and kwargs can be passed in to be used in a call on object
426460 if call is not None :
427461 obj = obj (* call ["args" ], ** call ["kwargs" ])
428462
429463 # Set object as temp variable in original environment, so we can
430464 # later get its class, etc.., in order to extract it from process
431465 get_env (shell .user_ns )[tempname ] = obj
466+
432467 return str (obj )
433468
434469 except Exception as e :
0 commit comments