diff --git a/src/grokcore/xmlrpc/__init__.py b/src/grokcore/xmlrpc/__init__.py index 24ab97d..075e19b 100644 --- a/src/grokcore/xmlrpc/__init__.py +++ b/src/grokcore/xmlrpc/__init__.py @@ -11,93 +11,14 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Grok -""" -from zope.interface import implements -from zope.component import adapts -from martian import ClassGrokker, InstanceGrokker, GlobalGrokker -from martian import baseclass -from martian.error import GrokError, GrokImportError +from grokcore.component import * +from grokcore.security import * +from grokcore.view import * -from grokcore.component import Adapter, MultiAdapter, GlobalUtility, Context -from grokcore.component.decorators import subscribe, adapter, implementer -from grokcore.component.directive import ( - context, name, title, description, provides, global_utility, direct) +from grokcore.xmlrpc.components import XMLRPC -from grokcore.content import Model, Container, OrderedContainer - -from grokcore.security import Permission -from grokcore.security import Public -from grokcore.security import require - -from grokcore.view import PageTemplate -from grokcore.view import PageTemplateFile -from grokcore.view import DirectoryResource -from grokcore.view import layer -from grokcore.view import template -from grokcore.view import templatedir -from grokcore.view import skin -from grokcore.view import url -from grokcore.view import path - -from grokcore.viewlet import Viewlet -from grokcore.viewlet import ViewletManager -from grokcore.viewlet import view -from grokcore.viewlet import viewletmanager -from grokcore.viewlet import order - -from grokcore.formlib import action -from grokcore.formlib import AutoFields -from grokcore.formlib import Fields - -from grokcore.annotation import Annotation - -from grokcore.site import LocalUtility -from grokcore.site import Site -from grokcore.site import local_utility - -from zope.event import notify -from zope.site.hooks import getSite -from zope.lifecycleevent import ( - IObjectCreatedEvent, ObjectCreatedEvent, - IObjectModifiedEvent, ObjectModifiedEvent, - IObjectCopiedEvent, ObjectCopiedEvent) -from zope.app.publication.interfaces import IBeforeTraverseEvent - -from zope.publisher.interfaces.browser import IBrowserRequest -from zope.publisher.interfaces.browser import IDefaultBrowserLayer - -from zope.container.interfaces import ( - IObjectAddedEvent, - IObjectMovedEvent, - IObjectRemovedEvent, - IContainerModifiedEvent) -from zope.container.contained import ( - ObjectAddedEvent, - ObjectMovedEvent, - ObjectRemovedEvent, - ContainerModifiedEvent) - -from grok.events import ApplicationInitializedEvent -from grok.components import Application -from grok.components import View, Form, AddForm, EditForm, DisplayForm -from grok.components import XMLRPC, REST, JSON -from grok.components import Traverser -from grok.components import Indexes -from grok.components import Role -from grok.interfaces import IRESTSkinType, IRESTLayer -from grok.interfaces import IApplicationInitializedEvent - -from grok.directive import ( - permissions, site, restskin, traversable) - -# BBB These two functions are meant for test fixtures and should be -# imported from grok.testing, not from grok. -from grok.testing import grok, grok_component - -# Our __init__ provides the grok API directly so using 'import grok' is enough. -from grok.interfaces import IGrokAPI +from grokcore.xmlrpc.interfaces import IGrokcoreXMLRPCAPI from zope.interface import moduleProvides -moduleProvides(IGrokAPI) -__all__ = list(IGrokAPI) +moduleProvides(IGrokcoreXMLRPCAPI) +__all__ = list(IGrokcoreXMLRPCAPI) diff --git a/src/grokcore/xmlrpc/components.py b/src/grokcore/xmlrpc/components.py index 992e1af..bbceb4a 100644 --- a/src/grokcore/xmlrpc/components.py +++ b/src/grokcore/xmlrpc/components.py @@ -18,166 +18,13 @@ provided here. """ -import simplejson - -import zope.location -from zope import component -from zope import interface -from zope.securitypolicy.role import Role as securitypolicy_Role -from zope.publisher.browser import BrowserPage -from zope.publisher.interfaces import NotFound -from zope.publisher.interfaces.browser import IBrowserPublisher -from zope.publisher.interfaces.http import IHTTPRequest -from zope.publisher.publish import mapply -from zope.publisher.defaultview import getDefaultViewName -from zope.container.interfaces import IReadContainer - -import grok -import martian.util import grokcore.view -import grokcore.site -import grokcore.message -from grok import interfaces, util - -# BBB this is for import backward compatibility. -from grokcore.json import JSON -from grokcore.content import Model, Container, OrderedContainer - - -class Application(grokcore.site.Site): - """Mixin for creating Grok application objects. - - When a `grok.Container` (or a `grok.Model`, though most developers - use containers) also inherits from `grok.Application`, it not only - gains the component registration abilities of a `grok.Site`, but - will also be listed in the Grok admin control panel as one of the - applications that the admin can install directly at the root of - their Zope database. - - """ - interface.implements(interfaces.IApplication) - - -class View(grokcore.view.View): - """The base class for views with templates in Grok applications. - - Each class that inherits from `grok.View` is designed to "render" a - category of content objects by reducing them to a document (often an - HTML document). Every view has a name, and is invoked when users - visit the URL of an eligible context object followed by the name of - the view itself:: - - http://example.com/app/folder/object/viewname - - If the view name might conflict with actual content inside of the - context (in the above URL, the context might already contain an - attribute or item named ``viewname``), then the URL can be explicit - that it is asking for a view by preceding its name with ``@@``:: - - http://example.com/app/folder/object/@@viewname - - Instead of returning a full document, views are sometimes used to - provide only a snippet of information for inclusion in some larger - document; the view can then be called from inside of another view's - page template:: - -
  • snippet goes here
  • - - A view class can specify the category of objects that it can render - by calling the `grok.context()` directive with either a class or an - interface. Otherwise, Grok will attempt to determine the context - automatically by searching the view's module for exactly one - `grok.Model` or `grok.Container` class (or some other class - providing the interface `IContext`) and using that class, if found. - - Grok normally creates a view's name (the name used in URLs) by - downcasing the name of the view class itself. The developer can - override this by supplying the `grok.name()` directive instead. - - The view name ``index`` is special (this works whether the view - class itself is named ``Index``, or whether ``grok.name('index')`` - is used instead). A view named ``index`` is used to render an - object when the user visits its URL without appending a view name. - - Each view needs to generate and return a document. There are two - ways of doing so: either the view can provide a `render()` method - that returns a document, or the view can be associated with a page - template that Grok will. Page templates can be associated with a - view in three different ways: - - * Grok will automatically associate a view with a page template - defined in an accompanying ``templates`` directory. If a view - class ``MammothList`` occurs in a module ``/animal.py``, for - example, then Grok will look for a page template with the name - ``/animal_templates/mammothlist.pt``, where ``.pt`` can be - any page-template extension recognized by Grok. - - * Grok will automatically associate a view with a page template - object in the same module whose name is the downcased name of the - view class itself. For example, a view ``MammothList`` might be - defined in a module alongside an actual template instance named - ``mammothlist``. - - * The developer can explicitly define the path to the page template - file by providing the ``grok.template()`` directive. - - Before a page template is rendered, Grok will call the `update()` - method on the view, if one is supplied, which can pre-compute values - that the template will need to display. Both `render()` methods and - `update()` methods will find the context for which the view is being - rendered under ``self.context``. - - """ - interface.implements(interfaces.IGrokView) - - def application_url(self, name=None, data=None): - """Return the URL of the nearest enclosing `grok.Application`.""" - return util.application_url(self.request, self.context, name, data) - - def flash(self, message, type='message'): - """Send a short message to the user.""" - grokcore.message.send(message, type=type, name='session') - - -class Form(grokcore.formlib.Form, View): - """The base class for forms in Grok applications. - - A class that inherits from `grok.Form` is a `grok.View` whose - template will be given information about the fields in its context, - and use that information to render an HTML form for adding or - editing the form. Generally developers use one of the subclasses: - - * `grok.AddForm` - * `grok.DisplayForm` - * `grok.EditForm` - - """ - interface.implements(interfaces.IGrokForm) - - -class AddForm(grokcore.formlib.AddForm, View): - """Base class for add forms in Grok applications.""" - interface.implements(interfaces.IGrokForm) - - -class DisplayForm(grokcore.formlib.DisplayForm, View): - """Base class for display forms in Grok applications.""" - interface.implements(interfaces.IGrokForm) - - -class EditForm(grokcore.formlib.EditForm, View): - """Base class for edit forms in Grok applications.""" - interface.implements(interfaces.IGrokForm) - - -class ViewishViewSupport(grokcore.view.ViewSupport): - def application_url(self, name=None, data=None): - return util.application_url(self.request, self.context, name, data) +from zope import interface -class XMLRPC(ViewishViewSupport): +class XMLRPC(grokcore.view.ViewSupport): """Base class for XML-RPC endpoints in Grok applications. When an application creates a subclass of `grok.XMLRPC`, it is @@ -202,171 +49,4 @@ class XMLRPC(ViewishViewSupport): used for the XML-RPC call is available as ``self.context``. """ - - -class REST(zope.location.Location, ViewishViewSupport): - """Base class for REST views in Grok applications.""" - interface.implements(interfaces.IREST) - - def __init__(self, context, request): - self.context = self.__parent__ = context - self.request = request - - -class Traverser(object): - """Base class for traversers in Grok applications.""" - interface.implements(IBrowserPublisher) - - def __init__(self, context, request): - self.context = context - self.request = request - - def browserDefault(self, request): - # if we have a RESTful request, we will handle - # GET, POST and HEAD differently (PUT and DELETE are handled already - # but not on the BrowserRequest layer but the HTTPRequest layer) - if interfaces.IRESTLayer.providedBy(request): - rest_view = component.getMultiAdapter( - (self.context, self.request), name=request.method) - return rest_view, () - view_name = getDefaultViewName(self.context, request) - view_uri = "@@%s" % view_name - return self.context, (view_uri,) - - def publishTraverse(self, request, name): - subob = self.traverse(name) - if subob is not None: - return util.safely_locate_maybe(subob, self.context, name) - - traversable_dict = grok.traversable.bind().get(self.context) - if traversable_dict: - if name in traversable_dict: - subob = getattr(self.context, traversable_dict[name]) - if callable(subob): - subob = subob() - return util.safely_locate_maybe(subob, self.context, name) - - # XXX Special logic here to deal with containers. It would be - # good if we wouldn't have to do this here. One solution is to - # rip this out and make you subclass ContainerTraverser if you - # wanted to override the traversal behaviour of containers. - if IReadContainer.providedBy(self.context): - item = self.context.get(name) - if item is not None: - return item - - view = component.queryMultiAdapter((self.context, request), name=name) - if view is not None: - return view - - raise NotFound(self.context, name, request) - - def traverse(self, name): - # this will be overridden by subclasses - pass - - -class ContextTraverser(Traverser): - """Base class for context traversers in Grok applications. - - A context traverser is like a normal `grok.Traverser` but, instead - of supplying its own `traverse()` method, it directs Grok to go call - the ``traverse()`` method on the context itself in order to process - the next name in the URL. - - """ - component.adapts(interfaces.IContext, IHTTPRequest) - - def traverse(self, name): - traverse = getattr(self.context, 'traverse', None) - if traverse: - return traverse(name) - - -class ContainerTraverser(Traverser): - """Base class for container traversers in Grok applications. - - A container traverser is like a normal `grok.Traverser` but, instead - of supplying its own ``traverse()`` method, Grok will either call - the ``traverse()`` method on the context itself, if any, else call - ``get()`` on the container (a getitem-style lookup) in order to - resolve the next name in the URL. - - """ - component.adapts(interfaces.IContainer, IHTTPRequest) - - def traverse(self, name): - traverse = getattr(self.context, 'traverse', None) - if traverse: - result = traverse(name) - if result is not None: - return result - # try to get the item from the container - return self.context.get(name) - - -class IndexesClass(object): - """Base class for index collections in a Grok application. - - A `grok.Indexes` utility provides one or more Zope Database content - indexes for use in a `grok.Site` or `grok.Application`. The site or - application that the indexes are intended for should be named with - the `grok.site()` directive, and the kind of object to index should - be named with a `grok.context()` directive. - - Inside their class, the developer should specify one or more - `grok.index.Field` instances naming object attributes that should be - indexed (and therefore searchable):: - - class ArticleIndex(grok.Indexes): - grok.site(Newspaper) - grok.context(Article) - author = index.Field() - title = index.Field() - body = index.Text() - - See the `grok.index` module for more information on field types. - - Note that indexes are persistent: they are stored in the Zope - database alongside the site or application that they index. They - are created when the site or application is first created, and so an - already-created site will not change just because the definition of - one of its `grok.Indexes` changes; it will either have to be deleted - and re-created, or some other operation performed to bring its - indexes up to date. - - """ - def __init__(self, name, bases=(), attrs=None): - if attrs is None: - return - indexes = {} - for name, value in attrs.items(): - # Ignore everything that's not an index definition object - # except for values set by directives - if '.' in name: - setattr(self, name, value) - continue - if not interfaces.IIndexDefinition.providedBy(value): - continue - indexes[name] = value - self.__grok_indexes__ = indexes - # __grok_module__ is needed to make defined_locally() return True for - # inline templates - self.__grok_module__ = martian.util.caller_module() - -Indexes = IndexesClass('Indexes') - - -class Role(securitypolicy_Role): - """Base class for roles in Grok applications. - - A role is a description of a class of users that gives them a - machine-readable name, a human-readable title, and a set of - permissions which users belong to that role should possess:: - - class Editor(grok.Role): - grok.name('news.Editor') - grok.title('Editor') - grok.permissions('news.EditArticle', 'news.PublishArticle') - - """ + interface.implements(grokcore.view.IGrokSecurityView) diff --git a/src/grokcore/xmlrpc/configure.zcml b/src/grokcore/xmlrpc/configure.zcml index 70444a6..5d5a3c7 100644 --- a/src/grokcore/xmlrpc/configure.zcml +++ b/src/grokcore/xmlrpc/configure.zcml @@ -3,65 +3,6 @@ xmlns:browser="http://namespaces.zope.org/browser" xmlns:grok="http://namespaces.zope.org/grok"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -73,15 +14,6 @@ priority="21" /> - - - - + diff --git a/src/grokcore/xmlrpc/ftesting.zcml b/src/grokcore/xmlrpc/ftesting.zcml index dbb6037..d08a899 100644 --- a/src/grokcore/xmlrpc/ftesting.zcml +++ b/src/grokcore/xmlrpc/ftesting.zcml @@ -2,57 +2,12 @@ xmlns="http://namespaces.zope.org/zope" xmlns:grok="http://namespaces.zope.org/grok" xmlns:browser="http://namespaces.zope.org/browser" - i18n_domain="grok" - package="grok"> + i18n_domain="grokcore.xmlrpc" + package="grokcore.xmlrpc"> - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/grokcore/xmlrpc/ftests/test_grok_functional.py b/src/grokcore/xmlrpc/ftests/test_grok_functional.py index 53e91b9..b358098 100644 --- a/src/grokcore/xmlrpc/ftests/test_grok_functional.py +++ b/src/grokcore/xmlrpc/ftests/test_grok_functional.py @@ -1,12 +1,12 @@ import re import unittest, doctest -import grok +import grokcore.xmlrpc from pkg_resources import resource_listdir from zope.testing import renormalizing from zope.app.wsgi.testlayer import BrowserLayer, http -FunctionalLayer = BrowserLayer(grok) +FunctionalLayer = BrowserLayer(grokcore.xmlrpc) checker = renormalizing.RENormalizing([ # Accommodate to exception wrapping in newer versions of mechanize @@ -41,7 +41,7 @@ def suiteFromPackage(name): if filename == '__init__.py': continue - dottedname = 'grok.ftests.%s.%s' % (name, filename[:-3]) + dottedname = 'grokcore.xmlrpc.ftests.%s.%s' % (name, filename[:-3]) test = doctest.DocTestSuite( dottedname, checker=checker, @@ -58,10 +58,7 @@ def suiteFromPackage(name): def test_suite(): suite = unittest.TestSuite() - for name in [ - 'xmlrpc', - 'traversal', 'form', 'url', 'security', 'rest', - 'catalog', 'site', 'application', 'viewlet', 'lifecycle']: + for name in ['xmlrpc']: suite.addTest(suiteFromPackage(name)) return suite diff --git a/src/grokcore/xmlrpc/ftests/xmlrpc/require.py b/src/grokcore/xmlrpc/ftests/xmlrpc/require.py index 461eabd..baac46e 100644 --- a/src/grokcore/xmlrpc/ftests/xmlrpc/require.py +++ b/src/grokcore/xmlrpc/ftests/xmlrpc/require.py @@ -34,30 +34,32 @@ >>> print server.rest() ME GROK TIRED! """ -import grok +import grokcore.component as grok +import grokcore.xmlrpc +import grokcore.security import zope.interface -class MammothRPC(grok.XMLRPC): +class MammothRPC(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) def stomp(self): return 'Manfred stomped.' - @grok.require('zope.ManageContent') + @grokcore.security.require('zope.ManageContent') def dance(self): return 'Manfred doesn\'t like to dance.' -class CavemanRPC(grok.XMLRPC): +class CavemanRPC(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) - grok.require('zope.ManageContent') + grokcore.security.require('zope.ManageContent') def hunt(self): return 'ME GROK LIKE MAMMOTH!' - @grok.require('zope.View') + @grokcore.security.require('zope.View') def eat(self): return 'MMM, MANFRED TASTE GOOD!' - @grok.require(grok.Public) + @grokcore.security.require(grokcore.security.Public) def rest(self): return 'ME GROK TIRED!' diff --git a/src/grokcore/xmlrpc/ftests/xmlrpc/xmlrpc.py b/src/grokcore/xmlrpc/ftests/xmlrpc/xmlrpc.py index 3ab1e86..5973edd 100644 --- a/src/grokcore/xmlrpc/ftests/xmlrpc/xmlrpc.py +++ b/src/grokcore/xmlrpc/ftests/xmlrpc/xmlrpc.py @@ -16,18 +16,19 @@ 'baby pounced.' """ -import grok +import grokcore.component as grok +import grokcore.xmlrpc -class Mammoth(grok.Model): +class Mammoth(grok.Context): def traverse(self, name): if name == 'baby': return MammothBaby() -class MammothBaby(grok.Model): +class MammothBaby(grok.Context): pass -class MammothRPC(grok.XMLRPC): +class MammothRPC(grokcore.xmlrpc.XMLRPC): grok.context(Mammoth) def stomp(self): @@ -36,7 +37,7 @@ def stomp(self): def dance(self): return '%s doesn\'t like to dance.' % self.context.__name__ -class BabyRPC(grok.XMLRPC): +class BabyRPC(grokcore.xmlrpc.XMLRPC): grok.context(MammothBaby) def pounce(self): diff --git a/src/grokcore/xmlrpc/interfaces.py b/src/grokcore/xmlrpc/interfaces.py index 9d3b4d6..e41be0d 100644 --- a/src/grokcore/xmlrpc/interfaces.py +++ b/src/grokcore/xmlrpc/interfaces.py @@ -14,202 +14,21 @@ """Grok interfaces """ from zope import interface -from zope.interface.interfaces import IInterface -from zope.component.interfaces import IObjectEvent -from zope.publisher.interfaces.http import IHTTPRequest -from zope.container.interfaces import IContainer as IContainerBase # Expose interfaces from grokcore.* packages as well: -import grokcore.annotation.interfaces import grokcore.component.interfaces -import grokcore.formlib.interfaces -import grokcore.json.interfaces import grokcore.security.interfaces -import grokcore.site.interfaces import grokcore.view.interfaces -import grokcore.viewlet.interfaces -from grokcore.component.interfaces import IContext -from grokcore.component.interfaces import IGrokErrors - -class IGrokBaseClasses(grokcore.annotation.interfaces.IBaseClasses, - grokcore.component.interfaces.IBaseClasses, - grokcore.security.interfaces.IBaseClasses, - grokcore.site.interfaces.IBaseClasses, - grokcore.view.interfaces.IBaseClasses, - grokcore.json.interfaces.IBaseClasses): - Model = interface.Attribute("Base class for persistent content objects " - "(models).") - Container = interface.Attribute("Base class for containers.") - OrderedContainer = interface.Attribute("Base class for ordered containers.") - Application = interface.Attribute("Base class for applications.") +class IBaseClasses(grokcore.component.interfaces.IBaseClasses, + grokcore.security.interfaces.IBaseClasses, + grokcore.view.interfaces.IBaseClasses): XMLRPC = interface.Attribute("Base class for XML-RPC methods.") - REST = interface.Attribute("Base class for REST views.") - Traverser = interface.Attribute("Base class for custom traversers.") - Indexes = interface.Attribute("Base class for catalog index definitions.") - Role = interface.Attribute("Base class for roles.") - - -class IGrokDirectives(grokcore.component.interfaces.IDirectives, - grokcore.security.interfaces.IDirectives, - grokcore.site.interfaces.IDirectives, - grokcore.view.interfaces.IDirectives): - - def permissions(permissions): - """Specify the permissions that comprise a role. - """ - - def site(class_or_interface): - """Specifies the site that an indexes definition is for. - - It can only be used inside grok.Indexes subclasses. - """ - - -class IGrokEvents(interface.Interface): - - IObjectCreatedEvent = interface.Attribute("") - - ObjectCreatedEvent = interface.Attribute("") - - IObjectModifiedEvent = interface.Attribute("") - - ObjectModifiedEvent = interface.Attribute("") - - IObjectCopiedEvent = interface.Attribute("") - - ObjectCopiedEvent = interface.Attribute("") - - IObjectAddedEvent = interface.Attribute("") - - ObjectAddedEvent = interface.Attribute("") - - IObjectMovedEvent = interface.Attribute("") - - ObjectMovedEvent = interface.Attribute("") - - IObjectRemovedEvent = interface.Attribute("") - - ObjectRemovedEvent = interface.Attribute("") - - IContainerModifiedEvent = interface.Attribute("") - - ContainerModifiedEvent = interface.Attribute("") - - IBeforeTraverseEvent = interface.Attribute("") - - IApplicationInitializedEvent = interface.Attribute("") - - ApplicationInitializedEvent = interface.Attribute("") - - -class IGrokAPI(grokcore.formlib.interfaces.IGrokcoreFormlibAPI, - grokcore.security.interfaces.IGrokcoreSecurityAPI, - grokcore.site.interfaces.IGrokcoreSiteAPI, - grokcore.view.interfaces.IGrokcoreViewAPI, - grokcore.viewlet.interfaces.IGrokcoreViewletAPI, - IGrokBaseClasses, IGrokDirectives, - IGrokEvents, IGrokErrors): - - # BBB this is deprecated - def grok(dotted_name): - """Grok a module or package specified by ``dotted_name``. - - NOTE: This function will be removed from the public Grok - public API. For tests and interpreter sessions, use - grok.testing.grok(). - """ - - # BBB this is deprecated - def grok_component(name, component, context=None, module_info=None, - templates=None): - """Grok an arbitrary object. Can be useful during testing. - - name - the name of the component (class name, or global instance name - as it would appear in a module). - component - the object (class, etc) to grok. - context - the context object (optional). - module_info - the module being grokked (optional). - templates - the templates registry (optional). - - Note that context, module_info and templates might be required - for some grokkers which rely on them. - - NOTE: This function will be removed from the public Grok - public API. For tests and interpreter sessions, use - grok.testing.grok_component(). - """ - - def notify(event): - """Send ``event`` to event subscribers.""" - - def getSite(): - """Get the current site.""" - - IRESTSkinType = interface.Attribute('The REST skin type') - - -class IGrokView(grokcore.view.interfaces.IGrokView): - """Grok views all provide this interface.""" - - def application_url(name=None): - """Return the URL of the closest application object in the - hierarchy or the URL of a named object (``name`` parameter) - relative to the closest application object. - """ - - def flash(message, type='message'): - """Send a short message to the user.""" - - -class IGrokForm(grokcore.formlib.interfaces.IGrokForm, IGrokView): - """All Grok forms provides this interface.""" - - -class IREST(interface.Interface): - context = interface.Attribute("Object that the REST handler presents.") - - request = interface.Attribute("Request that REST handler was looked" - "up with.") - - body = interface.Attribute( - """The text of the request body.""") - - -class IIndexDefinition(interface.Interface): - """Define an index for grok.Indexes. - """ - - def setup(catalog, name, context): - """Set up index called name in given catalog. - - Use name for index name and attribute to index. Set up - index for interface or class context. - """ - - -class IRESTLayer(IHTTPRequest): - """REST-specific Request functionality. - - Base Interfaces for defining REST-layers. - """ - - -class IRESTSkinType(IInterface): - """Skin type for REST requests. - """ - - -class IContainer(IContext, IContainerBase): - """A Grok container. - """ - -class IApplicationInitializedEvent(IObjectEvent): - """A Grok Application has been created with success and is now ready - to be used. - This event can be used to trigger the creation of contents or other tasks - that require the application to be fully operational : utilities installed - and indexes created in the catalog.""" +class IGrokcoreXMLRPCAPI(grokcore.component.interfaces.IGrokcoreComponentAPI, + grokcore.security.interfaces.IGrokcoreSecurityAPI, + grokcore.view.interfaces.IGrokcoreViewAPI, + IBaseClasses): + pass diff --git a/src/grokcore/xmlrpc/meta.py b/src/grokcore/xmlrpc/meta.py index e1fcd69..5883429 100644 --- a/src/grokcore/xmlrpc/meta.py +++ b/src/grokcore/xmlrpc/meta.py @@ -21,37 +21,21 @@ of a Grok-based web application. """ -import zope.component.interface -from zope import interface, component -from zope.interface.interface import InterfaceClass -from zope.publisher.interfaces.browser import IBrowserPublisher -from zope.publisher.interfaces.http import IHTTPRequest - +from grokcore.security.meta import PermissionGrokker from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest -from zope.securitypolicy.interfaces import IRole -from zope.securitypolicy.rolepermission import rolePermissionManager -from zope.i18nmessageid import Message -from zope.intid import IntIds -from zope.intid.interfaces import IIntIds -from zope.catalog.catalog import Catalog -from zope.catalog.interfaces import ICatalog -from zope.location import Location -from zope.exceptions.interfaces import DuplicationError -from zope.publisher.xmlrpc import XMLRPCView - -import martian -from martian.error import GrokError -import grok -from grok import components -from grok.interfaces import IRESTSkinType +from grokcore.view import make_checker -import grokcore.site.interfaces -from grokcore.security.meta import PermissionGrokker -from grokcore.view import make_checker +import martian +import grokcore.component +import grokcore.security +import grokcore.xmlrpc +from zope import interface, component +from zope.publisher.xmlrpc import XMLRPCView +from zope.location import Location class MethodPublisher(XMLRPCView, Location): """Copied from zope.app.publisher.xmlrpc to get rid of that dependency. @@ -79,9 +63,9 @@ class that we create on-the-fly by calling `type()`. We make each other to prepare a security check for the adapter. """ - martian.component(grok.XMLRPC) - martian.directive(grok.context) - martian.directive(grok.require, name='permission') + martian.component(grokcore.xmlrpc.XMLRPC) + martian.directive(grokcore.component.context) + martian.directive(grokcore.security.require, name='permission') def execute(self, factory, method, config, context, permission, **kw): name = method.__name__ @@ -104,272 +88,3 @@ def execute(self, factory, method, config, context, permission, **kw): args=(factory, method_view, permission), ) return True - - -class RESTGrokker(martian.MethodGrokker): - """Grokker for methods of a `grok.REST` subclass. - - When an application defines a `grok.REST` view, we do not actually - register the view with the Component Architecture. Instead, we grok - each of its methods separately, placing them each inside of a new - class that we create on-the-fly by calling `type()`. We make each - method the `__call__()` method of its new class, since that is how - Zope always invokes views. And it is this new class that is then - made the object of the two configuration actions that we schedule: - one to activate it as a REST adapter for the context, and the other - to prepare a security check for the adapter. - - This results in several registered views, typically with names like - `GET`, `PUT`, and `POST` - one for each method that the `grok.REST` - subclass defines. - - """ - martian.component(grok.REST) - martian.directive(grok.context) - martian.directive(grok.layer, default=grok.IRESTLayer) - martian.directive(grok.require, name='permission') - - def execute(self, factory, method, config, permission, context, - layer, **kw): - name = method.__name__ - - method_view = type( - factory.__name__, (factory,), - {'__call__': method}) - - adapts = (context, layer) - config.action( - discriminator=('adapter', adapts, interface.Interface, name), - callable=component.provideAdapter, - args=(method_view, adapts, interface.Interface, name), - ) - config.action( - discriminator=('protectName', method_view, '__call__'), - callable=make_checker, - args=(factory, method_view, permission), - ) - return True - - -_restskin_not_used = object() - - -class RestskinInterfaceDirectiveGrokker(martian.InstanceGrokker): - """Grokker for interfaces providing the `grok.restskin()` directive. - - Applications create REST skins by subclassing `grok.IRESTLayer` - and providing the subclass with a `grok.restskin()` directive giving - the prefix string which distinguishes that REST layers from others. - This grokker registers those skins. - - """ - martian.component(InterfaceClass) - - def grok(self, name, interface, module_info, config, **kw): - # This `InstanceGrokker` will be called for every instance of - # `InterfaceClass` - that is, for every interface defined in an - # application module! So we have to do our own filtering, by - # checking whether each interface includes the `grok.restskin()` - # directive, and skipping those that do not. - restskin = grok.restskin.bind(default=_restskin_not_used - ).get(interface) - if restskin is _restskin_not_used: - # The restskin directive is not actually used on the found - # interface. - return False - - if not interface.extends(grok.IRESTLayer): - # For REST layers it is required to extend IRESTLayer. - raise GrokError( - "The grok.restskin() directive is used on interface %r. " - "However, %r does not extend IRESTLayer which is " - "required for interfaces that are used as layers and are to " - "be registered as a restskin." - % (interface.__identifier__, interface.__identifier__), - interface) - - config.action( - discriminator=('restprotocol', restskin), - callable=zope.component.interface.provideInterface, - args=(restskin, interface, IRESTSkinType)) - - return True - - -class TraverserGrokker(martian.ClassGrokker): - """Grokker for subclasses of `grok.Traverser`.""" - martian.component(grok.Traverser) - martian.directive(grok.context) - - def execute(self, factory, config, context, **kw): - adapts = (context, IHTTPRequest) - config.action( - discriminator=('adapter', adapts, IBrowserPublisher, ''), - callable=component.provideAdapter, - args=(factory, adapts, IBrowserPublisher), - ) - return True - - -def default_fallback_to_name(factory, module, name, **data): - return name - - -class RoleGrokker(martian.ClassGrokker): - """Grokker for components subclassed from `grok.Role`. - - Each role is registered as a global utility providing the service - `IRole` under its own particular name, and then granted every - permission named in its `grok.permission()` directive. - - """ - martian.component(grok.Role) - martian.priority(martian.priority.bind().get(PermissionGrokker()) - 1) - martian.directive(grok.name) - martian.directive(grok.title, get_default=default_fallback_to_name) - martian.directive(grok.description) - martian.directive(grok.permissions) - - def execute(self, factory, config, name, title, description, - permissions, **kw): - if not name: - raise GrokError( - "A role needs to have a dotted name for its id. Use " - "grok.name to specify one.", factory) - # We can safely convert to unicode, since the directives makes sure - # it is either unicode already or ASCII. - if not isinstance(title, Message): - title = unicode(title) - if not isinstance(description, Message): - description = unicode(description) - role = factory(unicode(name), title, description) - - config.action( - discriminator=('utility', IRole, name), - callable=component.provideUtility, - args=(role, IRole, name), - ) - - for permission in permissions: - config.action( - discriminator=('grantPermissionToRole', permission, name), - callable=rolePermissionManager.grantPermissionToRole, - args=(permission, name), - ) - return True - - -class ApplicationGrokker(martian.ClassGrokker): - """Grokker for Grok application classes.""" - martian.component(grok.Application) - martian.priority(500) - - def grok(self, name, factory, module_info, config, **kw): - # XXX fail loudly if the same application name is used twice. - provides = grok.interfaces.IApplication - name = '%s.%s' % (module_info.dotted_name, name) - config.action( - discriminator=('utility', provides, name), - callable=component.provideUtility, - args=(factory, provides, name), - ) - return True - - -class IndexesGrokker(martian.InstanceGrokker): - """Grokker for Grok index bundles.""" - martian.component(components.IndexesClass) - - def grok(self, name, factory, module_info, config, **kw): - site = grok.site.bind().get(factory) - context = grok.context.bind().get(factory, module_info.getModule()) - catalog_name = grok.name.bind().get(factory) - - if site is None: - raise GrokError("No site specified for grok.Indexes " - "subclass in module %r. " - "Use grok.site() to specify." - % module_info.getModule(), - factory) - indexes = getattr(factory, '__grok_indexes__', None) - if indexes is None: - return False - - subscriber = IndexesSetupSubscriber(catalog_name, indexes, - context, module_info) - subscribed = (site, grok.IObjectAddedEvent) - config.action( - discriminator=None, - callable=component.provideHandler, - args=(subscriber, subscribed), - ) - return True - - -class IndexesSetupSubscriber(object): - """Helper that sets up indexes when their Grok site is created. - - Each `grok.Indexes` class serves as an assertion that, whenever an - instance of its `grok.site()` is created, the given list of indexes - should be generated as well. But a long period of time could elapse - between when the application starts (and its indexes are grokked), - and the moment, maybe days or weeks later, when a new instance of - that `grok.Site` is created. Hence this `IndexesSetupSubscriber`: - it can be instantiated at grokking time with the index information, - and then registered with the Component Architecture as an event that - should be fired later, whenever the right kind of `grok.Site` is - instantiated. At that point its `__call__` method is kicked off and - it makes sure the index catalogs get created properly. - - """ - def __init__(self, catalog_name, indexes, context, module_info): - self.catalog_name = catalog_name - self.indexes = indexes - self.context = context - self.module_info = module_info - - def __call__(self, site, event): - # make sure we have an intids - self._createIntIds(site) - # get the catalog - catalog = self._createCatalog(site) - # now install indexes - for name, index in self.indexes.items(): - try: - index.setup(catalog, name, self.context, self.module_info) - except DuplicationError: - raise GrokError( - "grok.Indexes in module %r causes " - "creation of catalog index %r in catalog %r, " - "but an index with that name is already present." % - (self.module_info.getModule(), name, self.catalog_name), - None) - - def _createCatalog(self, site): - """Create the catalog if needed and return it. - - If the catalog already exists, return that. - - """ - catalog = zope.component.queryUtility( - ICatalog, name=self.catalog_name, context=site, default=None) - if catalog is not None: - return catalog - catalog = Catalog() - setupUtility = component.getUtility( - grokcore.site.interfaces.IUtilityInstaller) - setupUtility(site, catalog, ICatalog, name=self.catalog_name) - return catalog - - def _createIntIds(self, site): - """Create intids if needed, and return it. - """ - intids = zope.component.queryUtility( - IIntIds, context=site, default=None) - if intids is not None: - return intids - intids = IntIds() - setupUtility = component.getUtility( - grokcore.site.interfaces.IUtilityInstaller) - setupUtility(site, intids, IIntIds) - return intids diff --git a/src/grokcore/xmlrpc/meta.zcml b/src/grokcore/xmlrpc/meta.zcml index 8b4a327..16f8217 100644 --- a/src/grokcore/xmlrpc/meta.zcml +++ b/src/grokcore/xmlrpc/meta.zcml @@ -3,16 +3,11 @@ xmlns:meta="http://namespaces.zope.org/meta" xmlns:grok="http://namespaces.zope.org/grok"> - - - - - diff --git a/src/grokcore/xmlrpc/publication.py b/src/grokcore/xmlrpc/publication.py new file mode 100644 index 0000000..6e4d538 --- /dev/null +++ b/src/grokcore/xmlrpc/publication.py @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Grok publication factories and classes. + +These factories, and the publication classes they return, make Grok +security different from the way that security normal operates during +Zope publication. Instead of security proxies being wrapped around +every object generated during traversal, and then wrapped around the +final object before it is viewed, only a single security check is done +when Grok is in charge: a check to see whether the view selected at the +end of the traversal process is, in fact, permitted to display the +object. + +""" +from grokcore.view.publication import ZopePublicationSansProxy +from zope.app.publication.http import BaseHTTPPublication +from zope.app.publication.requestpublicationfactories import XMLRPCFactory + + +class GrokXMLRPCPublication(ZopePublicationSansProxy, BaseHTTPPublication): + """Combines `BaseHTTPPublication` with the Grok sans-proxy mixin.""" + + +class GrokXMLRPCFactory(XMLRPCFactory): + """Returns the classes Grok uses for browser requests and publication. + + When an instance of this class is called, it returns a 2-element + tuple containing: + + - The request class that Grok uses for XML-RPC requests. + - The publication class that Grok uses to publish to a XML-RPC. + + """ + def __call__(self): + request, publication = super(GrokXMLRPCFactory, self).__call__() + return request, GrokXMLRPCPublication diff --git a/src/grokcore/xmlrpc/testing.py b/src/grokcore/xmlrpc/testing.py index 0e1b469..a60e49c 100644 --- a/src/grokcore/xmlrpc/testing.py +++ b/src/grokcore/xmlrpc/testing.py @@ -13,11 +13,8 @@ ############################################################################## """Grok test helpers """ -import sys from zope.configuration.config import ConfigurationMachine from grokcore.component import zcml -# Provide this import here for BBB reasons: -from grokcore.component.testing import grok_component def grok(module_name): @@ -26,41 +23,6 @@ def grok(module_name): zcml.do_grok('grokcore.security.meta', config) zcml.do_grok('grokcore.view.meta', config) zcml.do_grok('grokcore.view.templatereg', config) - zcml.do_grok('grokcore.viewlet.meta', config) - zcml.do_grok('grokcore.formlib.meta', config) - zcml.do_grok('grokcore.annotation.meta', config) - zcml.do_grok('grokcore.site.meta', config) - zcml.do_grok('grok.meta', config) + zcml.do_grok('grokcore.xmlrpc', config) zcml.do_grok(module_name, config) config.execute_actions() - - -def warn(message, category=None, stacklevel=1): - """Intended to replace warnings.warn in tests. - - Modified copy from zope.deprecation.tests to: - - * make the signature identical to warnings.warn - * to check for *.pyc and *.pyo files. - - When zope.deprecation is fixed, this warn function can be removed again. - """ - print "From grok.testing's warn():" - - frame = sys._getframe(stacklevel) - path = frame.f_globals['__file__'] - if path.endswith('.pyc') or path.endswith('.pyo'): - path = path[:-1] - - file = open(path) - lineno = frame.f_lineno - for i in range(lineno): - line = file.readline() - - print "%s:%s: %s: %s\n %s" % ( - path, - frame.f_lineno, - category.__name__, - message, - line.strip(), - ) diff --git a/src/grokcore/xmlrpc/tests/test_grok.py b/src/grokcore/xmlrpc/tests/test_grok.py index 4edc9c6..27e4976 100644 --- a/src/grokcore/xmlrpc/tests/test_grok.py +++ b/src/grokcore/xmlrpc/tests/test_grok.py @@ -4,8 +4,6 @@ from zope.testing import doctest, cleanup, renormalizing import zope.component.eventtesting -def setUpZope(test): - zope.component.eventtesting.setUp(test) def cleanUpZope(test): cleanup.cleanUp() @@ -29,9 +27,8 @@ def suiteFromPackage(name): if filename == '__init__.py': continue - dottedname = 'grok.tests.%s.%s' % (name, filename[:-3]) + dottedname = 'grokcore.xmlrpc.tests.%s.%s' % (name, filename[:-3]) test = doctest.DocTestSuite(dottedname, - setUp=setUpZope, tearDown=cleanUpZope, checker=checker, optionflags=doctest.ELLIPSIS+ @@ -42,11 +39,7 @@ def suiteFromPackage(name): def test_suite(): suite = unittest.TestSuite() - for name in ['adapter', 'error', 'event', 'security', 'catalog', - 'zcml', 'utility', 'xmlrpc', 'container', 'viewlet', - 'traversal', 'grokker', 'directive', - 'baseclass', 'application', - 'conflict']: + for name in ['xmlrpc']: suite.addTest(suiteFromPackage(name)) return suite diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission.py b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission.py index 6923b69..6f00ddc 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission.py @@ -2,20 +2,24 @@ A permission has to be defined first (using grok.Permission for example) before it can be used in grok.require() in an XMLRPC class. - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) Traceback (most recent call last): ... - ConfigurationExecutionError: martian.error.GrokError: Undefined permission 'doesnt.exist' in . Use grok.Permission first. + ConfigurationExecutionError: martian.error.GrokError: Undefined permission + 'doesnt.exist' in . Use grok.Permission first. ... """ -import grok import zope.interface +import grokcore.xmlrpc +import grokcore.security +import grokcore.component as grok -class MissingPermission(grok.XMLRPC): +class MissingPermission(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) - grok.require('doesnt.exist') + grokcore.security.require('doesnt.exist') def foo(self): pass diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission2.py b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission2.py index 731c22b..eb29287 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission2.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission2.py @@ -2,21 +2,25 @@ A permission has to be defined first (using grok.Permission for example) before it can be used in @grok.require(). - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) Traceback (most recent call last): ... - ConfigurationExecutionError: martian.error.GrokError: Undefined permission 'doesnt.exist' in . Use grok.Permission first. + ConfigurationExecutionError: martian.error.GrokError: Undefined permission + 'doesnt.exist' in . Use grok.Permission first. ... """ -import grok +import grokcore.component as grok +import grokcore.security +import grokcore.xmlrpc import zope.interface -class MissingPermission(grok.XMLRPC): +class MissingPermission(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) - @grok.require('doesnt.exist') + @grokcore.security.require('doesnt.exist') def foo(self): pass diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission3.py b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission3.py index f0a0715..e05b6fe 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission3.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/missing_permission3.py @@ -4,20 +4,23 @@ class. However, this is *not* the the case for a default permission that is never used. - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) """ -import grok +import grokcore.component as grok +import grokcore.security +import grokcore.xmlrpc import zope.interface -class Foo(grok.Permission): +class Foo(grokcore.security.Permission): grok.name('foo') -class MissingPermission(grok.XMLRPC): +class MissingPermission(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) - grok.require('doesnt.exist') + grokcore.security.require('doesnt.exist') - @grok.require(Foo) + @grokcore.security.require(Foo) def foo(self): pass diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/multiple_require.py b/src/grokcore/xmlrpc/tests/xmlrpc/multiple_require.py index 7b96462..0b333c2 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/multiple_require.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/multiple_require.py @@ -1,24 +1,27 @@ """ Multiple calls of grok.require in one class are not allowed. - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) Traceback (most recent call last): ... - GrokError: grok.require was called multiple times in . It may only be set once for a class. + GrokError: grok.require was called multiple times in . It may only be set once for a class. """ -import grok +import grokcore.component as grok +import grokcore.security +import grokcore.xmlrpc import zope.interface -class One(grok.Permission): +class One(grokcore.security.Permission): grok.name('permission.1') -class Two(grok.Permission): +class Two(grokcore.security.Permission): grok.name('permission.2') -class MultipleXMLRPC(grok.XMLRPC): +class MultipleXMLRPC(grokcore.xmlrpc.XMLRPC): grok.context(zope.interface.Interface) - grok.require(One) - grok.require(Two) + grokcore.security.require(One) + grokcore.security.require(Two) def render(self): pass diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/nocontext.py b/src/grokcore/xmlrpc/tests/xmlrpc/nocontext.py index e0c831f..ae16e2e 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/nocontext.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/nocontext.py @@ -3,17 +3,19 @@ Context-determination follows the same rules as for adapters. We just check whether it's hooked up at all: - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) Traceback (most recent call last): ... GrokError: No module-level context for - , please use the + , please use the 'context' directive. """ -import grok +import grokcore.component as grok +import grokcore.xmlrpc -class HomeRPC(grok.XMLRPC): +class HomeRPC(grokcore.xmlrpc.XMLRPC): def foo(self): pass diff --git a/src/grokcore/xmlrpc/tests/xmlrpc/nomethods.py b/src/grokcore/xmlrpc/tests/xmlrpc/nomethods.py index 6c3ff68..f1c3eed 100644 --- a/src/grokcore/xmlrpc/tests/xmlrpc/nomethods.py +++ b/src/grokcore/xmlrpc/tests/xmlrpc/nomethods.py @@ -1,16 +1,18 @@ """ - >>> grok.testing.grok(__name__) + >>> from grokcore.xmlrpc import testing + >>> testing.grok(__name__) Traceback (most recent call last): ... - GrokError: does not + GrokError: does not define any public methods. Please add methods to this class to enable its registration. """ -import grok +import grokcore.component as grok +import grokcore.xmlrpc -class Caveman(grok.Model): +class Caveman(grok.Context): pass -class RemoteCaveman(grok.XMLRPC): +class RemoteCaveman(grokcore.xmlrpc.XMLRPC): pass