Commit 3220e47f authored by Ayush Tiwari's avatar Ayush Tiwari

erp5_data_notebook bt5: Use interactivity/mode and finally execute all code...

erp5_data_notebook bt5: Use interactivity/mode and finally execute all code using exec to get rid of try:except
parent 33787a95
......@@ -4,12 +4,30 @@ from StringIO import StringIO
from Products.ERP5Type.Globals import PersistentMapping
import sys
import ast
def Base_compileJupyterCode(self, jupyter_code, old_local_variable_dict):
"""
Function to execute jupyter code and update the local_varibale dictionary.
It also handles the exception raised, catch them and return them as a python
dictionary. Use of eval for expressions and exec for statements
Code execution depends on 'interactivity', a.k.a , if the ast.node object has
ast.Expr instance(valid for expressions) or not.
The behaviour would be similar to that of jupyter notebook:-
( https://github.com/ipython/ipython/blob/master/IPython/core/interactiveshell.py#L2954 )
Example:
code1 = '''
23
print 23 #Last node not an expression, interactivity = 'last'
'''
out1 = '23'
code2 = '''
123
12 #Last node an expression, interactivity = 'none'
'''
out2 = '12'
"""
# Other way would be to use all the globals variables instead of just an empty
# dictionary, but that might hamper the speed of exec or eval.
......@@ -31,39 +49,54 @@ def Base_compileJupyterCode(self, jupyter_code, old_local_variable_dict):
# catching errors on server/erp5.
status = u'ok'
# eval used before exec because exec can handle the error raised by both eval
# and exec. Example to explain execution :-
# code eval_result eval_error exec_result exec_error
# =======================================================================
# print 42 None Syntax Error 42 None
# 42 42 None None None
# abc None Name Error None Name Error
# a=42;print ab None Syntax Error None Name Error
# From above, it infers that the invalid syntax is being handled by exec only
try:
jupyter_compiled = compile(jupyter_code, '<string>', 'eval')
eval_result = eval(jupyter_compiled, g, g)
result_string = str(eval_result)
# Trying to catch everything which results in error from eval
# It can be just an invalid syntax, invalid expression or some error
except Exception:
# Returning the printed statement from exec using sys.stdout
# Catching the executed output and saving it in a variable
# Execute only if jupyter_code is not empty
if jupyter_code:
# Create ast parse tree
ast_node = ast.parse(jupyter_code)
# Get the node list from the parsed tree
nodelist = ast_node.body
# If the last node is instance of ast.Expr, set its interactivity as 'last'
# This would be the case if the last node is expression
if isinstance(nodelist[-1], ast.Expr):
interactivity = "last"
else:
interactivity = "none"
# Here, we define which nodes to execute with 'single' and which to execute
# with 'exec' mode.
if interactivity == 'none':
to_run_exec, to_run_interactive = nodelist, []
elif interactivity == 'last':
to_run_exec, to_run_interactive = nodelist[:-1], nodelist[-1:]
old_stdout = sys.stdout
result = StringIO()
sys.stdout = result
# Execute the nodes with 'exec' mode
for node in to_run_exec:
mod = ast.Module([node])
code = compile(mod, '<string>', "exec")
exec(code, g, g)
# Execute the interactive nodes with 'single' mode
for node in to_run_interactive:
mod = ast.Interactive([node])
code = compile(mod, '<string>', "single")
exec(code, g, g)
# Letting the code fail in case of error while executing the python script/code
# XXX: Need to be refactored so to acclimitize transactions failure as well as
# normal python code failure and show it to user on jupyter frontend.
# Decided to let this fail silently in backend without letting the frontend
# user know the error so as to let tranasction or its error be handled by ZODB
# in uniform way instead of just using half transactions.
jupyter_compiled = compile(jupyter_code, '<string>', 'exec')
exec(jupyter_compiled, g, g)
sys.stdout = old_stdout
result_string = result.getvalue()
else:
result_string = jupyter_code
# Difference between the globals variable before and after exec/eval so that
# we don't have to save unnecessary variables in database which might or might
......
......@@ -46,8 +46,8 @@
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple>
<string>W: 45, 18: Use of eval (eval-used)</string>
<string>W: 64, 4: Use of exec (exec-used)</string>
<string>W: 81, 6: Use of exec (exec-used)</string>
<string>W: 87, 6: Use of exec (exec-used)</string>
</tuple>
</value>
</item>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment