[Zope-dev] z3c.form - ComputedErrorViewMessage and crazy adapter behaviour

2010-03-09 Thread Martin Aspeli
Hi,

I posted about this earlier, but the message seems to have gotten lost 
in the ether.

I am trying and struggling to use ComputedErrorViewMessage. So far, I've 
discovered two problems, one which affects IValue adapters in general.

1. The last discriminator of a ComputedErrorViewMessage (and an 
ErrorViewMessage) is listed as 'content':

ErrorViewMessage = value.StaticValueCreator(
discriminators = ('error', 'request', 'widget', 'field', 'form', 
'content')
)

ComputedErrorViewMessage = value.ComputedValueCreator(
discriminators = ('error', 'request', 'widget', 'field', 'form', 
'content')
)

However, the adapter lookup further down in error.py does:

class ErrorViewSnippet(object):

 ...

 def update(self):
 value = zope.component.queryMultiAdapter(
 (self.context, self.request, self.widget,
  self.field, self.form, self),
 interfaces.IValue, name='message')
 if value is not None:
 self.message = value.get()
 else:
 self.message = self.createMessage()

Notice how the last parameter is 'self', i.e. the ErrorViewSnippet. To 
me, at least, 'content' suggests this refers to the value returned by 
form.getContent(), most likely form.context.

There's no test for this discriminator that I can see, so I'm not sure 
whether the bug is that 'content' is a poor name, or if the last part 
of that tuple should be self.form.getContent() or self.form.context?

2. The order in which we use ErrorViewMessage or 
ComputedErrorViewMessage seems to make a difference to its ability to 
specialise. I really can't figure out what the bug is, though. I've 
captured and annotated an interpreter session to illustrate the problem:

First, some imports:

  from zope.interface import Interface
  from zope.component import provideAdapter

  from zope import schema
  from zope.schema.interfaces import TooSmall

  from z3c.form import form, error, field
  from z3c.form.testing import setupFormDefaults, TestRequest

Set up a test environment with two forms.

  setupFormDefaults()

  class DummyContext(object):
... pass

  context = DummyContext()

  class ISchema(Interface):
... field1 = schema.Int(min=10)

  class Form1(form.Form):
... fields = field.Fields(ISchema)
... ignoreContext = True

  class Form2(form.Form):
... fields = field.Fields(ISchema)
... ignoreContext = True

Define two error message value adapters. Note that message2 is a
specialisation of message1, that also takes the form into account. Also
note that message1 (the more general) happens to be created first.

  message1 = error.ErrorViewMessage(uMessage 1, error=TooSmall, 
field=ISchema['field1'])
  message2 = error.ErrorViewMessage(uMessage 2, error=TooSmall, 
field=ISchema['field1'], form=Form2)

Now register these adapters (the order doesn't matter)

  provideAdapter(message1, name=umessage)
  provideAdapter(message2, name=umessage)

Let's now create an instance of Form1 and try to validate it.

  form1 = Form1(context, TestRequest(form={'form.widgets.field1': '5'}))
  form1.update()
  data, errors = form1.extractData()
  len(errors)
1
  errors[0].message
u'Message 1'

No surprises there. Now let's try Form2. We'd expect to see message2, since
it is registered for the more specific form:

  form2 = Form2(context, TestRequest(form={'form.widgets.field1': '5'}))
  form2.update()
  data, errors = form2.extractData()
  len(errors)
1
  errors[0].message
u'Message 1'

Ouch. Why is our more specific adapter being ignored?

Recall that the ErrorViewSnippet does the IValue adapter lookup like so:

 value = zope.component.queryMultiAdapter(
 (self.context, self.request, self.widget,
  self.field, self.form, self),
 interfaces.IValue, name='message')

The adapted objects are:

  (errors[0].context, errors[0].request, errors[0].widget, 
errors[0].field, errors[0].form, errors[0])
(TooSmall(5, 10), z3c.form.testing.TestRequest instance 
URL=http://127.0.0.1, TextWidget 'form.widgets.field1', 
zope.schema._bootstrapfields.Int object at 0x1030a1190, 
__main__.Form2 object at 0x1035c0b90, ErrorViewSnippet for TooSmall)

Let's look at the interfaces provided by ISchema['field1']:

  from zope.interface import directlyProvidedBy
  list(directlyProvidedBy(ISchema['field1']).flattened())
[InterfaceClass z3c.form.util.IGeneratedForObject_4345958800, 
InterfaceClass z3c.form.util.IGeneratedForObject_4345958800, 
InterfaceClass zope.interface.Interface]
 

Let's also look at what our adapters are actually adapting:

  message1.__component_adapts__
(class 'zope.schema._bootstrapinterfaces.TooSmall', None, None, 
InterfaceClass z3c.form.util.IGeneratedForObject_4345958800, None, None)
  message2.__component_adapts__
(class 'zope.schema._bootstrapinterfaces.TooSmall', None, None, 
InterfaceClass z3c.form.util.IGeneratedForObject_4345958800, class 
'__main__.Form2', None)

Our adapters are both in the registry:

  from 

[Zope-dev] z3c.form - ComputedErrorViewMessage

2010-03-09 Thread Martin Aspeli
Hi,

I'm struggling to get custom ComputedErrorViewMessage adapters to work 
in z3c.form. I'm still debugging, but I came across this:

In error.py:

   ErrorViewMessage = value.StaticValueCreator(
 discriminators = ('error', 'request', 'widget', 'field', 'form', 
'content')
 )

ComputedErrorViewMessage = value.ComputedValueCreator(
 discriminators = ('error', 'request', 'widget', 'field', 'form', 
'content')
 )


class ErrorViewSnippet(object):

 ...

 def update(self):
 value = zope.component.queryMultiAdapter(
 (self.context, self.request, self.widget,
  self.field, self.form, self),
 interfaces.IValue, name='message')
 if value is not None:
 self.message = value.get()
 else:
 self.message = self.createMessage()

 ...

 From what I can tell, self.context is the exception; self.request, 
self.widget, self.field and self.form are as you'd expect. But the last 
discriminator, 'content' does not match up with 'self' (i.e. the error 
view snippet).

So, which is right? The discriminator on the value adapter factory, or 
the multi-adapter lookup? The documentation mentions 'content' as the 
discriminator, but doesn't define it any further.

FWIW, my expection is that 'content' should be the form's context 
content object.

Martin

___
Zope-Dev maillist  -  Zope-Dev@zope.org
https://mail.zope.org/mailman/listinfo/zope-dev
**  No cross posts or HTML encoding!  **
(Related lists - 
 https://mail.zope.org/mailman/listinfo/zope-announce
 https://mail.zope.org/mailman/listinfo/zope )