Commit 20f0f6dc authored by Jérome Perrin's avatar Jérome Perrin

ERP5Type: allow pickling temp objects

What should not be allowed is to store persistent objects in ZODB, but pickling
them should be OK. This is necessary if we want to store them in memcached.

This also modifies loadTempPortalTypeClass which was returning a class with a
different name than requested, by prepending "Temporary ". This prevented
pickling, because pickle checks that the class is can be imported under its
same name. This was problematic because before this change, we had such a behavior:

    (Pdb) tmp = self.portal.person_module.newContent(portal_type='Person', temp_object=True)
    (Pdb) tmp.__class__
    <class 'erp5.temp_portal_type.Temporary Person'>
    (Pdb) import erp5.temp_portal_type
    (Pdb) getattr(erp5.temp_portal_type, 'Temporary Person')
    <class 'erp5.temp_portal_type.Temporary Temporary Person'>

My understanding is that this prefix in the class name was not useful, that
class lives in another module than the "real" Person class.
parent 2de7f10d
...@@ -383,9 +383,8 @@ def loadTempPortalTypeClass(portal_type_name): ...@@ -383,9 +383,8 @@ def loadTempPortalTypeClass(portal_type_name):
""" """
import erp5.portal_type import erp5.portal_type
klass = getattr(erp5.portal_type, portal_type_name) klass = getattr(erp5.portal_type, portal_type_name)
return type(portal_type_name, (TemporaryDocumentMixin, klass), {})
return type("Temporary " + portal_type_name,
(TemporaryDocumentMixin, klass), {})
last_sync = -1 last_sync = -1
_bootstrapped = set() _bootstrapped = set()
......
...@@ -35,6 +35,7 @@ from Acquisition import aq_base ...@@ -35,6 +35,7 @@ from Acquisition import aq_base
from Products.ERP5Type.Accessor.Constant import PropertyGetter as \ from Products.ERP5Type.Accessor.Constant import PropertyGetter as \
PropertyConstantGetter PropertyConstantGetter
class TemporaryDocumentMixin(object): class TemporaryDocumentMixin(object):
""" """
Setters and attributes that are attached to temporary documents. Setters and attributes that are attached to temporary documents.
...@@ -44,8 +45,10 @@ class TemporaryDocumentMixin(object): ...@@ -44,8 +45,10 @@ class TemporaryDocumentMixin(object):
__roles__ = None __roles__ = None
def __getstate__(self): def __getstate__(self):
if getattr(self, '_p_jar', None) is not None:
# disallow persistent storage # disallow persistent storage
raise PicklingError("Temporary objects can't be pickled") raise PicklingError("Temporary objects can't be pickled")
return super(TemporaryDocumentMixin, self).__getstate__()
def reindexObject(self, *args, **kw): def reindexObject(self, *args, **kw):
pass pass
......
...@@ -31,8 +31,10 @@ other Tests are in erp5_core_test:testERP5Type) which is deprecated in favor ...@@ -31,8 +31,10 @@ other Tests are in erp5_core_test:testERP5Type) which is deprecated in favor
of Portal Type as Classes and ZODB Components of Portal Type as Classes and ZODB Components
""" """
import pickle
import unittest import unittest
import warnings import warnings
from Acquisition import aq_base
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from AccessControl.ZopeGuards import guarded_import from AccessControl.ZopeGuards import guarded_import
from Products.ERP5Type.tests.utils import LogInterceptor from Products.ERP5Type.tests.utils import LogInterceptor
...@@ -244,6 +246,19 @@ class TestERP5Type(ERP5TypeTestCase, LogInterceptor): ...@@ -244,6 +246,19 @@ class TestERP5Type(ERP5TypeTestCase, LogInterceptor):
self.assertTrue(o.isTempObject()) self.assertTrue(o.isTempObject())
self.assertTrue(guarded_import("Products.ERP5Type.Document", fromlist=["newTempBase"])) self.assertTrue(guarded_import("Products.ERP5Type.Document", fromlist=["newTempBase"]))
def test_TempObjectPersistent(self):
# Temp objects can not be stored in ZODB
temp_object = self.portal.person_module.newContent(portal_type='Person', temp_object=True)
self.assertTrue(temp_object.isTempObject())
# they can be pickled
self.assertTrue(pickle.dumps(aq_base(temp_object)))
# but they can not be saved in ZODB accidentally
self.portal.person_module.oops = temp_object
self.assertRaisesRegexp(Exception, "Temporary objects can't be pickled", self.commit)
self.abort()
def test_warnings_redirected_to_event_log(self): def test_warnings_redirected_to_event_log(self):
self._catch_log_errors() self._catch_log_errors()
self.addCleanup(self._ignore_log_errors) self.addCleanup(self._ignore_log_errors)
......
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