Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-17 Thread Martin Aspeli
Mikko Ohtamaa wrote:
 This isn't right: a z3c.form form is just a view like any other. It is
 looked up on the context and the request. In Dexterity (which I assume
 you're using?), that's going to be the content object.

 Sorry, I think I mixed with zope.formlib. In any case I hope to find
 the answer to the orignal question.

 The original pattern which I copied is is here:
 http://code.google.com/p/getpaid/source/browse/Products.PloneGetPaid/trunk/Products/PloneGetPaid/preferences.py

That code is insane. Really insane. It's generating classes and doing 
custom adapter factories that inject a _v_ variable into a persistent 
object temporarily. I wouldn't try to copy it at all. I would run away.

I *think* what may be happening here is that most of the vocabularies in 
plone.app.vocabularies have code like this:

context = getattr(context, 'context', context)

This hack is there to support the IAdding view use case where the 
context of the add form is another view and the first real content 
object from which you can acquire stuff is self.context.context. We're 
moving away from that in favour of not acquiring tools and not using 
views-on-views, which are evil anyway. Hence, the 'context' property in 
StoreSettings, which I presume is used as the context for a form, is 
being cajoled into being the Plone site root. But that's using a hack to 
trick a hack into doing different hackery.

This is getting far off topic for the ZODB list, but if you can explain 
what problem you're actually trying to solve, I'm sure we can come up 
with a suggestion that is less of an architecture spacewalk.

When you start having to ask the ZODB list, maybe it's time to think 
whether you could find a simpler solution. :-)

 I don't think I am doing it incorrectly. The other reason of using
 this pattern is that the behavior factory does not need to have a
 write or (an extra) object load if the behavior is never set on the
 context object. The default object returned by factory acts similar
 than the persistent object and I can set the behavior defaults using
 zope.schema - I don't need to do if not set logic anywhere else in
 my code.

There is no reason you can't get this don't-write-unless-necessary 
behaviour in a much more sane way. In fact, it ought to be the default. 
To help you, I'd need a more comprehensive understanding of what you're 
doing and why, though.

Martin

-- 
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book

___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev


Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-17 Thread Mikko Ohtamaa
 That code is insane. Really insane. It's generating classes and doing
 custom adapter factories that inject a _v_ variable into a persistent
 object temporarily. I wouldn't try to copy it at all. I would run away.

But I thought this was *the* Zope way and the brightest minds in the
community have obviously written the code :)

In any case, the persistent + property issue has received some
awareness until it bits someone's ass again. I am going back to the
drawing board and my behaviors.

property set = always starts transaction even though nothing is being written

-Mikko
___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev


Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-17 Thread Jim Fulton
On Wed, Dec 16, 2009 at 7:12 PM, Mikko Ohtamaa mi...@redinnovation.com wrote:
 I need to have little clarification should properties work on
 Persistent objects.

Properties work with persistent objects with one limitation that I'm aware of.
The __setattribute__ method used by the persistent base class assumes
that any attribute assignment modifies the object's persistent state.
A property
that doesn't modify state or that modifies volatile state will fool
it. Conceivably,
one could override __setattribute__ to handle such properties, but doing so
would require great care. I don't recommend it.

Jim

-- 
Jim Fulton
___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev


Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-17 Thread Mikko Ohtamaa

 Properties work with persistent objects with one limitation that I'm aware of.
 The __setattribute__ method used by the persistent base class assumes
 that any attribute assignment modifies the object's persistent state.
 A property
 that doesn't modify state or that modifies volatile state will fool
 it. Conceivably,
 one could override __setattribute__ to handle such properties, but doing so
 would require great care. I don't recommend it.

Great, thanks! I already assumed so, but now this little feature is
officially confirmed.

-Mikko
___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev


[ZODB-Dev] Python properties on Persistent objects

2009-12-16 Thread Mikko Ohtamaa
Hi,

I need to have little clarification should properties work on
Persistent objects. I am running ZODB 3.8.4 on Plone 3.3.

I am using plone.behavior and adapters to retrofit objects with a new
behavior (HeaderBehavior object). This object is also editable through
z3c.form interface. z3c.form requires a context variable on the object
e.g. to look up dynamic vocabularies. To avoid having this
object.context attribute to be peristent (as it's known every time by
the factory method of the adapter which creates/look-ups
HeaderBehavior) I tried to spoof context variable using properties and
internal volatile variable. This was a trick I learnt somewhere
(getpaid.core?)

It didn't work. Looks like Persistent class is not aware of properties
and interprets property set as a transaction start. So even though I
thought I was having a volatile context, my Undo log kept having new
write entries on every page read for pages having HeaderBehavior
objects in their annotations. Data.fs grew steadily with small writes
and there was a whole bunch of ConflictErrors.

I think I found a workaround. By overriding __setattr__ and not
letting through properties and volatile attribute set to Persistent
__setattr__ you can avoid the problem.

Is my rationale correct? Should Persistent behave well with
properties? I found only one discussion in Google, circa 2004.

Sample code below:

from persistent import Persistent
class VolatileContext(Persistent):
 Mix-in class to provide non-persistent context attribute to
persistent classes.

Some subsystems (e.g. z3c.forms) expect objects to have a context
reference to parent/site/whatever.
However, storing this back-reference persistenly is not needed, as
the factory
method will always know the context.

This helper class creates a context property which is volatile
(never persistent),
but can be still set on the object after creation or after database load.


zope.interface.implements(IVolatileContext)

def _set_context(self, context):
self._v_context = context

def _get_context(self):
return self._v_context

def _set_factory(self, factory):
self._v_factory = factory

def _get_factory(self):
return self._v_factory

# http://docs.python.org/library/functions.html#property
context = property(_get_context, _set_context)

factory = property(_get_factory, _set_factory)

def save(self):
 
self.factory.makePersistent(self)

def __setattr__(self, name, value):
if name not in (context, factory, _v_context, _v_factory):
Persistent.__setattr__(self, name, value)
print leaking  + name +   + str(value)
else:
print property set  + name +   + str(value)
object.__setattr__(self, name, value)



class AnnotationPersistentFactory(object):
 A factory pattern to manufacture persistent objects stored
within the parent object annotations.

Until the first write, the default (non-persistent) object is
return. This prevents
possible situations where database read could cause write.

The first write must call
AnnotationPersistentFactory.makePersistent(object).
Alternative, you can call AnnotationPersistentFactory.makePersistent(object)
when entering the editing interface for the first time.

After the first write, the saved persistent object is return.



def __init__(self, persistent_class, key):

@param persistent_class: Class reference / factory method
which will create new objects.
Created classes must conform VolatileContext interface

@param key: ASCII string, Key name used with IAnnotations

self.persistent_class = persistent_class
self.key = key
self._assertProperlySetUp()

def _assertProperlySetUp(self):

Check that the framework is properly set up

assert callable(self.persistent_class), Factory is missing

assert hasattr(self.persistent_class, context), The
persistent object must support volatile context interface

assert self.key is not None, You must give the annotations key

def makePersistent(self, object):
 Write created persistent object to the database.

This will store the object on the annotations of its context.

assert isinstance(object, self.persistent_class), Object %s
was not type of %s % (str(object), str(self.persistent_class))
annotations = IAnnotations(object.context)
annotations[self.key] = object

def __call__(self, context):
 Called by Zope framework when doing a factory call.

Usually this class is refered as adapter factory= and
this method creates a new, read-only, persistent object.


annotations = IAnnotations(context)

if not self.key in annotations:
# Construct a new (default) instance
print Created

Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-16 Thread Laurence Rowe
2009/12/17 Mikko Ohtamaa mi...@redinnovation.com:
 Hi,

 I need to have little clarification should properties work on
 Persistent objects. I am running ZODB 3.8.4 on Plone 3.3.

 I am using plone.behavior and adapters to retrofit objects with a new
 behavior (HeaderBehavior object). This object is also editable through
 z3c.form interface. z3c.form requires a context variable on the object
 e.g. to look up dynamic vocabularies. To avoid having this
 object.context attribute to be peristent (as it's known every time by
 the factory method of the adapter which creates/look-ups
 HeaderBehavior) I tried to spoof context variable using properties and
 internal volatile variable. This was a trick I learnt somewhere
 (getpaid.core?)

This sounds like you are passing context somewhere where view is expected.

Laurence
___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev


Re: [ZODB-Dev] Python properties on Persistent objects

2009-12-16 Thread Martin Aspeli
Laurence Rowe wrote:
 2009/12/17 Mikko Ohtamaami...@redinnovation.com:
 Hi,

 I need to have little clarification should properties work on
 Persistent objects. I am running ZODB 3.8.4 on Plone 3.3.

 I am using plone.behavior and adapters to retrofit objects with a new
 behavior (HeaderBehavior object). This object is also editable through
 z3c.form interface. z3c.form requires a context variable on the object
 e.g. to look up dynamic vocabularies. To avoid having this
 object.context attribute to be peristent (as it's known every time by
 the factory method of the adapter which creates/look-ups
 HeaderBehavior) I tried to spoof context variable using properties and
 internal volatile variable. This was a trick I learnt somewhere
 (getpaid.core?)

 This sounds like you are passing context somewhere where view is expected.

Yeah, to me as well it sounds like you're barking up the wrong tree 
here. You shouldn't need any kind of faking like this.

z3c.form requires a context variable on the object
e.g. to look up dynamic vocabularies

This isn't right: a z3c.form form is just a view like any other. It is 
looked up on the context and the request. In Dexterity (which I assume 
you're using?), that's going to be the content object.

I think you need to take a step back and look at how your form is set 
up, not dive into deep magic of persistence.

Martin

-- 
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book

___
For more information about ZODB, see the ZODB Wiki:
http://www.zope.org/Wikis/ZODB/

ZODB-Dev mailing list  -  ZODB-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zodb-dev