[PEAK] component metaclasses

Phillip J. Eby pje at telecommunity.com
Wed Apr 6 20:14:35 EDT 2005


At 10:56 AM 4/6/05 -0700, Jason wrote:

>First, to create the callbacks that are invoked by gui events.  I was
>playing with using signals, having every callback just emit a signal
>which the rest of the app can observe.  So in glade if I've tied a
>button click to a callback named on_help_button_clicked, in the class
>I define something like
>
>handlers = [('on_help_button_clicked', 'help')]

Use 'binding.metadata()' to do this.  See 'attributes.txt' in peak/binding:

     http://cvs.eby-sarna.com/PEAK/src/peak/binding/attributes.txt?rev=HEAD&content-type=text/vnd.viewcvs-markup

for details on how it works.  You should create a class like ObserveSignal, 
such that you can do this:

class SomeClass(binding.Component):
     binding.metadata(
         on_help_button_clicked = ObserveSignal('help')
     )

In order for this to work, you have to define a handler that tells PEAK 
what to do when the attribute is declared.  Your handler will receive the 
class, the attribute name, and the ObserveSignal instance, and it's up to 
it do whatever else you want it to do.

Once you've done this, you can also use it with bindings, e.g.:


class SomeClass(binding.Component):
    someAttr = binding.Obtain('config:myapp.foobar_handler/',
        [ObserveSignal('foobar')]
    )

This will call your metadata handler with 
'(SomeClass,"someAttr",ObserveSignal("foobar"))' when the class is constructed.

For another example of use of this technique, see:

     http://peak.telecommunity.com/DevCenter/OptionsHowTo

which uses this same metadata technique to define command line options.


>The other use of the metaclass is to allow subclasses to easily define
>what methods should be run during setup and shutdown.  Each class will
>inherit some setup procedures and can define their own.

An easy way to do this is make a SetupMethod class and do:

     binding.metadata(some_method = SetupMethod())

in the class.   Again, you will define a handler that's passed the class, 
attribute name, and metadata instance.  If you don't need any parameters to 
SetupMethod, you might just make a single instance and use that 
instead.  Metadata objects in general should be shareable between multiple 
attributes, as usually they are immutable objects that just describe 
something; it's up to the handler to do something with the information such 
as registering an attribute or method with some framework.

However, if you need to keep a logical sequence of setup methods, then you 
should keep a class-level counter in SetupMethod, and assign its value to 
each new instance, then you can sort them later on by that, in case they've 
gotten out of order due to keyword arguments.  (A bit more on this below.)


>class GenericPluginA(Base):
>     handlers = [('on_help_button_clicked', 'help')]
>     setup = ['run_me', 'then_me']
>
>     def run_me(self): ...
>     def then_me(self):...
>
>class Plugin(GenericPluginA):
>     setup = ['run_me_too']
>     ...

So I see these as:

class GenericPluginA(Base):
     binding.metadata(
         on_help_button_clicked = ObserveSignal('help'),
         run_me = SetupMethod(),
         then_me = SetupMethod()
     )
     ...

class Plugin(GenericPluginA):
     binding.metadata(
         run_me_too = SetupMethod()
     )

The idea here is that if each call to SetupMethod() gets a new ID, then 
despite the fact that the attributes don't get registered in any particular 
order (due to being in dictionaries), you still know what the original 
definition order is.  You'll notice that peak.running.options uses this 
trick to be able to display options help in the order the options were 
defined in the source code.




More information about the PEAK mailing list