Merge lp:~leonardr/lazr.restful/django-helpers into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Approved by: Barry Warsaw
Approved revision: 55
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/django-helpers
Merge into: lp:lazr.restful
Diff against target: None lines
To merge this branch: bzr merge lp:~leonardr/lazr.restful/django-helpers
Reviewer Review Type Date Requested Status
Barry Warsaw code Approve
Review via email: mp+10274@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

This branch adds a tiny ZCML snippet to frameworks/django.zcml that makes Django "object not found" exceptions show up in the web service as 404 errors.

The biggest part of this code is the test, which creates a temporary fake Django module hierarchy. This is based on code Gary gave me, and the code should work whether or not Django is installed.

Revision history for this message
Barry Warsaw (barry) wrote :

Looks good to me. As we talked about in IRC, it would be good to register the same kind of view on zope.publisher.interfaces.NotFound so that custom hacks in handleException aren't necessary.

review: Approve (code)
Revision history for this message
Gary Poster (gary) wrote :

On Aug 17, 2009, at 4:45 PM, Leonard Richardson wrote:
>
> +Finally, if fake Django modules were created for this test, clean
> them
> +up.
> +
> + >>> if not django_present:
> + ... del(django)

Oops, sorry, I wasn't clear.

This doesn't do anything important.

You need to delete all the keys that you stuffed into sys.modules.
That's the more important bit (to the degree that this clean up is
important at all).

Gary

Revision history for this message
Barry Warsaw (barry) wrote :

>
> On Aug 17, 2009, at 4:45 PM, Leonard Richardson wrote:
> >
> > +Finally, if fake Django modules were created for this test, clean
> > them
> > +up.
> > +
> > + >>> if not django_present:
> > + ... del(django)
>
> Oops, sorry, I wasn't clear.
>
> This doesn't do anything important.
>
> You need to delete all the keys that you stuffed into sys.modules.
> That's the more important bit (to the degree that this clean up is
> important at all).

It's probably not. It can't hurt, but you're right it probably doesn't help at all either. Might as well remove that code since it's just test framework noise.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/restful/NEWS.txt'
--- src/lazr/restful/NEWS.txt 2009-08-17 16:22:22 +0000
+++ src/lazr/restful/NEWS.txt 2009-08-17 20:24:58 +0000
@@ -2,6 +2,13 @@
2NEWS for lazr.restful2NEWS for lazr.restful
3=====================3=====================
44
5Development
6===========
7
8For services that use Django, added an adapter from Django's
9ObjectDoesNotExist to lazr.restful's NotFoundView.
10
11
50.9.4 (2009-08-17)120.9.4 (2009-08-17)
6==================13==================
714
815
=== modified file 'src/lazr/restful/docs/django.txt'
--- src/lazr/restful/docs/django.txt 2009-08-14 20:25:45 +0000
+++ src/lazr/restful/docs/django.txt 2009-08-17 20:24:58 +0000
@@ -68,12 +68,14 @@
68between IDjangoLocation and ILocation. We'll load it.68between IDjangoLocation and ILocation. We'll load it.
6969
70 >>> from zope.configuration import xmlconfig70 >>> from zope.configuration import xmlconfig
71 >>> zcmlcontext = xmlconfig.string("""71 >>> def load_django_config():
72 ... xmlconfig.string("""
72 ... <configure xmlns="http://namespaces.zope.org/zope">73 ... <configure xmlns="http://namespaces.zope.org/zope">
73 ... <include package="lazr.restful" file="basic-site.zcml" />74 ... <include package="lazr.restful" file="basic-site.zcml" />
74 ... <include package="lazr.restful.frameworks" file="django.zcml" />75 ... <include package="lazr.restful.frameworks" file="django.zcml" />
75 ... </configure>76 ... </configure>
76 ... """)77 ... """)
78 >>> load_django_config()
7779
78Now it's possible to adapt a Django model object to ILocation and80Now it's possible to adapt a Django model object to ILocation and
79check on its __name__.81check on its __name__.
@@ -92,3 +94,79 @@
92 >>> request = Request("", {})94 >>> request = Request("", {})
93 >>> print str(getMultiAdapter((resource, request), IAbsoluteURL))95 >>> print str(getMultiAdapter((resource, request), IAbsoluteURL))
94 http://dummy/myname96 http://dummy/myname
97
98
99ObjectNotFound
100==============
101
102The django.zcml helper file contains an adapter registration so that
103Django's ObjectDoesNotExist exceptions are treated as 404 errors.
104
105Since lazr.restful does not depend on Django, this test needs to work
106whether or not Django is installed. If Django is installed, the
107following code does nothing. If Django is not installed, this code
108creates a fake django.core.exceptions.ObjectDoesNotExist class that
109lazr.restful can use.
110
111 >>> django_present = False
112 >>> try:
113 ... from django.core.exceptions import ObjectDoesNotExist
114 ... django_present = True
115 ... except ImportError:
116 ... class ObjectDoesNotExist(Exception):
117 ... pass
118 ...
119 ... import sys
120 ... import types
121 ... previous = None
122 ... previous_names = []
123 ...
124 ... for module_name in 'django.core.exceptions'.split('.'):
125 ... previous_names.append(module_name)
126 ... full_name = '.'.join(previous_names)
127 ... module = types.ModuleType(full_name)
128 ... if previous is not None:
129 ... setattr(previous, module_name, module)
130 ... previous = module
131 ... sys.modules[module_name] = module
132 ...
133 ... import django.core.exceptions
134 ... django.core.exceptions.ObjectDoesNotExist = ObjectDoesNotExist
135
136Now we can act as though this portion of Django was installed.
137
138 >>> from django.core.exceptions import ObjectDoesNotExist
139 >>> exception = ObjectDoesNotExist()
140
141Reload the Zope configuration so it'll know about the new class.
142
143 >>> load_django_config()
144
145Register a simple IWebServiceConfiguration so that the error view will
146run correctly.
147
148 >>> from lazr.restful.interfaces import IWebServiceConfiguration
149 >>> class SimpleWebServiceConfiguration:
150 ... implements(IWebServiceConfiguration)
151 ... show_tracebacks = False
152 >>> webservice_configuration = SimpleWebServiceConfiguration()
153 >>> sm.registerUtility(webservice_configuration)
154
155That was a lot of work, but now we can instantiate an
156ObjectDoesNotExist and then get a view for it.
157
158 >>> view = getMultiAdapter((exception, request), name="index.html")
159 >>> view()
160 ''
161
162The view for the ObjectDoesNotExist exception sets the HTTP response
163code to 404.
164
165 >>> request.response.getStatus()
166 404
167
168Finally, if fake Django modules were created for this test, clean them
169up.
170
171 >>> if not django_present:
172 ... del(django)
95173
=== modified file 'src/lazr/restful/frameworks/django.zcml'
--- src/lazr/restful/frameworks/django.zcml 2009-08-14 18:33:04 +0000
+++ src/lazr/restful/frameworks/django.zcml 2009-08-17 16:51:25 +0000
@@ -20,4 +20,17 @@
20 factory="zope.traversing.browser.absoluteurl.AbsoluteURL"20 factory="zope.traversing.browser.absoluteurl.AbsoluteURL"
21 />21 />
2222
23</configure>
24\ No newline at end of file23\ No newline at end of file
24 <!--We want a raised django.core.exceptions.ObjectDoesNotExist
25 object to result in a 404 status code. But we don't
26 control that class definition, so we can't annotate it.
27 Instead, we define an adapter from that exception class to the
28 NotFoundView.-->
29 <adapter
30 for="django.core.exceptions.ObjectDoesNotExist
31 lazr.restful.interfaces.IWebServiceClientRequest"
32 provides="zope.interface.Interface"
33 factory="lazr.restful.error.NotFoundView"
34 name="index.html"
35 />
36
37</configure>

Subscribers

People subscribed via source and target branches