Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Laurent S
erp5
Commits
eb896b21
Commit
eb896b21
authored
Apr 19, 2018
by
Klaus Wölfel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
jupyter: store parameters for notebook in notebook context
parent
6394ffbf
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
131 additions
and
62 deletions
+131
-62
bt5/erp5_data_notebook/ExtensionTemplateItem/portal_components/extension.erp5.JupyterCompile.py
...teItem/portal_components/extension.erp5.JupyterCompile.py
+122
-55
bt5/erp5_data_notebook/ExtensionTemplateItem/portal_components/extension.erp5.JupyterCompile.xml
...eItem/portal_components/extension.erp5.JupyterCompile.xml
+9
-7
No files found.
bt5/erp5_data_notebook/ExtensionTemplateItem/portal_components/extension.erp5.JupyterCompile.py
View file @
eb896b21
...
...
@@ -28,6 +28,19 @@ from ipykernel.jsonutil import json_clean, encode_images
import
threading
display_data_wrapper_lock
=
threading
.
Lock
()
# Well known unserializable types
from
Record
import
Record
well_known_unserializable_type_tuple
=
(
ModuleType
,
Record
)
# ZBigArray may not be availableK
try
:
from
wendelin.bigarray.array_zodb
import
ZBigArray
# FIXME ZBigArrays are regular ZODB objects and must be serializable
# FIXME the bug is probably in CanSerialize()
# FIXME -> see https://lab.nexedi.com/nexedi/erp5/commit/5fb16acd#note_33582 for details
well_known_unserializable_type_tuple
=
tuple
(
list
(
well_known_unserializable_type_tuple
)
+
[
ZBigArray
])
except
ImportError
:
pass
def
Base_executeJupyter
(
self
,
python_expression
=
None
,
reference
=
None
,
\
title
=
None
,
request_reference
=
False
,
**
kw
):
# Check permissions for current user and display message to non-authorized user
...
...
@@ -66,14 +79,22 @@ def Base_executeJupyter(self, python_expression=None, reference=None, \
title
=
title
,
reference
=
reference
,
batch_mode
=
True
)
# Add new Data Notebook Line to the Data Notebook
data_notebook_line
=
data_notebook
.
DataNotebook_addDataNotebookLine
(
# By default, store_history is True
store_history
=
kw
.
get
(
'store_history'
,
True
)
# Klaus
store_history
=
False
data_notebook_line
=
None
if
store_history
:
# Add new Data Notebook Line to the Data Notebook
data_notebook_line
=
data_notebook
.
DataNotebook_addDataNotebookLine
(
notebook_code
=
python_expression
,
batch_mode
=
True
)
# Gets the context associated to the data notebook being used
old_notebook_context
=
data_notebook
.
getNotebookContext
()
old_notebook_context
=
None
if
data_notebook
.
hasNotebookContext
():
old_notebook_context
=
data_notebook
.
getNotebookContext
().
copy
()
if
not
old_notebook_context
:
old_notebook_context
=
self
.
Base_createNotebookContext
()
...
...
@@ -83,6 +104,9 @@ def Base_executeJupyter(self, python_expression=None, reference=None, \
new_notebook_context
=
final_result
[
'notebook_context'
]
# Klaus
new_notebook_context
[
"variables"
]
=
old_notebook_context
[
"variables"
]
result
=
{
u'code_result'
:
final_result
[
'result_string'
],
u'print_result'
:
final_result
[
'print_result'
],
...
...
@@ -110,6 +134,7 @@ def Base_executeJupyter(self, python_expression=None, reference=None, \
transaction
.
abort
()
exception_dict
=
getErrorMessageForException
(
self
,
e
,
new_notebook_context
)
result
.
update
(
exception_dict
)
result
[
'notebook_context'
]
=
None
return
json
.
dumps
(
result
)
# Catch exception while seriaizing the result to be passed to jupyter frontend
...
...
@@ -128,10 +153,11 @@ def Base_executeJupyter(self, python_expression=None, reference=None, \
u'status'
:
u'error'
,
u'mime_type'
:
result
[
'mime_type'
]}
serialized_result
=
json
.
dumps
(
result
)
data_notebook_line
.
edit
(
notebook_code_result
=
result
[
'code_result'
],
mime_type
=
result
[
'mime_type'
])
if
data_notebook_line
is
not
None
:
data_notebook_line
.
edit
(
notebook_code_result
=
result
[
'code_result'
],
mime_type
=
result
[
'mime_type'
])
return
serialized_result
...
...
@@ -147,7 +173,7 @@ def mergeTracebackListIntoResultDict(result_dict, error_result_dict_list):
def
matplotlib_pre_run
():
matplotlib
.
interactive
(
Tru
e
)
matplotlib
.
interactive
(
Fals
e
)
rc
=
{
'figure.figsize'
:
(
6.0
,
4.0
),
'figure.facecolor'
:
(
1
,
1
,
1
,
0
),
'figure.edgecolor'
:
(
1
,
1
,
1
,
0
),
...
...
@@ -176,7 +202,10 @@ def matplotlib_post_run(data_list):
class
Displayhook
(
object
):
def
hook
(
self
,
value
):
if
value
is
not
None
:
self
.
result
=
repr
(
value
)
if
getattr
(
value
,
'_repr_html_'
,
None
)
is
not
None
:
self
.
result
=
{
'data'
:{
'text/html'
:
value
.
_repr_html_
()},
'metadata'
:{}}
else
:
self
.
result
=
repr
(
value
)
def
pre_run
(
self
):
self
.
old_hook
=
sys
.
displayhook
sys
.
displayhook
=
self
.
hook
...
...
@@ -277,6 +306,20 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
print_fixer
=
PrintFixer
()
environment_collector
=
EnvironmentParser
()
ast_node
=
import_fixer
.
visit
(
ast_node
)
# Whenever we have new imports we need to warn the user about the
# environment
if
(
import_fixer
.
warning_module_names
!=
[]):
warning
=
(
"print '"
"WARNING: You imported from the modules %s without "
"using the environment object, which is not recomended. "
"Your import was automatically converted to use such method. "
"The setup functions were named as *module*_setup. "
"'"
)
%
(
', '
.
join
(
import_fixer
.
warning_module_names
))
tree
=
ast
.
parse
(
warning
)
tree
.
body
[
0
].
lineno
=
ast_node
.
body
[
-
1
].
lineno
+
5
ast_node
.
body
.
append
(
tree
.
body
[
0
])
ast_node
=
print_fixer
.
visit
(
ast_node
)
ast
.
fix_missing_locations
(
ast_node
)
...
...
@@ -335,6 +378,7 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
'_print'
:
CustomPrint
()}
user_context
.
update
(
inject_variable_dict
)
user_context
.
update
(
notebook_context
[
'variables'
])
user_context
[
'parameter_dict'
]
=
notebook_context
[
'parameter_dict'
]
# Getting the environment setup defined in the current code cell
current_setup_dict
=
environment_collector
.
getEnvironmentSetupDict
()
...
...
@@ -381,6 +425,7 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
del
notebook_context
[
'setup'
][
key
]
# Running all the setup functions that we got
failed_setup_key_list
=
[]
for
key
,
value
in
notebook_context
[
'setup'
].
iteritems
():
try
:
code
=
compile
(
value
[
'code'
],
'<string>'
,
'exec'
)
...
...
@@ -388,12 +433,15 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
# An error happened, so we show the user the stacktrace along with a
# note that the exception happened in a setup function's code.
except
Exception
as
e
:
failed_setup_key_list
.
append
(
key
)
if
value
[
'func_name'
]
in
user_context
:
del
user_context
[
value
[
'func_name'
]]
error_return_dict
=
getErrorMessageForException
(
self
,
e
,
notebook_context
)
additional_information
=
"An error happened when trying to run the one of your setup functions:"
error_return_dict
[
'traceback'
].
insert
(
0
,
additional_information
)
setup_error_return_dict_list
.
append
(
error_return_dict
)
for
failed_setup_key
in
failed_setup_key_list
:
del
notebook_context
[
'setup'
][
failed_setup_key
]
# Iterating over envinronment.define calls captured by the environment collector
# that are functions and saving them as setup functions.
...
...
@@ -420,8 +468,8 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
user_context
[
'_volatile_variable_list'
]
+=
variable
if
environment_collector
.
showEnvironmentSetup
():
inject_variable_dict
.
write
(
"%s
\
n
"
%
str
(
notebook_context
[
'setup'
]))
inject_variable_dict
[
'_print'
]
.
write
(
"%s
\
n
"
%
str
(
notebook_context
[
'setup'
]))
# Execute the nodes with 'exec' mode
for
node
in
to_run_exec
:
mod
=
ast
.
Module
([
node
])
...
...
@@ -429,10 +477,12 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
try
:
exec
(
code
,
user_context
,
user_context
)
except
Exception
as
e
:
error_message
=
getErrorMessageForException
(
self
,
e
,
notebook_context
)
# Abort the current transaction. As a consequence, the notebook lines
# are not added if an exception occurs.
transaction
.
abort
()
return
mergeTracebackListIntoResultDict
(
getErrorMessageForException
(
self
,
e
,
notebook_context
),
log
(
error_message
)
return
mergeTracebackListIntoResultDict
(
error_message
,
setup_error_return_dict_list
)
# Execute the interactive nodes with 'single' mode
...
...
@@ -456,32 +506,36 @@ def Base_runJupyterCode(self, jupyter_code, old_notebook_context):
volatile_variable_list
=
current_setup_dict
.
keys
()
+
inject_variable_dict
.
keys
()
+
user_context
.
get
(
'_volatile_variable_list'
,
[])
volatile_variable_list
.
append
(
'__builtins__'
)
for
key
,
val
in
user_context
.
items
():
if
not
key
in
globals_dict
.
keys
()
and
not
isinstance
(
val
,
ModuleType
)
and
not
key
in
volatile_variable_list
:
if
canSerialize
(
val
):
notebook_context
[
'variables'
][
key
]
=
val
else
:
del
user_context
[
key
]
message
=
(
"Cannot serialize the variable named %s whose value is %s, "
"thus it will not be stored in the context. "
"You should move it's definition to a function and "
"use the environment object to load it.
\
n
"
)
%
(
key
,
val
)
inject_variable_dict
[
'_print'
].
write
(
message
)
# Klaus
#for key, val in user_context.items():
# if not key in globals_dict.keys() and not isinstance(val, well_known_unserializable_type_tuple) and not key in volatile_variable_list:
# if canSerialize(val):
# notebook_context['variables'][key] = val
# else:
# del user_context[key]
# message = (
# "Cannot serialize the variable named %s whose value is %s, "
# "thus it will not be stored in the context. "
# "You should move it's definition to a function and "
# "use the environment object to load it.\n"
# ) % (key, val)
# inject_variable_dict['_print'].write(message)
# Deleting from the variable storage the keys that are not in the user
# context anymore (i.e., variables that are deleted by the user).
for
key
in
notebook_context
[
'variables'
].
keys
():
if
not
key
in
user_context
:
del
notebook_context
[
'variables'
][
key
]
#
for key in notebook_context['variables'].keys():
#
if not key in user_context:
#
del notebook_context['variables'][key]
if
inject_variable_dict
.
get
(
'_print'
)
is
not
None
:
output
=
inject_variable_dict
[
'_print'
].
getCapturedOutputString
()
displayhook_result
=
{
"data"
:{},
"metadata"
:{}}
if
displayhook
.
result
is
not
None
:
displayhook_result
[
"data"
][
"text/plain"
]
=
displayhook
.
result
if
isinstance
(
displayhook
.
result
,
str
):
displayhook_result
[
"data"
][
"text/plain"
]
=
displayhook
.
result
elif
isinstance
(
displayhook
.
result
,
dict
):
displayhook_result
=
displayhook
.
result
result
=
{
'result_string'
:
output
,
'print_result'
:
{
"data"
:{
"text/plain"
:
output
},
"metadata"
:{}},
...
...
@@ -506,7 +560,7 @@ class EnvironmentDefinitionError(TypeError):
def
canSerialize
(
obj
):
container_type_tuple
=
(
list
,
tuple
,
dict
,
set
,
frozenset
)
# if object is a container, we need to check its elements for presence of
# objects that cannot be put inside the zodb
if
isinstance
(
obj
,
container_type_tuple
):
...
...
@@ -524,7 +578,11 @@ def canSerialize(obj):
# Need to unwrap the variable, otherwise we get a TypeError, because
# objects cannot be pickled while inside an acquisition wrapper.
unwrapped_obj
=
Acquisition
.
aq_base
(
obj
)
writer
=
ObjectWriter
(
unwrapped_obj
)
try
:
writer
=
ObjectWriter
(
unwrapped_obj
)
except
:
# Ignore any exceptions, otherwise Jupyter becomes permanent unusble state.
return
False
for
obj
in
writer
:
try
:
writer
.
serialize
(
obj
)
...
...
@@ -727,6 +785,7 @@ class ImportFixer(ast.NodeTransformer):
def
__init__
(
self
):
self
.
import_func_dict
=
{}
self
.
warning_module_names
=
[]
def
visit_FunctionDef
(
self
,
node
):
"""
...
...
@@ -835,24 +894,26 @@ class ImportFixer(ast.NodeTransformer):
if
not
self
.
import_func_dict
.
get
(
name
):
final_module_names
.
append
(
name
)
log
(
"module_names[0]: "
+
module_names
[
0
])
log
(
"result_name: "
+
result_name
)
if
final_module_names
:
# try to import module before it is added to environment
# this way if user tries to import non existent module Exception
# is immediately raised and doesn't block next Jupyter cell execution
exec
(
test_import_string
)
empty_function
=
self
.
newEmptyFunction
(
"%s_setup"
%
result_name
)
dotless_result_name
=
""
for
character
in
result_name
:
if
character
==
'.'
:
dotless_result_name
=
dotless_result_name
+
'_dot_'
else
:
dotless_result_name
=
dotless_result_name
+
character
empty_function
=
self
.
newEmptyFunction
(
"%s_setup"
%
dotless_result_name
)
return_dict
=
self
.
newReturnDict
(
final_module_names
)
log
(
return_dict
)
empty_function
.
body
=
[
node
,
return_dict
]
environment_set
=
self
.
newEnvironmentSetCall
(
"%s_setup"
%
result_name
)
warning
=
self
.
newImportWarningCall
(
root_module_name
,
result_name
)
return
[
empty_function
,
environment_set
,
warning
]
environment_set
=
self
.
newEnvironmentSetCall
(
"%s_setup"
%
dotless_
result_name
)
self
.
newImportWarningCall
(
root_module_name
,
dotless_
result_name
)
return
[
empty_function
,
environment_set
]
else
:
return
node
...
...
@@ -872,7 +933,11 @@ class ImportFixer(ast.NodeTransformer):
"""
return_dict
=
"return {"
for
name
in
module_names
:
return_dict
=
return_dict
+
"'%s': %s, "
%
(
name
,
name
)
if
name
.
find
(
'.'
)
!=
-
1
:
base_name
=
name
[:
name
.
find
(
'.'
)]
else
:
base_name
=
name
return_dict
=
return_dict
+
"'%s': %s, "
%
(
base_name
,
base_name
)
return_dict
=
return_dict
+
'}'
return
ast
.
parse
(
return_dict
).
body
[
0
]
...
...
@@ -887,18 +952,10 @@ class ImportFixer(ast.NodeTransformer):
def
newImportWarningCall
(
self
,
module_name
,
function_name
):
"""
Return an AST.Expr representanting a print statement with a warning to an
user about the import of a module named `module_name` and instructs him
on how to fix it.
Adds a new module to the warning to the user about the importing of new
modules.
"""
warning
=
(
"print '"
"WARNING: Your imported from the module %s without "
"using the environment object, which is not recomended. "
"Your import was automatically converted to use such method."
"The setup function was named as: %s_setup.
\
\
n"
"'"
)
%
(
module_name
,
function_name
)
tree
=
ast
.
parse
(
warning
)
return
tree
.
body
[
0
]
self
.
warning_module_names
.
append
(
module_name
)
def
renderAsHtml
(
self
,
renderable_object
):
...
...
@@ -947,11 +1004,12 @@ def getErrorMessageForException(self, exception, notebook_context):
'traceback'
:
traceback_text
}
def
createNotebookContext
(
self
):
def
createNotebookContext
(
self
,
parameter_dict
=
{}
):
"""
Function to create an empty notebook context.
"""
return
{
'variables'
:
{},
'setup'
:
{}}
nbc
=
{
'variables'
:
{},
'setup'
:
{},
'parameter_dict'
:
parameter_dict
}
return
nbc
class
ObjectProcessor
(
object
):
'''
...
...
@@ -1148,3 +1206,12 @@ def erp5PivotTableUI(self, df):
iframe_host
=
self
.
REQUEST
[
'HTTP_X_FORWARDED_HOST'
].
split
(
','
)[
0
]
url
=
"https://%s/erp5/Base_displayPivotTableFrame?key=%s"
%
(
iframe_host
,
key
)
return
IFrame
(
src
=
url
,
width
=
'100%'
,
height
=
'500'
)
def
Base_checkExistingReference
(
self
,
reference
):
existing_notebook
=
self
.
portal_catalog
.
getResultValue
(
owner
=
self
.
portal_membership
.
getAuthenticatedMember
().
getUserName
(),
portal_type
=
'Data Notebook'
,
reference
=
reference
)
if
not
existing_notebook
is
None
:
return
True
return
False
\ No newline at end of file
bt5/erp5_data_notebook/ExtensionTemplateItem/portal_components/extension.erp5.JupyterCompile.xml
View file @
eb896b21
...
...
@@ -46,13 +46,15 @@
<key>
<string>
text_content_warning_message
</string>
</key>
<value>
<tuple>
<string>
W:312, 10: Use of exec (exec-used)
</string>
<string>
W:355, 10: Use of exec (exec-used)
</string>
<string>
W:368, 10: Use of exec (exec-used)
</string>
<string>
W:453, 6: No exception type(s) specified (bare-except)
</string>
<string>
W:706, 6: Use of exec (exec-used)
</string>
<string>
W:932, 2: Redefining name \'IFrame\' from outer scope (line 4) (redefined-outer-name)
</string>
<string>
W: 18, 0: Unused log imported from Products.ERP5Type.Log (unused-import)
</string>
<string>
W:432, 10: Use of exec (exec-used)
</string>
<string>
W:478, 10: Use of exec (exec-used)
</string>
<string>
W:493, 10: Use of exec (exec-used)
</string>
<string>
W:286, 2: Unused variable \'globals_dict\' (unused-variable)
</string>
<string>
W:583, 4: No exception type(s) specified (bare-except)
</string>
<string>
W:591, 6: No exception type(s) specified (bare-except)
</string>
<string>
W:901, 6: Use of exec (exec-used)
</string>
<string>
W:1007, 0: Dangerous default value {} as argument (dangerous-default-value)
</string>
<string>
W:1138, 2: Redefining name \'IFrame\' from outer scope (line 4) (redefined-outer-name)
</string>
</tuple>
</value>
</item>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment