[TransWarp] PEAK binding & naming howto

Phillip J. Eby pje at telecommunity.com
Thu Mar 6 18:11:49 EST 2003


At 07:49 PM 3/6/03 +0200, alexander smishlajev wrote:

>are there any recommended readings that may cast some light on the PEAK 
>binding and naming techniques while the tutorial is incomplete?

Not really.  But most of your questions actually seem to relate more to the 
peak.config package, so reading its source (or at least the reference docs 
and interfaces module), and the contents of 'peak.ini' would probably be 
helpful.  While you can create bindings for utilities, properties, etc., 
these are actually looked up by the peak.config package.


>1. suppose i have a (global) service Spam implementing interface ISpam:
>
>     class ISpam(Interface.Interface):
>          """Spam interface"""
>          def eggs():
>              """provide eggs"""
>
>      class Spam(binding.Component):
>           __implements__ = ISpam
>           def eggs(self):
>               return "eggs"
>
>how and when do i instantiate the Spam service object?

Unless it's your top-level application object, or an "element" rather than 
a service, you *don't* instantiate it directly.  You create a binding for 
it, or register it as a utility via a configuration file.  So, for example, 
if you decide that your 'App' class will always include a Spam instance, 
you might do:

class MyApp(binding.Component):

     my_spam = binding.New(Spam, provides=ISpam)

And then, when any object that is a child of your app instance requests 
binding to an ISpam utility, it will cause my_spam to be initialized (if it 
wasn't already) and the binding on the child object will be set from it.

Alternatively, you can do this:

[Provide Utilities]
mypkg.spammod.ISpam =
     config.CachingProvider(
         lambda foundIn: importString('mypkg.spammod.Spam')(foundIn),
         local = True,
     )

in an .ini file.  The disadvantages to this approach are that it's verbose, 
obscure, and inefficient for many situations.  This approach is really best 
reserved for situations where you need to replace a built-in PEAK default 
utility, such as the transaction service.

Hm.  I probably should find a way to express the above idiom more clearly 
in an .ini file, as it seems to pop up for any simple "global service"-type 
components, such as logfiles, transactions, etc.


>how do i register this service so that all components know that eggs may 
>be got from that object?

I actually just answered that.  :)  Any component which is a child of the 
component which declared it to provide that service, will see it as a 
provider, so long as there is not a "closer" provider.  Think Zope-style 
acquisition, only without all the gotchas, or the Zope 2 "placeful utility" 
concept, but with less overhead in the implementation.  PEAK components 
acquire placeful things based on a fixed notion of context: their "parent" 
component.  Zope 3 components have a context that depends on "where you got 
them from", which may vary for the same object and thus requires context 
wrappers.

Of course, the registration techniques I've shown are all "static", at 
least in the sense that they are defined in code and not at 
"run-time".  Classes that subclass 'binding.Component' (as opposed to 
'binding.Base') can also declare providers at runtime, using their 
'registerProvider()' method (see the config.IConfigurable interface, which 
is subclassed by binding.IComponent).


>2. consider application-wide utility Foo:
>
>     class Foo(binding.Component):
>          bar = "bar"
>          def baz(self):
>              return "baz"
>
>is it rational to bind this utility to "/foo"?
>
>other application components may then use the utility like this:
>
>     class FooBar(binding.Component):
>          foo = naming.lookup("/foo")
>
>.. am i right?

naming.lookup() looks something up right away; binding.bindTo() creates a 
descriptor that will do the lookup when the attribute is accessed.  Also, 
bindTo() can look up interfaces and configuration properties as well as 
naming system names, and the simple component path names are actually not 
supported by the naming package; if you want to look up a relative 
component path "right away", you have to use binding.lookupComponent().

Anyway, what you want to write here would be:

class FooBar(binding.Component):
     foo = binding.bindTo("/foo")

However, as I believe I've mentioned before, it is not wise to do this for 
components you might want to reuse, since FooBar will now *insist* that its 
instances be used in a hierarchy where the root component has a 'foo' 
object that implements the Foo behavior.  It might be best to use an 
interface or a property for this instead.


>what bindinding function should be used in class Foo?

Er, whatever you need.  If you're asking me, "how do I make it be found as 
'/foo'?", the answer is to set a root component's "foo" attribute to an 
instance of it.  It will then be '/foo' for any component that is a child 
of that root.

Note that any PEAK component without a parent component is a "root 
component".  So if I create a binding.Component() and then set its "foo" 
attribute to a Foo(), and its "bar" attribute to a FooBar(), then as far as 
those three Component, Foo, and FooBar instances are concerned, '/' is the 
Component(), '/foo' is the Foo(), and '/bar' is the FooBar().

(This means, by the way, that it's useless to lookup a component path 
without having some component to use as context, since there can be 
arbitrarily many "root" objects in a PEAK application.  For example, PEAK 
considers any non-PEAK object to be a root.  If you ask PEAK what the 
component path of the number "42" is, PEAK will tell you it's "/", because 
42 has no parent component.  The only non-PEAK object type PEAK can guess 
parent components for, is ModuleType.  PEAK considers every top-level 
Python module to be a root component, but modules contained in packages 
have the package as their parent component.)


>should foo be a property of application object?  say,
>
>     class FooBarApp(binding.component):
>          foo = binding.New(Foo)

That's definitely one way to do it, and certainly one of the easier 
ways.  I would suggest you add a 'provides=' keyword to the binding.New(), 
giving either an interface or a PropertyName() (or a tuple of either), so 
that components can find it by function rather than by location.  Because 
PEAK bindings are always computed once and then cached, there is almost no 
performance difference to look something up by interface or property name 
instead of by absolute location.


>if not, then how and when the utility object should be instantiated?

You can also do it "manually" or register a provider rule via 
'registerProvider()', but I haven't yet run into a situation where I needed 
or wanted to.  It's generally easiest to just create bindings, or if you 
want it to be externally customizable, use a config file.




More information about the PEAK mailing list