[z3-five] Does Five support Local Utilities?

Philipp von Weitershausen philipp at weitershausen.de
Tue Sep 19 12:01:13 CEST 2006


Chris Withers wrote:
> There are true global utilities that require no configuration that 
> implement IOutput, such as the default output renderer which takes the 
> Twiddler DOM and turns it into a lump of text:
> 
> from twiddler.interfaces import IOutput
> from *somewhere* import _render
> def Default(root,*args,**kw):
>     output = []
>     _render(output, root._root, {})
>     return u''.join(output)
> directlyProvides(Default,IOutput)
> 
> This is easy enough to expose through the following zcml:
> 
> <utility provides="twiddler.interfaces.IOutput"
>          component="twiddler.output.default.Default"
>          name="Default"/>

I see.

> However, I was hoping to use something like utilities for objects that 
> implement the same interface but which require configuration.

Right.

> For example, Twiddler has an output renderer that returns an 
> email.MIMEMultiPart-ish object which also has a send method:
> 
> class Email:
> 
>     implements(IOutput)
> 
>     def __init__(self,
>                  smtp_host='localhost',
>                  smtp_port='25',
>                  mfrom=None,
>                  mto=None,...etc...)
>         # do stuff
>         pass
> 
> 
>     def __call__(self,root,*args,**kw):
>         # gubbinz goes here
>         return MTMultipart(self,
>                            values['mfrom'],
>                            values['mto'],
>                            **multipart_kw)

(Interlude:

Note that Zope 3 has a decent mail framework that's transaction aware 
and all that, so while you might want to create the mail message 
yourself, you probably want to use zope.sendmail [zope.app.mail 
previously] for sending it out. Plus, it feels weird to do the mail 
sending in an twiddler output renderer, but I don't know the framework...
)

> As you can see, this has configuration that is likely to be specific to 
> the individual Twiddler: who the email is to, what smtp server to use, 
> etc. Of course, you *might* want to share this between several twiddlers 
> if, for example, you're sending several different mails but that all 
> share the same From address and use the same smtp server.
> 
> I currently have this registered using the following zcml:
> 
> <utility provides="twiddler.interfaces.IOutput"
>          factory="twiddler.output.emailer.Email"
>          name="Emailer"/>
> 
> ...which, given what you've told me previously, is why I have the 
> deepcopy in the Twiddler.setInput pseudocode above. The logic is roughly 
> "if the input/output/whatever if configurable, then copy it and store 
> the copy, otherwise just store a reference to the global utility"...
> 
> Is that evil?

At least storing the twiddler tree in the global utility seems weird to 
me. I *think* you could express all this better through adapters. Let's 
take the first example:

   class DefaultRenderer(object):
       adapts(ITwiddler)
       implements(IOutput)

       def __init__(self, context):
           self.context = context

       def __call__(self, *args, **kw):
          root = self.context
          return u''.join(_render(output, root._root, {}))

The mail rendering could be expressed with a different adapter:

   class MailRenderer(DefaultRenderer):
       adapts(IMailTwiddler)

       smtp_host = 'localhost'
       smtp_port = 25
       mfrom = None
       mto = None

       def __call__(self, *args, **kw):
          return MTMultipart(self,
                             values['mfrom'],
                             values['mto'],
                             **multipart_kw).to_string()

Now you would just register different incarnations of this mail renderer 
(with different configuration values) for your various twiddlers. The 
only downside is that you need marker interfaces to express this 
properly. Or you use named adapters.

Philipp


More information about the z3-five mailing list