Zope component architechture
-
Upload
anatoly-bubenkov -
Category
Technology
-
view
1.370 -
download
0
description
Transcript of Zope component architechture
Zope Component Architecture
There should be a silver bullet! Let's try to look for it with ZCA at least :)
Anatoly Bubenkov@bubenkoffPaylogic Groningen Office19.08.2013
I you’ve heard something bad about zope...Then probably you don’t know what it is :) So i’ll tell you:ZOPE stands for The Object Publising Environment, so TOPE, but T -> Z to be polite.Name zope itself is a fish, Ballerus ballerus, also known as zope or blue bream, is a fish native to Eurasia.
Some people say that python became popular in web development because of zope :)
So what is zope?
Object-oriented web application server and frameworktwo main versions alive and actively developed:zope2 - mainly known because Plone CMF is based on itzope3, then BlueBream, then part of BlueBream became Zope Toolkit (ZTK).Many things from zope3 were backported to zope2, but zope3 is initially a full rewrite of zope2 using Component Architecture.And probably you’ve heard most of bad things about zope2.
Component based architectureBasically the idea:
● objects(components) interact only through interfaces.● single interface - various implementations possible.
But interaction only through interfaces can be tricky...class ISize(Interface):
“””Object size.”””size = Interface.Attribute(‘size’)
--------------------component---------------------------class IDocument(Interface):
“””Text document.”””text = Interface.Attribute(‘text’)
--------------------component---------------------------class IImage(Interface):
“””Image.”””image = Interface.Attribute(‘image’)
--------------------component---------------------------To get size from objects of various types, we need to implement ISize, but if we want to keep components separate, we can’t do it.
So here comes an Adapter paradigmAdding adaptors, leads by-interface communication much more elegant, because you can control the interaction between objects through them without direct modification.
IDocument -> ISize adaptor
ISize
IDocument IImage
IImage -> ISize adaptor
What do we have in python?
● PEP 246 (adaptation) and PEP 245 (interfaces) - killed by Guido in favor of __notimplemented__
● zope.interface● PyProtocols (not active since 2003)● PEAK-Rules (allows you to HACK
everything,so not a direct alternative, also seems not active already)
zope.interface, here is the implementation
The key package of:● zope● twisted● pyramideven Shipped out of the box with OSX :)Pythonic (with C optimizations) implementation of component architechture.● python3 ready● pypy compliant
zope.interface: InterfacesInterfaces are defined using Python class statements:>>> import zope.interface>>> class IFoo(zope.interface.Interface):... """Foo blah blah"""...... x = zope.interface.Attribute("""X blah blah""")...... def bar(q, r=None):... """bar blah blah"""
The interface is not a class, it’s an Interface, an instance of InterfaceClass:>>> type(IFoo)<class 'zope.interface.interface.InterfaceClass'>
We can ask for the interface’s documentation:>>> IFoo.__doc__'Foo blah blah'and its name:>>> IFoo.__name__'IFoo'Interfaces can inherit from each other just like normal python classes.
zope.interface: Interface implementationThe most common way to declare interfaces is using the implements function in a class statement:>>> class Foo(object):... zope.interface.implements(IFoo)...... def __init__(self, x=None):... self.x = x...... def bar(self, q, r=None):... return q, r, self.x...... def __repr__(self):... return "Foo(%s)" % self.x
We can ask if interface is implemented by class:>>> IFoo.implementedBy(Foo)TrueInterface will be Provided by object, if it’s class implements it:>>> foo = Foo()>>> IFoo.providedBy(foo)TrueAnd of course interface is not provided by a class implementing it>>> IFoo.providedBy(Foo)FalseBut it’s also possible to dynamically provide interface for an object via zope.interface.directlyProvides
zope.interface: InvariantsInvariants are validation expressions, where input is only depends on interface attributes.>>> def range_invariant(ob):... if ob.max < ob.min:... raise RangeError(ob)Given this invariant, we can use it in an interface definition:>>> class IRange(zope.interface.Interface):... min = zope.interface.Attribute("Lower bound")... max = zope.interface.Attribute("Upper bound")...... zope.interface.invariant(range_invariant)Interfaces have a method for checking their invariants:>>> class Range(object):... zope.interface.implements(IRange)...... def __init__(self, min, max):... self.min, self.max = min, max...... def __repr__(self):... return "Range(%s, %s)" % (self.min, self.max)
>>> IRange.validateInvariants(Range(1,2))>>> IRange.validateInvariants(Range(2,1))Traceback (most recent call last):...RangeError: Range(2, 1)
zope.interface: AdaptersSingle AdaptersLet’s look at a simple example, using a single required specification:>>> from zope.interface.adapter import AdapterRegistry>>> import zope.interface
>>> class IR1(zope.interface.Interface):... pass>>> class IP1(zope.interface.Interface):... pass>>> class IP2(IP1):... pass
>>> registry = AdapterRegistry()
We’ll register an object that depends on IR1 and “provides” IP2:>>> registry.register([IR1], IP2, '', 12)
Given the registration, we can look it up again:>>> registry.lookup([IR1], IP2, '')12
Finding out what, if anything, is registeredWe can ask if there is an adapter registered for a collection of interfaces. This is different than lookup, because it looks for an exact match:>>> print registry.registered([IR1], IP1)11
>>> print registry.registered([IR1], IP2)12
>>> print registry.registered([IR1], IP2, 'bob')Bob's 12
>>> print registry.registered([IR2], IP1)21
>>> print registry.registered([IR2], IP2)None
The adapter registry supports the computation of adapters. In this case, we have to register adapter factories:>>> class IR(zope.interface.Interface): ... pass
>>> class X: ... zope.interface.implements(IR)
>>> class Y: ... zope.interface.implements(IP1) ... def __init__(self, context): ... self.context = context
>>> registry.register([IR], IP1, '', Y)In this case, we registered a class as the factory. Now we can call queryAdapter to get the adapted object:>>> x = X()>>> y = registry.queryAdapter(x, IP1)>>> y.__class__.__name__'Y'>>> y.context is xTrue
Multi-adaptationYou can adapt multiple objects:>>> class Q:... zope.interface.implements(IQ)
As with single adapters, we register a factory, which is often a class:>>> class IM(zope.interface.Interface):... pass>>> class M:... zope.interface.implements(IM)... def __init__(self, x, q):... self.x, self.q = x, q>>> registry.register([IR, IQ], IM, '', M)
And then we can call queryMultiAdapter to compute an adapter:>>> q = Q()>>> m = registry.queryMultiAdapter((x, q), IM)>>> m.__class__.__name__'M'>>> m.x is x and m.q is qTrue
Listing named adaptersAdapters are named. Sometimes, it’s useful to get all of the named adapters for given interfaces:>>> adapters = list(registry.lookupAll([IR1], IP1))>>> adapters.sort()>>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")]
This works for multi-adapters too:>>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')>>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))>>> adapters.sort()>>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')]
zope.interface: SubscriptionsNormally, we want to look up an object that most-closely matches a specification. Sometimes, we want to get all of the objects that match some specification. We use subscriptions for this. We subscribe objects against specifications and then later find all of the subscribed objects:>>> registry.subscribe([IR1], IP2, 'sub12 1')>>> registry.subscriptions([IR1], IP2)['sub12 1']
Note that, unlike regular adapters, subscriptions are unnamed.You can have multiple subscribers for the same specification:>>> registry.subscribe([IR1], IP2, 'sub12 2')>>> registry.subscriptions([IR1], IP2)['sub12 1', 'sub12 2']
If subscribers are registered for the same required interfaces, they are returned in the order of definition.You can register subscribers for all specifications using None:>>> registry.subscribe([None], IP1, 'sub_1')>>> registry.subscriptions([IR2], IP1)['sub_1', 'sub12 1', 'sub12 2']
zope.interface: Subscription adaptersWe normally register adapter factories, which then allow us to compute adapters, but with subscriptions, we get multiple adapters. Here’s an example of multiple-object subscribers:>>> registry.subscribe([IR, IQ], IM, M)>>> registry.subscribe([IR, IQ], IM, M2)
>>> subscribers = registry.subscribers((x, q), IM)>>> len(subscribers)2>>> class_names = [s.__class__.__name__ for s in subscribers]>>> class_names.sort()>>> class_names['M', 'M2']>>> [(s.x is x and s.q is q) for s in subscribers][True, True]
adapter factory subcribers can’t return None values:>>> def M3(x, y):... return None
>>> registry.subscribe([IR, IQ], IM, M3)>>> subscribers = registry.subscribers((x, q), IM)>>> len(subscribers)2
zope.interface: Handlers
A handler is a subscriber factory that doesn’t produce any normal output. It returns None. A handler is unlike adapters in that it does all of its work when the factory is called.To register a handler, simply provide None as the provided interface:>>> def handler(event):... print 'handler', event
>>> registry.subscribe([IR1], None, handler)>>> registry.subscriptions([IR1], None) == [handler]True
Where it can be used?
Basically in every ‘big and complex’ project. Providing a clear communication protocol for pieces of your big picture.Adaptors are great when you need a representation, for example a web-based view of an object.Handlers provide efficient event model.Interfaces give you contracts.
..And why?
● Having system of many independent components interacting via robust protocol is much better than monolithic approach when everyone knows everyone
● No need to invent the wheel (having api.py in each of your package)
● Relying on python imports potentially causes problems with circular dependencies, with zope.interface you only need interfaces (can be separate) for communication.
Thanks! Questions?
Reference:● http://docs.zope.org/zope.interface/● http://docs.zope.org/zope.component/● http://www.muthukadan.net/docs/zca.html● http://en.wikipedia.org/wiki/Zope● http://en.wikipedia.
org/wiki/Design_by_contract● http://www.artima.com/weblogs/viewpost.
jsp?thread=155123