Commit a0e79027 authored by Bryton Lacquement's avatar Bryton Lacquement 🚪

Monkey-patch to apply my2to3 fixers

parent 7eed3ba6
...@@ -32,6 +32,8 @@ from copy import copy ...@@ -32,6 +32,8 @@ from copy import copy
import warnings import warnings
import types import types
import thread, threading import thread, threading
import os
from lib2to3.pgen2.parse import ParseError
from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOBTree
from Products.ERP5Type.Globals import InitializeClass, DTMLFile from Products.ERP5Type.Globals import InitializeClass, DTMLFile
...@@ -42,6 +44,7 @@ from AccessControl.SecurityManagement import getSecurityManager ...@@ -42,6 +44,7 @@ from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.ZopeGuards import guarded_getattr from AccessControl.ZopeGuards import guarded_getattr
from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain from Acquisition import aq_base, aq_inner, aq_acquire, aq_chain
from DateTime import DateTime from DateTime import DateTime
from my2to3.trace import apply_fixers
import OFS.History import OFS.History
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from OFS.PropertyManager import PropertyManager from OFS.PropertyManager import PropertyManager
...@@ -855,6 +858,27 @@ class Base( ...@@ -855,6 +858,27 @@ class Base(
self.uid = uid # Else it will be generated when we need it self.uid = uid # Else it will be generated when we need it
self.sid = sid self.sid = sid
def __setstate__(self, state):
if os.environ.get("MY2TO3_ACTION") == "trace" and \
self.getPortalType() in [
"Document Component", "Extension Component", "Interface Component",
"Mixin Component", "Module Component", "Test Component",
"Tool Component",
]:
# See Products.ERP5Type.patches.my2to3_patch
# Apply "trace" fixers on the fly
# Note: The modifications are not saved (unless it is done explicitly,
# e.g. the user saves manually).
text_content = state.get('text_content')
if text_content:
try:
state['text_content'] = apply_fixers(text_content, state['id'])
except ParseError:
# text_content is not valid code
pass
super(Base, self).__setstate__(state)
# XXX This is necessary to override getId which is also defined in SimpleItem. # XXX This is necessary to override getId which is also defined in SimpleItem.
security.declareProtected( Permissions.AccessContentsInformation, 'getId' ) security.declareProtected( Permissions.AccessContentsInformation, 'getId' )
getId = BaseAccessor.Getter('getId', 'id', 'string') getId = BaseAccessor.Getter('getId', 'id', 'string')
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
############################################################################## ##############################################################################
# Load all monkey patches # Load all monkey patches
from Products.ERP5Type.patches import my2to3_patch
from Products.ERP5Type.patches import WSGIPublisher from Products.ERP5Type.patches import WSGIPublisher
from Products.ERP5Type.patches import HTTPRequest from Products.ERP5Type.patches import HTTPRequest
from Products.ERP5Type.patches import AccessControl_patch from Products.ERP5Type.patches import AccessControl_patch
......
import new, os
from Acquisition import aq_parent
from my2to3.trace import apply_fixers, patch_imports, tracing_functions
from Products.PageTemplates.ZRPythonExpr import PythonExpr
from Products.PythonScripts.PythonScript import _marker, PythonScript, PythonScriptTracebackSupplement
from RestrictedPython import compile_restricted_eval
from . import PatchClass
erp5_products_path = os.path.join(os.path.dirname(__file__), '..', '..')
erp5_products = ['Products.' + p for p in os.listdir(erp5_products_path)
if os.path.isdir(os.path.join(erp5_products_path, p))]
# Apply "trace" fixers on the fly
if os.environ.get("MY2TO3_ACTION") == "trace":
def is_whitelisted(fullname, path):
return any(fullname.startswith(p) for p in erp5_products)
# Apply "trace" fixers on the fly, when importing modules
patch_imports(is_whitelisted)
# Apply "trace" fixers on the fly, on PythonScript bodies
# Note: The modifications are not saved (unless it is done explicitly, e.g.
# the user saves manually).
class _(PatchClass(PythonScript)):
___setstate__ = PythonScript.__setstate__
def __setstate__(self, state):
self.___setstate__(state)
if '_body' in state:
# Note that self.___setstate__ is called unconditionally a first time,
# before. The reason is:
# We need the file path (i.e. self.get_filepath()). This information
# is contained in state['_filepath'], but it is not always the case.
# Calling ___setstate__ before makes this information available.
new_body = apply_fixers(state['_body'], self.get_filepath())
if new_body != state['_body']:
state['_body'] = new_body
# This time, it's called to update the body.
self.___setstate__(state)
self._compile()
# Add new "builtins" which the script can access
def _exec(self, bound_names, args, kw):
"""Call a Python Script
Calling a Python Script is an actual function invocation.
"""
# Retrieve the value from the cache.
keyset = None
if self.ZCacheable_isCachingEnabled():
# Prepare a cache key.
keyset = kw.copy()
asgns = self.getBindingAssignments()
name_context = asgns.getAssignedName('name_context', None)
if name_context:
keyset[name_context] = aq_parent(self).getPhysicalPath()
name_subpath = asgns.getAssignedName('name_subpath', None)
if name_subpath:
keyset[name_subpath] = self._getTraverseSubpath()
# Note: perhaps we should cache based on name_ns also.
keyset['*'] = args
result = self.ZCacheable_get(keywords=keyset, default=_marker)
if result is not _marker:
# Got a cached value.
return result
#__traceback_info__ = bound_names, args, kw, self.func_defaults
ft = self._v_ft
if ft is None:
__traceback_supplement__ = (
PythonScriptTracebackSupplement, self)
raise RuntimeError, '%s %s has errors.' % (self.meta_type, self.id)
fcode, g, fadefs = ft
g = g.copy()
if bound_names is not None:
g.update(bound_names)
g['__traceback_supplement__'] = (
PythonScriptTracebackSupplement, self, -1)
g['__file__'] = getattr(self, '_filepath', None) or self.get_filepath()
# <patch>
g.update({f.__name__: f for f in tracing_functions})
# </patch>
f = new.function(fcode, g, None, fadefs)
try:
result = f(*args, **kw)
except SystemExit:
raise ValueError('SystemExit cannot be raised within a PythonScript')
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
return result
# Apply "trace" fixers on the fly, on TALES PythonExpr
class __(PatchClass(PythonExpr)):
def __init__(self, name, expr, engine):
self.text = self.expr = text = expr.strip().replace('\n', ' ')
# Unicode expression are not handled properly by RestrictedPython
# We convert the expression to UTF-8 (ajung)
if isinstance(text, unicode):
text = text.encode('utf-8')
code, err, warn, use = compile_restricted_eval(text,
self.__class__.__name__)
if err:
raise engine.getCompilerError()('Python expression error:\n%s' %
'\n'.join(err))
# <patch>
# `compile_restricted_eval` is called a first time before (the original
# code), mainly to handle errors related to the content of `text`. If it
# succeeds, we know that `text` contains valid code, on which we can
# safely apply fixers.
# XXX: "'PythonExpr:' + text" is used as the name for now. Let's try to
# find something better.
code, err, warn, use = compile_restricted_eval(
apply_fixers(text, 'PythonExpr:' + text),
self.__class__.__name__)
# </patch>
self._varnames = use.keys()
self._code = code
# Add new "builtins" which the script can access
PythonExpr._globals.update({f.__name__: f for f in tracing_functions})
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