Commit 8c87524d authored by Godefroid Chapelle's avatar Godefroid Chapelle

LP #143531: Fix broken object so they give access to their state.

merge branch gotcha-LP143531
parent 2752d75b
...@@ -11,6 +11,8 @@ http://docs.zope.org/zope2/releases/. ...@@ -11,6 +11,8 @@ http://docs.zope.org/zope2/releases/.
Bugs Fixed Bugs Fixed
++++++++++ ++++++++++
- LP #143531: Fix broken object so they give access to their state.
- LP #578326: Issue a warning if someone specifies a non-public permission - LP #578326: Issue a warning if someone specifies a non-public permission
attribute in the browser:view directive. This attribute has never been attribute in the browser:view directive. This attribute has never been
supported in Zope 2. supported in Zope 2.
......
...@@ -22,12 +22,14 @@ from Acquisition import Explicit ...@@ -22,12 +22,14 @@ from Acquisition import Explicit
from App.special_dtml import DTMLFile from App.special_dtml import DTMLFile
from OFS.SimpleItem import Item from OFS.SimpleItem import Item
from Persistence import Overridable from Persistence import Overridable
from ZODB.broken import Broken as ZODB_Broken
from ZODB.broken import persistentBroken
broken_klasses={} broken_klasses={}
broken_klasses_lock = allocate_lock() broken_klasses_lock = allocate_lock()
LOG = getLogger('OFS.Uninstalled') LOG = getLogger('OFS.Uninstalled')
class BrokenClass(Explicit, Item, Overridable): class BrokenClass(ZODB_Broken, Explicit, Item, Overridable):
_p_changed=0 _p_changed=0
meta_type='Broken Because Product is Gone' meta_type='Broken Because Product is Gone'
icon='p_/broken' icon='p_/broken'
...@@ -37,12 +39,6 @@ class BrokenClass(Explicit, Item, Overridable): ...@@ -37,12 +39,6 @@ class BrokenClass(Explicit, Item, Overridable):
manage_page_header = Acquired manage_page_header = Acquired
manage_page_footer = Acquired manage_page_footer = Acquired
def __getstate__(self):
raise SystemError, (
"""This object was originally created by a product that
is no longer installed. It cannot be updated.
(%s)""" % repr(self))
def __getattr__(self, name): def __getattr__(self, name):
if name[:3]=='_p_': if name[:3]=='_p_':
return BrokenClass.inheritedAttribute('__getattr__')(self, name) return BrokenClass.inheritedAttribute('__getattr__')(self, name)
...@@ -74,6 +70,7 @@ def Broken(self, oid, pair): ...@@ -74,6 +70,7 @@ def Broken(self, oid, pair):
klass.info=( klass.info=(
'This object\'s class was %s in module %s.' % 'This object\'s class was %s in module %s.' %
(klass.__name__, klass.__module__)) (klass.__name__, klass.__module__))
klass = persistentBroken(klass)
LOG.warning('Could not import class %s ' LOG.warning('Could not import class %s '
'from module %s' % (`klass.__name__`, `klass.__module__`)) 'from module %s' % (`klass.__name__`, `klass.__module__`))
finally: finally:
......
...@@ -13,6 +13,13 @@ ...@@ -13,6 +13,13 @@
############################################################################## ##############################################################################
import unittest import unittest
from OFS.SimpleItem import SimpleItem
from Testing.ZopeTestCase import base
class ToBreak(SimpleItem):
pass
class TestsOfBroken(unittest.TestCase): class TestsOfBroken(unittest.TestCase):
"""Tests for the factory for "broken" classes. """Tests for the factory for "broken" classes.
...@@ -77,24 +84,8 @@ class TestsOfBroken(unittest.TestCase): ...@@ -77,24 +84,8 @@ class TestsOfBroken(unittest.TestCase):
self.assertEqual(klass.__module__, 'Products.MyProduct.MyClass') self.assertEqual(klass.__module__, 'Products.MyProduct.MyClass')
self.assertEqual(klass.product_name, 'MyProduct') self.assertEqual(klass.product_name, 'MyProduct')
def test_Broken_instance___getstate___raises_useful_exception(self):
# see http://www.zope.org/Collectors/Zope/2157
from OFS.Uninstalled import Broken
from OFS.Uninstalled import BrokenClass
OID = '\x01' * 8
inst = Broken(self, OID, ('Products.MyProduct.MyClass', 'MyClass'))
try:
dict = inst.__getstate__()
except SystemError, e:
self.failUnless('MyClass' in str(e), str(e))
else:
self.fail("'__getstate__' didn't raise SystemError!")
def test_Broken_instance___getattr___allows_persistence_attrs(self): def test_Broken_instance___getattr___allows_persistence_attrs(self):
from OFS.Uninstalled import Broken from OFS.Uninstalled import Broken
from OFS.Uninstalled import BrokenClass
OID = '\x01' * 8 OID = '\x01' * 8
PERSISTENCE_ATTRS = ["_p_changed", PERSISTENCE_ATTRS = ["_p_changed",
"_p_jar", "_p_jar",
...@@ -119,14 +110,47 @@ class TestsOfBroken(unittest.TestCase): ...@@ -119,14 +110,47 @@ class TestsOfBroken(unittest.TestCase):
for meth_name in PERSISTENCE_METHODS: for meth_name in PERSISTENCE_METHODS:
meth = getattr(inst, meth_name) # doesn't raise meth = getattr(inst, meth_name) # doesn't raise
class TestsIntegratedBroken(base.TestCase):
def test_Broken_instance___getstate___gives_access_to_its_state(self):
from Acquisition import aq_base
from OFS.Uninstalled import BrokenClass
from OFS.tests import test_Uninstalled
import transaction
# store an instance
tr = ToBreak()
tr.id = 'tr'
self.app._setObject('tr', tr)
# commit to allow access in another connection
transaction.commit()
# remove class from namespace to ensure broken object
del test_Uninstalled.ToBreak
# get new connection that will give access to broken object
app = base.app()
inst = aq_base(app.tr)
self.failUnless(isinstance(inst, BrokenClass))
state = inst.__getstate__()
self.assertEqual(state, {'id': 'tr'})
# cleanup
app.manage_delObjects('tr')
transaction.commit()
# check that object is not left over
app = base.app()
self.failIf('tr' in app.objectIds())
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite(TestsOfBroken)) suite.addTest(unittest.makeSuite(TestsOfBroken))
suite.addTest(unittest.makeSuite(TestsIntegratedBroken))
return suite return suite
def main(): def main():
unittest.main(defaultTest='test_suite') unittest.main(defaultTest='test_suite')
if __name__ == '__main__': if __name__ == '__main__':
main() main()
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