[Checkins] SVN: grok/trunk/s Add JSON support to Grok.

Martijn Faassen faassen at infrae.com
Mon Apr 16 14:23:35 EDT 2007


Log message for revision 74192:
  Add JSON support to Grok.
  
  Merge timte-json branch with
  
    svn merge -r 73356:74191 svn+ssh://svn.zope.org/repos/main/grok/branches/timte-json .
  
  ------------------------------------------------------------------------
  r73358 | timt | 2007-03-19 21:54:58 +0100 (Mon, 19 Mar 2007) | 2 lines
  
  Added handling for arguments to the json method.
  
  ------------------------------------------------------------------------
  r73359 | timt | 2007-03-19 22:02:20 +0100 (Mon, 19 Mar 2007) | 2 lines
  
  added comments
  
  ------------------------------------------------------------------------
  r73961 | timt | 2007-04-01 11:19:50 +0200 (Sun, 01 Apr 2007) | 2 lines
  
  Added tests for json support.
  
  ------------------------------------------------------------------------
  r74181 | faassen | 2007-04-16 18:20:17 +0200 (Mon, 16 Apr 2007) | 2 lines
  
  Require simplejson. this is now a dependency of grok.
  
  ------------------------------------------------------------------------
  r74185 | faassen | 2007-04-16 18:32:01 +0200 (Mon, 16 Apr 2007) | 2 lines
  
  Extend this test a little.
  
  ------------------------------------------------------------------------
  r74186 | faassen | 2007-04-16 18:32:19 +0200 (Mon, 16 Apr 2007) | 2 lines
  
  Add another some more security-related tests.
  
  ------------------------------------------------------------------------
  r74190 | faassen | 2007-04-16 20:09:08 +0200 (Mon, 16 Apr 2007) | 1 line
  
  Whitespace.
  ------------------------------------------------------------------------
  r74191 | faassen | 2007-04-16 20:10:42 +0200 (Mon, 16 Apr 2007) | 8 lines
  
  After the JSON view got added, I noticed a lot of code redundant 
  with both View and XMLRPC. I've refactored this a bit so more
  code is reused.
  
  In addition, some checks for the existence of a permission were
  not taking place with XMLRPC and JSON views. Tests have been
  added and we now make sure we do the same check there too.
  
  ------------------------------------------------------------------------
  
  

Changed:
  U   grok/trunk/setup.py
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/components.py
  A   grok/trunk/src/grok/ftests/security/json.py
  U   grok/trunk/src/grok/meta.py
  A   grok/trunk/src/grok/tests/json/
  U   grok/trunk/src/grok/tests/security/missing_permission.py
  A   grok/trunk/src/grok/tests/security/missing_permission_json.py
  A   grok/trunk/src/grok/tests/security/missing_permission_json2.py
  A   grok/trunk/src/grok/tests/security/missing_permission_xmlrpc.py
  A   grok/trunk/src/grok/tests/security/missing_permission_xmlrpc2.py
  A   grok/trunk/src/grok/tests/security/missing_permission_xmlrpc3.py
  U   grok/trunk/src/grok/tests/security/multiple_require.py
  A   grok/trunk/src/grok/tests/security/multiple_require_json.py
  U   grok/trunk/src/grok/tests/security/multiple_require_xmlrpc.py
  U   grok/trunk/src/grok/tests/test_grok.py
  U   grok/trunk/src/grok/util.py

-=-
Modified: grok/trunk/setup.py
===================================================================
--- grok/trunk/setup.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/setup.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -15,5 +15,6 @@
     package_dir = {'': 'src'},
     include_package_data = True,
     zip_safe=False,    
-    install_requires=['setuptools'],
+    install_requires=['setuptools',
+                      'simplejson'],
 )

Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/__init__.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -30,7 +30,7 @@
     IContainerModifiedEvent, ContainerModifiedEvent)
 
 from grok.components import ClassGrokker, InstanceGrokker, ModuleGrokker
-from grok.components import Model, Adapter, MultiAdapter, View, XMLRPC
+from grok.components import Model, Adapter, MultiAdapter, View, XMLRPC, JSON
 from grok.components import PageTemplate, PageTemplateFile, Container, Traverser
 from grok.components import Site, GlobalUtility, LocalUtility, Annotation
 from grok.components import Application, Form, AddForm, EditForm, DisplayForm

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/components.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -17,6 +17,7 @@
 import os
 import persistent
 import urllib
+import simplejson
 
 from zope import component
 from zope import interface
@@ -220,7 +221,14 @@
 class XMLRPC(object):
     pass
 
+class JSON(BrowserPage):
 
+    def __call__(self):
+        view_name = self.__view_name__
+        method = getattr(self, view_name)
+        method_result = mapply(method, (), self.request)
+        return simplejson.dumps(method_result)
+
 class GrokPageTemplate(object):
 
     def __repr__(self):

Copied: grok/trunk/src/grok/ftests/security/json.py (from rev 74191, grok/branches/timte-json/src/grok/ftests/security/json.py)

Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/meta.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -6,7 +6,6 @@
                                                IBrowserRequest,
                                                IBrowserPublisher)
 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
-from zope.security.checker import NamesChecker, defineChecker
 from zope.security.permission import Permission
 from zope.security.interfaces import IPermission
 from zope.annotation.interfaces import IAnnotations
@@ -83,18 +82,8 @@
         # the outside -- need to discuss how to restrict such things.
         methods = util.methods_from_class(factory)
 
-        # Determine the default permission for the XMLRPC methods.
-        # There can only be 0 or 1 of those.
-        permissions = util.class_annotation(factory, 'grok.require', [])
-        if not permissions:
-            default_permission = None
-        elif len(permissions) == 1:
-            default_permission = permissions[0]
-        else:
-            raise GrokError('grok.require was called multiple times in '
-                            '%r. It may only be called once on class level.'
-                            % factory, factory)
-
+        default_permission = util.get_default_permission(factory)
+        
         for method in methods:
             # Make sure that the class inherits MethodPublisher, so that the
             # views have a location
@@ -110,14 +99,10 @@
             # Protect method_view with either the permission that was
             # set on the method, the default permission from the class
             # level or zope.Public.
-            permission = getattr(method, '__grok_require__', default_permission)
-            if permission is None or permission == 'zope.Public':
-                checker = NamesChecker(['__call__'])
-            else:
-                checker = NamesChecker(['__call__'], permission)
-            defineChecker(method_view, checker)
-
-
+            permission = getattr(method, '__grok_require__',
+                                 default_permission)
+            util.make_checker(factory, method_view, permission)
+    
 class ViewGrokker(grok.ClassGrokker):
     component_class = grok.View
 
@@ -181,25 +166,9 @@
                                  name=view_name)
 
         # protect view, public by default
-        permissions = util.class_annotation(factory, 'grok.require', [])
-        if not permissions:
-            checker = NamesChecker(['__call__'])
-        elif len(permissions) > 1:
-            raise GrokError('grok.require was called multiple times in view '
-                            '%r. It may only be called once.' % factory,
-                            factory)
-        elif permissions[0] == 'zope.Public':
-            checker = NamesChecker(['__call__'])
-        else:
-            perm = permissions[0]
-            if component.queryUtility(IPermission, name=perm) is None:
-                raise GrokError('Undefined permission %r in view %r. Use '
-                                'grok.define_permission first.'
-                                % (perm, factory), factory)
-            checker = NamesChecker(['__call__'], permissions[0])
-
-        defineChecker(factory, checker)
-
+        default_permission = util.get_default_permission(factory)
+        util.make_checker(factory, factory, default_permission)
+    
         # safety belt: make sure that the programmer didn't use
         # @grok.require on any of the view's methods.
         methods = util.methods_from_class(factory)
@@ -211,6 +180,35 @@
                                 % (method.__name__, factory), factory)
 
 
+class JSONGrokker(grok.ClassGrokker):
+    component_class = grok.JSON
+
+    def register(self, context, name, factory, module_info, templates):
+        view_context = util.determine_class_context(factory, context)
+        methods = util.methods_from_class(factory)
+
+        default_permission = util.get_default_permission(factory)
+        
+        for method in methods:
+            # Create a new class with a __view_name__ attribute so the
+            # JSON class knows what method to call.
+            method_view = type(
+                factory.__name__, (factory,),
+                {'__view_name__': method.__name__}
+                )
+            component.provideAdapter(
+                method_view, (view_context, IDefaultBrowserLayer),
+                interface.Interface,
+                name=method.__name__)
+
+            # Protect method_view with either the permission that was
+            # set on the method, the default permission from the class
+            # level or zope.Public.
+
+            permission = getattr(method, '__grok_require__',
+                                 default_permission)
+            util.make_checker(factory, method_view, permission)
+
 class TraverserGrokker(grok.ClassGrokker):
     component_class = grok.Traverser
 

Copied: grok/trunk/src/grok/tests/json (from rev 74191, grok/branches/timte-json/src/grok/tests/json)

Modified: grok/trunk/src/grok/tests/security/missing_permission.py
===================================================================
--- grok/trunk/src/grok/tests/security/missing_permission.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/tests/security/missing_permission.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -4,7 +4,7 @@
 
   >>> grok.grok(__name__)
   Traceback (most recent call last):
-  GrokError: Undefined permission 'doesnt.exist' in view <class 'grok.tests.security.missing_permission.MissingPermission'>. Use grok.define_permission first.
+  GrokError: Undefined permission 'doesnt.exist' in <class 'grok.tests.security.missing_permission.MissingPermission'>. Use grok.define_permission first.
 
 """
 
@@ -17,3 +17,4 @@
 
     def render(self):
         pass
+

Copied: grok/trunk/src/grok/tests/security/missing_permission_json.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/missing_permission_json.py)

Copied: grok/trunk/src/grok/tests/security/missing_permission_json2.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/missing_permission_json2.py)

Copied: grok/trunk/src/grok/tests/security/missing_permission_xmlrpc.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/missing_permission_xmlrpc.py)

Copied: grok/trunk/src/grok/tests/security/missing_permission_xmlrpc2.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/missing_permission_xmlrpc2.py)

Copied: grok/trunk/src/grok/tests/security/missing_permission_xmlrpc3.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/missing_permission_xmlrpc3.py)

Modified: grok/trunk/src/grok/tests/security/multiple_require.py
===================================================================
--- grok/trunk/src/grok/tests/security/multiple_require.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/tests/security/multiple_require.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -3,7 +3,8 @@
 
   >>> grok.grok(__name__)
   Traceback (most recent call last):
-  GrokError: grok.require was called multiple times in view <class 'grok.tests.security.multiple_require.MultipleView'>. It may only be called once.
+    ...
+  GrokError: grok.require was called multiple times in <class 'grok.tests.security.multiple_require.MultipleView'>. It may only be set once for a class.
 
 """
 import grok

Copied: grok/trunk/src/grok/tests/security/multiple_require_json.py (from rev 74191, grok/branches/timte-json/src/grok/tests/security/multiple_require_json.py)

Modified: grok/trunk/src/grok/tests/security/multiple_require_xmlrpc.py
===================================================================
--- grok/trunk/src/grok/tests/security/multiple_require_xmlrpc.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/tests/security/multiple_require_xmlrpc.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -3,8 +3,8 @@
 
   >>> grok.grok(__name__)
   Traceback (most recent call last):
-  GrokError: grok.require was called multiple times in <class 'grok.tests.security.multiple_require_xmlrpc.MultipleXMLRPC'>. It may only be called once on class level.
-
+     ...
+  GrokError: grok.require was called multiple times in <class 'grok.tests.security.multiple_require_xmlrpc.MultipleXMLRPC'>. It may only be set once for a class.
 """
 import grok
 import zope.interface

Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/tests/test_grok.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -33,7 +33,7 @@
 def test_suite():
     suite = unittest.TestSuite()
     for name in ['adapter', 'error', 'view', 'scan', 'event', 'security',
-                 'zcml', 'static', 'utility', 'xmlrpc', 'container',
+                 'zcml', 'static', 'utility', 'xmlrpc', 'json', 'container',
                  'traversal', 'form', 'site', 'grokker', 'directive', 'util',
                  'baseclass', 'annotation', 'application']:
         suite.addTest(suiteFromPackage(name))

Modified: grok/trunk/src/grok/util.py
===================================================================
--- grok/trunk/src/grok/util.py	2007-04-16 18:10:42 UTC (rev 74191)
+++ grok/trunk/src/grok/util.py	2007-04-16 18:23:34 UTC (rev 74192)
@@ -22,6 +22,9 @@
 from zope import component
 from zope import interface
 
+from zope.security.checker import NamesChecker, defineChecker
+from zope.security.interfaces import IPermission
+
 from grok.error import GrokError, GrokImportError
 
 def not_unicode_or_ascii(value):
@@ -144,3 +147,47 @@
                   if name != '__provides__' ]
     methods = [c for c in candidates if inspect.ismethod(c)]
     return methods
+
+def make_checker(factory, view_factory, permission):
+    """Make a checker for a view_factory associated with factory.
+
+    These could be one and the same for normal views, or different
+    in case we make method-based views such as for JSON and XMLRPC.
+    """
+    if permission is not None:
+        check_permission(factory, permission)
+    if permission is None or permission == 'zope.Public':
+        checker = NamesChecker(['__call__'])
+    else:
+        checker = NamesChecker(['__call__'], permission)
+    defineChecker(view_factory, checker)
+
+def check_permission(factory, permission):
+    """Check whether a permission is defined.
+
+    If not, raise error for factory.
+    """
+    if component.queryUtility(IPermission,
+                              name=permission) is None:
+       raise GrokError('Undefined permission %r in %r. Use '
+                       'grok.define_permission first.'
+                       % (permission, factory), factory)
+
+def get_default_permission(factory):
+    """Determine the default permission for a view.
+    
+    There can be only 0 or 1 default permission.
+    """
+    permissions = class_annotation(factory, 'grok.require', [])
+    if not permissions:
+        return None
+    if len(permissions) > 1:
+        raise GrokError('grok.require was called multiple times in '
+                        '%r. It may only be set once for a class.'
+                        % factory, factory)
+
+    result = permissions[0]
+    check_permission(factory, result)
+    return result
+
+



More information about the Checkins mailing list