{ "info": { "author": "Bebop Team", "author_email": "uwe_oestermeier@iwm-kmrc.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Framework :: Zope3", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Internet :: WWW/HTTP" ], "description": "Bebop Protocol\n==============\n\nThis package contains a extension to Zope3 which simplifies the registration\nof components.\n\nZope3 has been criticized as an overly complex and difficult to learn framework.\nEspecially the ZCML configuration language and the missing Python API for \nconfiguration actions has been a topic of debate.\n\nThis package tries to combine the conciseness of Python with the explicitness, \nfine-grained configurability, and conflict management of ZCML. A protocol is a \nPython object that defines how a component is registered and configured, how \nthe component is called, and how it is unregistered. Protocols are used and \nextended by declarations, i.e. class advisors and decorators that correspond \nto existing ZCML directives. All declarations within a package can be activated \nwith a single line of ZCML. The equivalent ZCML configuration can be recorded \nfor documentary purposes and used as a basis for more selective configurations \nand overloads.\n\nSince the protocol package mimics the ZCML directives as closely as possible \nit provides no extra learning curve for the experienced Zope3 programmer. \nPredefined protocols are available for adapters, utilities, subscribers, pages,\nand menus. Since protocols are extensible, they can also be used to define \ngeneric functions and extend the component architecture with special forms \nof utilities and adapter lookup without the need to define new ZCML directives. \n\n\nDetailed Documentation\n**********************\n\n=========\nProtocols\n=========\n\nThis package defines Protocols that use Zope's component registry.\nIt was inspired by Tim Hochberg's way to register generic functions.\n\n http://mail.python.org/pipermail/python-3000/2006-April/000394.html\n \nBasically a Protocol says how a component is configured and how it is called.\nTherefore each Protocol has two main methods: configure and __call__.\nThe configure method triggers the configuration actions which in turn\nensure the registration/activation of the protocol elements.\nThis is typically done in production mode via a few ZCML directives\nwhich are part of the protocol package. Since you often want to\nextend a protocol with application specific behavior you can\nalso activate protocol additions via ZCML. \n\nMost protocols support an `activate` method. This method\nregisters all component declarations that have been collected \nin the protocol. A typical use case are doctests where you want to\nensure that protocol elements like adapters and utilities are registered.\n\nIn the following we describe how protocols interact with content classes, \nadapters, utilities, and subscribers. See browser.txt for view related \nprotocols and generic.txt for an implementation of generic functions.\n\n\nHello World\n===========\n\nLet's start with \"hello world\". In demo/hello you find the usual structure\nof a Zope3 application:\n\n interfaces.py: the involved (public) interfaces which are needed\n to make things replaceable and extensible\n world.py: the content class and a basic implementation \n of the content related interfaces\n greeting.py: the view that shows the mini program to the user\n configure.zcml the ZCML statements which feed the component registries\n\nAll this can be reduced to a single file (demo/hello/allinone.py):\n\n from persistent import Persistent \n from zope.publisher.browser import BrowserView \n \n from bebop.protocol import protocol\n from bebop.protocol import browser\n \n class World(Persistent): \n pass\n \n greet = protocol.GenericFunction('IGreet')\n @greet.when(World)\n def greet_world(world):\n return 'world'\n \n class Greeting(BrowserView): \n browser.page(World, name=\"greet\", permission='zope.Public')\n \n def __call__(self): \n return \"Hello %s\" % greet(self.context) \n\n\nNote that this file contains no reference to external interfaces and no\nZCML. Even the model and the view are in the same file. That of course\nworks only in simple cases, in more complex applications it may be\nnecessary to split things up, and then the traditional Zope3 structure\nmake of course much more sense. So this example is not intended to show\nthat there is something inherently bad with Zope3's verbosity.\nTo the contrary: We strive at a simplification that integrates well \nwith existing Zope3 packages and at the same time allows to simplify \nthings without loosing the explicitness of Zope3.\n\nThe example illustrates two main ideas which are the core of this package:\n\n 1. ZCML statements integrated into the Python code as class advisors\n and method decorators\n \n 2. A generic function ``greet``that replaces all the IGreetable, IGreet\n interfaces without loosing the replaceability and extensibility\n of the Zope component architecture.\n\nThe necessary ZCML can be generated from the source code since most of\nthe class advisors and decorators are equivalent to existing ZCML\nstatements. The browser.pages directive, for instance, exactly matches\nthe following ZCML snippet:\n\n <browser:pages name=\"greet\"/>\n \nThe generic function ``greet`` however goes beyond the simple idea\nof putting configuration statements into the code. It show's that Zope3's\ncomponent architecture is rich enough to maintain extensions which\nwere not anticipated by the original design. If we look at the complete\ngenerated ZCML of this example we see that the generic function is \nregistered as an adapter for an interface that is generated by the\nGenericFunction factory:\n \n >>> from bebop.protocol.directive import record\n >>> print record(module='bebop.protocol.demo.hello.allinone')\n <configure\n xmlns:browser=\"http://namespaces.zope.org/browser\"\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <browser:page\n class=\"bebop.protocol.demo.hello.allinone.Greeting\"\n layer=\"zope.publisher.interfaces.browser.IDefaultBrowserLayer\"\n for=\"bebop.protocol.demo.hello.allinone.World\"\n permission=\"zope.Public\"\n name=\"greet\"\n attribute=\"__call__\"\n />\n <zope:adapter\n factory=\"bebop.protocol.demo.hello.allinone.greet_world\"\n provides=\"bebop.protocol.demo.hello.allinone.IGreet\"\n for=\"bebop.protocol.demo.hello.allinone.World\"\n />\n </configure>\n\n\nBoth ideas are elaborated in the following. Protocols are the basic\nbuilding blocks of this approach.\n\n\nClass Protocol\n==============\n\nFor many cases it is sufficient to use the predefined protocols. The class\nprotocol may serve as an example. It closely mimics the ZCML class \ndirective since the protocol does exactly what the directive does: it\nconfigures the security machinery. There's only one important difference. \nThe protocol is completely written in Python, and the corresponding \nZCML directives are generated from the Python code.\n\n >>> class IPerson(zope.interface.Interface):\n ... name = zope.interface.Attribute(u'The name of the person')\n ... email = zope.interface.Attribute(u'The email address')\n\n >>> from bebop.protocol import protocol\n >>> class Person(object):\n ... zope.interface.implements(IPerson)\n ... protocol.classProtocol.require(\n ... permission=\"zope.View\",\n ... interface=IPerson)\n\nWe could have used `require` as a shorthand for `classProtocol.require`.\nThe more explict form was choosen to make clear that the a predefined protocol \ninstance is involved.\n\nThe protocol collects all declarations and allows to configure the described\nobjects according to these declarations. We can inspect the protocol by \ncalling it's report method. This method returns the corresponding configuration\nstatements which belong to a set of modules:\n\n >>> print protocol.classProtocol.record(modules=('bebop.protocol.readme',))\n <configure\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <zope:class class=\"bebop.protocol.readme.Person\">\n <zope:require\n permission=\"zope.View\"\n interface=\"bebop.protocol.readme.IPerson\"\n />\n </class>\n </configure>\n\nSince this written record is like a contract further modifications to the \nprotocol are prohibited:\n\n >>> class Employee(object):\n ... zope.interface.implements(IPerson)\n ... protocol.classProtocol.require(\n ... permission=\"zope.View\",\n ... interface=IPerson)\n Traceback (most recent call last):\n ...\n ProtocolError: protocol 'class' is written and must be reopened ...\n\nWe must explicitely reopen the protocol to allow further extensions:\n\n >>> protocol.classProtocol.reopen()\n >>> class Employee2(object):\n ... zope.interface.implements(IPerson)\n ... protocol.classProtocol.require(\n ... permission=\"zope.View\",\n ... interface=IPerson)\n\nDeclarations as such have no consequences (as in politics). We have \nenact the protocol in order to use the declarations. Typically this\nis done in ZCML. We need to load the metaconfiguration first:\n\n >>> from zope.configuration import xmlconfig\n >>> import bebop.protocol\n >>> context = xmlconfig.file('meta.zcml', bebop.protocol) \n\nThe most powerfull way to enact protocols is a call to the protocol \ndirective. This directive recursively collects all used protocols in \nall modules of a package, enacts the declarations, and records the \ncorresponding ZCML directives in a singe file:\n\n >>> protocol_zcml = '''<configure\n ... xmlns=\"http://iwm-kmrc.de/bebop\"\n ... >\n ... <protocol package=\"bebop.protocol.demo.package\"\n ... record=\"demo/package/protocol.zcml\"/>\n ... </configure>'''\n >>> ignore = xmlconfig.string(protocol_zcml, context=context)\n\nIf you compare the demo code in bebop.protocol.demo.package with the \nresulting configuration, you can estimate the amount of saved typed text:\n\n >>> print open(context.path('demo/package/protocol.zcml')).read()\n <configure\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <zope:adapter\n factory=\"bebop.protocol.demo.package.adapter.SampleAdapter\"\n for=\"zope.interface.Interface\"\n />\n <zope:adapter\n factory=\"bebop.protocol.demo.package.adapter.NamedSampleAdapter\"\n for=\"zope.interface.Interface\"\n name=\"demo\"\n />\n <zope:adapter\n factory=\"bebop.protocol.demo.package.adapter.SampleMultiAdapter\"\n for=\"str int\"\n />\n <zope:adapter\n factory=\"bebop.protocol.demo.package.adapter.TrustedAdapter\"\n for=\"dict\"\n trusted=\"True\"\n />\n <zope:utility\n factory=\"bebop.protocol.demo.package.utility.SampleUtility\"\n />\n <zope:utility\n component=\"bebop.protocol.demo.package.utility.sampleComponent\"\n />\n <zope:utility\n component=\"bebop.protocol.demo.package.utility.SampleComponentUtility\"\n provides=\"zope.component.interfaces.IFactory\"\n />\n <zope:utility\n permission=\"zope.View\"\n factory=\"bebop.protocol.demo.package.utility.SampleNamedUtility\"\n name=\"demo\"\n />\n <zope:subscriber\n handler=\"bebop.protocol.demo.package.subscriber.sampleSubscriber\"\n for=\"bebop.protocol.demo.package.interfaces.ISampleEvent\"\n />\n <zope:class class=\"bebop.protocol.demo.package.security.SampleClass\">\n <zope:factory\n id=\"bebop.protocol.demo.package.security.SampleClass\"\n />\n <zope:require\n permission=\"zope.View\"\n interface=\"...demo.package.interfaces.ISampleClass\"\n />\n <zope:require\n permission=\"zope.MangeContent\"\n interface=\"...demo.package.interfaces.IProtectedMethods\"\n />\n <zope:allow\n interface=\"...demo.package.interfaces.IPublicMethods\"\n />\n <zope:require\n permission=\"zope.MangeContent\"\n set_attributes=\"protected_attribute\"\n />\n </class>\n </configure>\n \nLet's check whether the protocol directive registers the components correctly:\n\n >>> from bebop.protocol.demo.package import interfaces\n >>> from bebop.protocol.demo.package.utility import sampleComponent\n >>> utility = zope.component.getUtility(interfaces.ISampleComponentUtility)\n >>> utility == sampleComponent\n True\n \n >>> from bebop.protocol.demo.package.utility import SampleComponentUtility\n >>> factory = zope.component.getUtility(zope.component.interfaces.IFactory)\n >>> factory == SampleComponentUtility\n True\n \n >>> from bebop.protocol.demo.package.subscriber import SampleEvent\n >>> import zope.event\n >>> zope.event.notify(SampleEvent())\n sampleEventSubscriber called\n \nSometimes, mostly in tests, it might be usefull to activate the protocol \ndirectly. Most protocols support activate and deactivate methods which \nregister and unregister the corresponding components. The classPotocol \nabove is an exception to this rule since this protocol is not able to \nrevert the security settings. All following protocols can be activated\nand deactivated at will.\n\n\nAdapter Protocol\n================\n\nThe adapter protocol mimics the ZCML adapter directive. The protocol.adapter\ndeclaration says that an object or function can be used as an adapter:\n \n >>> class ISample(zope.interface.Interface):\n ... def foo(self):\n ... pass\n \n >>> class Adapted(object):\n ... pass\n >>> class SampleAdapter(object):\n ... zope.interface.implements(ISample)\n ... protocol.adapter(Adapted, permission='zope.View')\n ... def __init__(self, context):\n ... self.context = context\n ... def foo(self):\n ... print 'foo'\n \nThe protocol has to be activated before we can use the adapter:\n\n >>> ISample(Adapted()).foo()\n Traceback (most recent call last):\n ...\n TypeError: ('Could not adapt', <bebop.protocol.readme.Adapted object at ...\n\nWe need an explicit activation of the underlying protocol:\n\n >>> protocol.adapterProtocol.activate()\n \nAfter the protocol has been activated the adapter works as expected:\n\n >>> ISample(Adapted()).foo()\n foo\n \nFunctions can also be declared as adapter factories. This is done by calling \nprotocol.adapter as a function decorator:\n\n >>> class ISampleAnnotation(zope.interface.Interface):\n ... pass\n >>> class SampleAnnotations(object):\n ... def __init__(self, context):\n ... self.context = context\n \n >>> @protocol.adapter(Adapted, provides=ISampleAnnotation)\n ... def sampleannotation(obj):\n ... return SampleAnnotations(obj)\n >>> ISampleAnnotation(Adapted())\n <bebop.protocol.readme.SampleAnnotations object at ...>\n \nLet's see what has been recorded:\n \n >>> adapterProtocol = protocol.adapterProtocol\n >>> print adapterProtocol.record(modules=('bebop.protocol.readme',))\n <configure\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <zope:adapter\n factory=\"bebop.protocol.readme.SampleAdapter\"\n for=\"bebop.protocol.readme.Adapted\"\n permission=\"zope.View\"\n />\n <zope:adapter\n factory=\"bebop.protocol.readme.sampleannotation\"\n provides=\"bebop.protocol.readme.ISampleAnnotation\"\n for=\"bebop.protocol.readme.Adapted\"\n />\n </configure>\n\n >>> protocol.adapterProtocol.reopen()\n \n\nUtility Protocol\n================\n\nIn ZCML you can declare a utility factory or a component. The same can be \ndone with the protocol.utility declaration:\n\n >>> class SampleUtility(object):\n ... zope.interface.implements(ISample)\n >>> protocol.utility(factory=SampleUtility, name='test1')\n\nIn cases were the name and module can be deduced from the object (e.g.\nclasses and functions), you can provide the object itself:\n\n >>> def utilityFunction(): print \"called utility function\"\n >>> protocol.utility(\n ... component=utilityFunction,\n ... provides=ISample,\n ... name='test3')\n \nIf you assign a component to a variable for future reference you have to\nuse a syntax which deviates a little from the corresponding ZCML directive \nsince the variable name is available to the component itself (the module can\nbe taken from the internal stack frame of the utility call):\n\n >>> sampleUtility = SampleUtility()\n >>> protocol.utility(\n ... component=sampleUtility,\n ... variable='sampleUtility',\n ... name='test2')\n \nLet's see how these declarations have been recorded:\n\n >>> utilityProtocol = protocol.utilityProtocol\n >>> print utilityProtocol.record(modules=('bebop.protocol.readme',))\n <configure\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <zope:utility\n factory=\"bebop.protocol.readme.SampleUtility\"\n name=\"test1\"\n />\n <zope:utility\n component=\"bebop.protocol.readme.utilityFunction\"\n provides=\"bebop.protocol.readme.ISample\"\n name=\"test3\"\n />\n <zope:utility\n component=\"bebop.protocol.readme.sampleUtility\"\n name=\"test2\"\n />\n </configure>\n\nSince the underlying protocol has not been activated yet the objects are \nnot registered:\n\n >>> zope.component.getUtility(ISample, name='test1')\n Traceback (most recent call last):\n ...\n ComponentLookupError: (<InterfaceClass ...readme.ISample>, 'test1')\n\nThe utilities can be used only after an explicit activation:\n\n >>> protocol.utilityProtocol.activate()\n >>> zope.component.getUtility(ISample, name='test1')\n <bebop.protocol.readme.SampleUtility object at ...>\n \n >>> sampleUtility is zope.component.getUtility(ISample, name='test2')\n True\n \n >>> zope.component.getUtility(ISample, name='test3')()\n called utility function\n\n\nSubscriber Protocol\n===================\n\nA subscriber can be declared as follows:\n\n >>> from zope.lifecycleevent.interfaces import IObjectCreatedEvent\n >>> @protocol.subscriber(IPerson, IObjectCreatedEvent)\n ... def personCreatedHandler(obj, event):\n ... print \"personCreatedHandler called\"\n \nThis corresponds to the following ZCML:\n\n >>> subscriberProtocol = protocol.subscriberProtocol\n >>> print subscriberProtocol.record(modules=('bebop.protocol.readme',))\n <configure\n xmlns:zope=\"http://namespaces.zope.org/zope\"\n >\n <!-- GENERATED PROTOCOL. DO NOT MODIFY OR INCLUDE -->\n <zope:subscriber\n handler=\"bebop.protocol.readme.personCreatedHandler\"\n for=\"bebop.protocol.readme.IPerson ...IObjectCreatedEvent\"\n />\n </configure>\n \nAgain the corresponding protocol must be activated:\n\n >>> subscriberProtocol.activate()\n \n >>> person = Person() \n >>> event = zope.lifecycleevent.ObjectCreatedEvent(person)\n >>> zope.component.event.objectEventNotify(event)\n personCreatedHandler called\n\n\nDownload\n**********************", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://svn.kmrc.de/projects/devel/bebop.protocol", "keywords": "zope3 ZCML generation configuration", "license": "GPL", "maintainer": null, "maintainer_email": null, "name": "bebop.protocol", "package_url": "https://pypi.org/project/bebop.protocol/", "platform": "any", "project_url": "https://pypi.org/project/bebop.protocol/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://svn.kmrc.de/projects/devel/bebop.protocol" }, "release_url": "https://pypi.org/project/bebop.protocol/0.1/", "requires_dist": null, "requires_python": null, "summary": "This package allows to register components from Python. It also provides a basic implementation of generic functions in Zope3", "version": "0.1" }, "last_serial": 786783, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "6997b09e736a6571ac12b8efd30ef623", "sha256": "75786b58ef5255ea3231b40d84cd3bc771cdb402413da2db712f06dafba13774" }, "downloads": -1, "filename": "bebop.protocol-0.1.tar.gz", "has_sig": false, "md5_digest": "6997b09e736a6571ac12b8efd30ef623", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30706, "upload_time": "2007-07-10T20:18:19", "url": "https://files.pythonhosted.org/packages/da/74/02dd7a4a2e19ade2d0393667cbf3720f9ca62c0430f2c0b347a68a2de4a8/bebop.protocol-0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6997b09e736a6571ac12b8efd30ef623", "sha256": "75786b58ef5255ea3231b40d84cd3bc771cdb402413da2db712f06dafba13774" }, "downloads": -1, "filename": "bebop.protocol-0.1.tar.gz", "has_sig": false, "md5_digest": "6997b09e736a6571ac12b8efd30ef623", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30706, "upload_time": "2007-07-10T20:18:19", "url": "https://files.pythonhosted.org/packages/da/74/02dd7a4a2e19ade2d0393667cbf3720f9ca62c0430f2c0b347a68a2de4a8/bebop.protocol-0.1.tar.gz" } ] }