=========
Protocols
=========

This package defines Protocols that use Zope's component registry.
It was inspired by Tim Hochberg's way to register generic functions.

    http://mail.python.org/pipermail/python-3000/2006-April/000394.html
    
Basically a Protocol says how a component is configured and how it is called.
Therefore each Protocol has two main methods: configure and __call__.
The configure method triggers the configuration actions which in turn
ensure the registration/activation of the protocol elements.
This is typically done in production mode via a few ZCML directives
which are part of the protocol package. Since you often want to
extend a protocol with application specific behavior you can
also activate protocol additions via ZCML. 

Most protocols support an `activate` method. This method
registers all component declarations that have been collected 
in the protocol. A typical use case are doctests where you want to
ensure that protocol elements like adapters and utilities are registered.

In the following we describe how protocols interact with content classes, 
adapters, utilities, and subscribers. See browser.txt for view related 
protocols and generic.txt for an implementation of generic functions.


Hello World
===========

Let's start with "hello world". In demo/hello you find the usual structure
of a Zope3 application:

    interfaces.py: the involved (public) interfaces which are needed
                   to make things replaceable and extensible
    world.py:      the content class and a basic implementation 
                   of the content related interfaces
    greeting.py:   the view that shows the mini program to the user
    configure.zcml the ZCML statements which feed the component registries

All this can be reduced to a single file (demo/hello/allinone.py):

    from persistent import Persistent 
    from zope.publisher.browser import BrowserView 
    
    from bebop.protocol import protocol
    from bebop.protocol import browser
    
    class World(Persistent): 
        pass
    
    greet = protocol.GenericFunction('IGreet')
    @greet.when(World)
    def greet_world(world):
        return 'world'
    
    class Greeting(BrowserView): 
        browser.page(World, name="greet", permission='zope.Public')
      
        def __call__(self): 
            return "Hello %s" % greet(self.context) 


Note that this file contains no reference to external interfaces and no
ZCML. Even the model and the view are in the same file. That of course
works only in simple cases, in more complex applications it may be
necessary to split things up, and then the traditional Zope3 structure
make of course much more sense. So this example is not intended to show
that there is something inherently bad with Zope3's verbosity.
To the contrary: We strive at a simplification that integrates well 
with existing Zope3 packages and at the same time allows to simplify 
things without loosing the explicitness of Zope3.

The example illustrates two main ideas which are the core of this package:

    1. ZCML statements integrated into the Python code as class advisors
       and method decorators
       
    2. A generic function ``greet``that replaces all the IGreetable, IGreet
       interfaces without loosing the replaceability and extensibility
       of the Zope component architecture.

The necessary ZCML can be generated from the source code since most of
the class advisors and decorators are equivalent to existing ZCML
statements. The browser.pages directive, for instance, exactly matches
the following ZCML snippet:

    <browser:pages name="greet"/>
   
The generic function ``greet`` however goes beyond the simple idea
of putting configuration statements into the code. It show's that Zope3's
component architecture is rich enough to maintain extensions which
were not anticipated by the original design. If we look at the complete
generated ZCML of this example we see that the generic function is 
registered as an adapter for an interface that is generated by the
GenericFunction factory:
   
    >>> from bebop.protocol.directive import record
    >>> print record(module='bebop.protocol.demo.hello.allinone')
    <configure
           xmlns:browser="http://namespaces.zope.org/browser"
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <browser:page
          class="bebop.protocol.demo.hello.allinone.Greeting"
          layer="zope.publisher.interfaces.browser.IDefaultBrowserLayer"
          for="bebop.protocol.demo.hello.allinone.World"
          permission="zope.Public"
          name="greet"
          attribute="__call__"
       />
       <zope:adapter
          factory="bebop.protocol.demo.hello.allinone.greet_world"
          provides="bebop.protocol.demo.hello.allinone.IGreet"
          for="bebop.protocol.demo.hello.allinone.World"
       />
    </configure>


Both ideas are elaborated in the following. Protocols are the basic
building blocks of this approach.


Class Protocol
==============

For many cases it is sufficient to use the predefined protocols. The class
protocol may serve as an example. It closely mimics the ZCML class 
directive since the protocol does exactly what the directive does: it
configures the security machinery. There's only one important difference. 
The protocol is completely written in Python, and the corresponding 
ZCML directives are generated from the Python code.

    >>> class IPerson(zope.interface.Interface):
    ...     name = zope.interface.Attribute(u'The name of the person')
    ...     email = zope.interface.Attribute(u'The email address')

    >>> from bebop.protocol import protocol
    >>> class Person(object):
    ...     zope.interface.implements(IPerson)
    ...     protocol.classProtocol.require(
    ...         permission="zope.View",
    ...         interface=IPerson)

We could have used `require` as a shorthand for `classProtocol.require`.
The more explict form was choosen to make clear that the a predefined protocol 
instance is involved.

The protocol collects all declarations and allows to configure the described
objects according to these declarations. We can inspect the protocol by 
calling it's report method. This method returns the corresponding configuration
statements which belong to a set of modules:

    >>> print protocol.classProtocol.record(modules=('bebop.protocol.readme',))
    <configure
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <zope:class class="bebop.protocol.readme.Person">
           <require
                  permission="zope.View"
                  interface="bebop.protocol.readme.IPerson"
           />
       </zope:class>
    </configure>

Since this written record is like a contract further modifications to the 
protocol are prohibited:

    >>> class Employee(object):
    ...     zope.interface.implements(IPerson)
    ...     protocol.classProtocol.require(
    ...         permission="zope.View",
    ...         interface=IPerson)
    Traceback (most recent call last):
    ...
    ProtocolError: protocol 'class' is written and must be reopened ...

We must explicitely reopen the protocol to allow further extensions:

    >>> protocol.classProtocol.reopen()
    >>> class Employee2(object):
    ...     zope.interface.implements(IPerson)
    ...     protocol.classProtocol.require(
    ...         permission="zope.View",
    ...         interface=IPerson)

Declarations as such have no consequences (as in politics). We have 
enact the protocol in order to use the declarations. Typically this
is done in ZCML. We need to load the metaconfiguration first:

    >>> from zope.configuration import xmlconfig
    >>> import bebop.protocol
    >>> context = xmlconfig.file('meta.zcml',  bebop.protocol)    

The most powerfull way to enact protocols is a call to the protocol 
directive.  This directive recursively collects all used protocols in 
all modules of a package, enacts the declarations, and records the 
corresponding ZCML directives in a singe file:

    >>> protocol_zcml = '''<configure
    ...       xmlns="http://iwm-kmrc.de/bebop"
    ...      >
    ...     <protocol package="bebop.protocol.demo.package"
    ...               record="demo/package/protocol.zcml"/>
    ... </configure>'''
    >>> ignore = xmlconfig.string(protocol_zcml, context=context)

If you compare the demo code in bebop.protocol.demo.package with the 
resulting configuration, you can estimate the amount of saved typed text:

    >>> generated_zcml = open(context.path('demo/package/protocol.zcml')).read()
    >>> print generated_zcml
    <configure
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <zope:adapter
          factory="bebop.protocol.demo.package.adapter.SampleAdapter"
          for="zope.interface.Interface"
       />
       <zope:adapter
          factory="bebop.protocol.demo.package.adapter.NamedSampleAdapter"
          for="zope.interface.Interface"
          name="demo"
       />
       <zope:adapter
          factory="bebop.protocol.demo.package.adapter.SampleMultiAdapter"
          for="str int"
       />
       <zope:adapter
          factory="bebop.protocol.demo.package.adapter.TrustedAdapter"
          for="dict"
          trusted="True"
       />
       <zope:utility
          factory="bebop.protocol.demo.package.utility.SampleUtility"
       />
       <zope:utility
          component="bebop.protocol.demo.package.utility.sampleComponent"
       />
       <zope:utility
          component="bebop.protocol.demo.package.utility.SampleComponentUtility"
          provides="zope.component.interfaces.IFactory"
       />
       <zope:utility
          permission="zope.View"
          factory="bebop.protocol.demo.package.utility.SampleNamedUtility"
          name="demo"
       />
       <zope:subscriber
          handler="bebop.protocol.demo.package.subscriber.sampleSubscriber"
          for="bebop.protocol.demo.package.interfaces.ISampleEvent"
       />
       <zope:class class="bebop.protocol.demo.package.security.SampleClass">
           <factory
                  id="bebop.protocol.demo.package.security.SampleClass"
           />
           <require
                  permission="zope.View"
                  interface="bebop.protocol.demo.package.interfaces.ISampleClass"
           />
           <require
                  permission="zope.MangeContent"
                  interface="bebop.protocol.demo.package.interfaces.IProtectedMethods"
           />
           <allow
                  interface="bebop.protocol.demo.package.interfaces.IPublicMethods"
           />
           <require
                  permission="zope.MangeContent"
                  set_attributes="protected_attribute"
           />
       </zope:class>
    </configure>
 
Let's check whether the protocol directive registers the components correctly:

    >>> from bebop.protocol.demo.package import interfaces
    >>> from bebop.protocol.demo.package.utility import sampleComponent
    >>> utility = zope.component.getUtility(interfaces.ISampleComponentUtility)
    >>> utility == sampleComponent
    True
    
    >>> from bebop.protocol.demo.package.utility import SampleComponentUtility
    >>> factory = zope.component.getUtility(zope.component.interfaces.IFactory)
    >>> factory == SampleComponentUtility
    True
    
    >>> from bebop.protocol.demo.package.subscriber import SampleEvent
    >>> import zope.event
    >>> zope.event.notify(SampleEvent())
    sampleEventSubscriber called
    
Sometimes, mostly in tests, it might be useful to activate the protocol 
directly.  Most protocols support activate and deactivate methods which 
register and unregister the corresponding components. The classPotocol 
above is an exception to this rule since this protocol is not able to 
revert the security settings. All following protocols can be activated
and deactivated at will.


Adapter Protocol
================

The adapter protocol mimics the ZCML adapter directive. The protocol.adapter
declaration says that an object or function can be used as an adapter:
    
    >>> class ISample(zope.interface.Interface):
    ...     def foo(self):
    ...         pass
    
    >>> class Adapted(object):
    ...     pass
    >>> class SampleAdapter(object):
    ...     zope.interface.implements(ISample)
    ...     protocol.adapter(Adapted, permission='zope.View')
    ...     def __init__(self, context):
    ...         self.context = context
    ...     def foo(self):
    ...         print 'foo'
 
As a more complex example we consider also a named multiadapter:

    >>> class IComplexSample(zope.interface.Interface):
    ...     def foo(self):
    ...         print 'complex foo'

    >>> class SampleMultiAdapter(object):
    ...     zope.interface.implements(IComplexSample)
    ...     def __init__(self, context, num):
    ...         pass
    ...     protocol.adapter(Adapted, int,
    ...         permission='zope.View',
    ...         name='complex')
    ...     def foo(self):
    ...         print 'complex foo'

The protocol has to be activated before we can use the adapter:

    >>> ISample(Adapted()).foo()
    Traceback (most recent call last):
    ...
    TypeError: ('Could not adapt', <bebop.protocol.readme.Adapted object at ...

We need an explicit activation of the underlying protocol:

    >>> protocol.adapterProtocol.activate()
    
After the protocol has been activated the adapter works as expected:

    >>> ISample(Adapted()).foo()
    foo
    
    >>> adapter = zope.component.getMultiAdapter((Adapted(), 123),
    ...     IComplexSample, name='complex')
    >>> adapter.foo()
    complex foo
    
Functions can also be declared as adapter factories. This is done by calling 
protocol.adapter as a function decorator:

    >>> class ISampleAnnotation(zope.interface.Interface):
    ...     pass
    >>> class SampleAnnotations(object):
    ...     def __init__(self, context):
    ...         self.context = context
    
    >>> @protocol.adapter(Adapted, provides=ISampleAnnotation)
    ... def sampleannotation(obj):
    ...     return SampleAnnotations(obj)
    >>> ISampleAnnotation(Adapted())
    <bebop.protocol.readme.SampleAnnotations object at ...>
    
Let's see what has been recorded:
    
    >>> adapterProtocol = protocol.adapterProtocol
    >>> print adapterProtocol.record(modules=('bebop.protocol.readme',))
    <configure
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <zope:adapter
          factory="bebop.protocol.readme.SampleAdapter"
          for="bebop.protocol.readme.Adapted"
          permission="zope.View"
       />
       <zope:adapter
          factory="bebop.protocol.readme.SampleMultiAdapter"
          for="bebop.protocol.readme.Adapted int"
          permission="zope.View"
          name="complex"
       />
       <zope:adapter
          factory="bebop.protocol.readme.sampleannotation"
          provides="bebop.protocol.readme.ISampleAnnotation"
          for="bebop.protocol.readme.Adapted"
       />
    </configure>

    >>> protocol.adapterProtocol.reopen()
 
    
    
Utility Protocol
================

In ZCML you can declare a utility factory or a component. The same can be 
done with the protocol.utility declaration:

    >>> class SampleUtility(object):
    ...     zope.interface.implements(ISample)
    >>> protocol.utility(factory=SampleUtility, name='test1')

In cases were the name and module can be deduced from the object (e.g.
classes and functions), you can provide the object itself:

    >>> def utilityFunction(): print "called utility function"
    >>> protocol.utility(
    ...     component=utilityFunction,
    ...     provides=ISample,
    ...     name='test3')
    
If you assign a component to a variable for future reference you have to
use a syntax which deviates a little from the corresponding ZCML directive 
since the variable name is available to the component itself (the module can
be taken from the internal stack frame of the utility call):

    >>> sampleUtility = SampleUtility()
    >>> protocol.utility(
    ...     component=sampleUtility,
    ...     variable='sampleUtility',
    ...     name='test2')
    
Let's see how these declarations have been recorded:

    >>> utilityProtocol = protocol.utilityProtocol
    >>> print utilityProtocol.record(modules=('bebop.protocol.readme',))
    <configure
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <zope:utility
          factory="bebop.protocol.readme.SampleUtility"
          name="test1"
       />
       <zope:utility
          component="bebop.protocol.readme.utilityFunction"
          provides="bebop.protocol.readme.ISample"
          name="test3"
       />
       <zope:utility
          component="bebop.protocol.readme.sampleUtility"
          name="test2"
       />
    </configure>

Since the underlying protocol has not been activated yet the objects are 
not registered:

    >>> zope.component.getUtility(ISample, name='test1')
    Traceback (most recent call last):
    ...
    ComponentLookupError: (<InterfaceClass ...readme.ISample>, 'test1')

The utilities can be used only after an explicit activation:

    >>> protocol.utilityProtocol.activate()
    >>> zope.component.getUtility(ISample, name='test1')
    <bebop.protocol.readme.SampleUtility object at ...>
    
    >>> sampleUtility is zope.component.getUtility(ISample, name='test2')
    True
    
    >>> zope.component.getUtility(ISample, name='test3')()
    called utility function


Subscriber Protocol
===================

A subscriber can be declared as follows:

    >>> from zope.lifecycleevent.interfaces import IObjectCreatedEvent
    >>> @protocol.subscriber(IPerson, IObjectCreatedEvent)
    ... def personCreatedHandler(obj, event):
    ...     print "personCreatedHandler called"
    
This corresponds to the following ZCML:

    >>> subscriberProtocol = protocol.subscriberProtocol
    >>> print subscriberProtocol.record(modules=('bebop.protocol.readme',))
    <configure
           xmlns:zope="http://namespaces.zope.org/zope"
          >
       <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->
       <zope:subscriber
          handler="bebop.protocol.readme.personCreatedHandler"
          for="bebop.protocol.readme.IPerson ...IObjectCreatedEvent"
       />
    </configure>
    
Again the corresponding protocol must be activated:

    >>> subscriberProtocol.activate()
    
    >>> person = Person()   
    >>> event = zope.lifecycleevent.ObjectCreatedEvent(person)
    >>> zope.component.event.objectEventNotify(event)
    personCreatedHandler called

