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
1=== modified file 'src/lazr/restful/NEWS.txt'
2--- src/lazr/restful/NEWS.txt 2009-08-17 16:22:22 +0000
3+++ src/lazr/restful/NEWS.txt 2009-08-17 20:24:58 +0000
4@@ -2,6 +2,13 @@
5 NEWS for lazr.restful
6 =====================
7
8+Development
9+===========
10+
11+For services that use Django, added an adapter from Django's
12+ObjectDoesNotExist to lazr.restful's NotFoundView.
13+
14+
15 0.9.4 (2009-08-17)
16 ==================
17
18
19=== modified file 'src/lazr/restful/docs/django.txt'
20--- src/lazr/restful/docs/django.txt 2009-08-14 20:25:45 +0000
21+++ src/lazr/restful/docs/django.txt 2009-08-17 20:24:58 +0000
22@@ -68,12 +68,14 @@
23 between IDjangoLocation and ILocation. We'll load it.
24
25 >>> from zope.configuration import xmlconfig
26- >>> zcmlcontext = xmlconfig.string("""
27+ >>> def load_django_config():
28+ ... xmlconfig.string("""
29 ... <configure xmlns="http://namespaces.zope.org/zope">
30 ... <include package="lazr.restful" file="basic-site.zcml" />
31 ... <include package="lazr.restful.frameworks" file="django.zcml" />
32 ... </configure>
33 ... """)
34+ >>> load_django_config()
35
36 Now it's possible to adapt a Django model object to ILocation and
37 check on its __name__.
38@@ -92,3 +94,79 @@
39 >>> request = Request("", {})
40 >>> print str(getMultiAdapter((resource, request), IAbsoluteURL))
41 http://dummy/myname
42+
43+
44+ObjectNotFound
45+==============
46+
47+The django.zcml helper file contains an adapter registration so that
48+Django's ObjectDoesNotExist exceptions are treated as 404 errors.
49+
50+Since lazr.restful does not depend on Django, this test needs to work
51+whether or not Django is installed. If Django is installed, the
52+following code does nothing. If Django is not installed, this code
53+creates a fake django.core.exceptions.ObjectDoesNotExist class that
54+lazr.restful can use.
55+
56+ >>> django_present = False
57+ >>> try:
58+ ... from django.core.exceptions import ObjectDoesNotExist
59+ ... django_present = True
60+ ... except ImportError:
61+ ... class ObjectDoesNotExist(Exception):
62+ ... pass
63+ ...
64+ ... import sys
65+ ... import types
66+ ... previous = None
67+ ... previous_names = []
68+ ...
69+ ... for module_name in 'django.core.exceptions'.split('.'):
70+ ... previous_names.append(module_name)
71+ ... full_name = '.'.join(previous_names)
72+ ... module = types.ModuleType(full_name)
73+ ... if previous is not None:
74+ ... setattr(previous, module_name, module)
75+ ... previous = module
76+ ... sys.modules[module_name] = module
77+ ...
78+ ... import django.core.exceptions
79+ ... django.core.exceptions.ObjectDoesNotExist = ObjectDoesNotExist
80+
81+Now we can act as though this portion of Django was installed.
82+
83+ >>> from django.core.exceptions import ObjectDoesNotExist
84+ >>> exception = ObjectDoesNotExist()
85+
86+Reload the Zope configuration so it'll know about the new class.
87+
88+ >>> load_django_config()
89+
90+Register a simple IWebServiceConfiguration so that the error view will
91+run correctly.
92+
93+ >>> from lazr.restful.interfaces import IWebServiceConfiguration
94+ >>> class SimpleWebServiceConfiguration:
95+ ... implements(IWebServiceConfiguration)
96+ ... show_tracebacks = False
97+ >>> webservice_configuration = SimpleWebServiceConfiguration()
98+ >>> sm.registerUtility(webservice_configuration)
99+
100+That was a lot of work, but now we can instantiate an
101+ObjectDoesNotExist and then get a view for it.
102+
103+ >>> view = getMultiAdapter((exception, request), name="index.html")
104+ >>> view()
105+ ''
106+
107+The view for the ObjectDoesNotExist exception sets the HTTP response
108+code to 404.
109+
110+ >>> request.response.getStatus()
111+ 404
112+
113+Finally, if fake Django modules were created for this test, clean them
114+up.
115+
116+ >>> if not django_present:
117+ ... del(django)
118
119=== modified file 'src/lazr/restful/frameworks/django.zcml'
120--- src/lazr/restful/frameworks/django.zcml 2009-08-14 18:33:04 +0000
121+++ src/lazr/restful/frameworks/django.zcml 2009-08-17 16:51:25 +0000
122@@ -20,4 +20,17 @@
123 factory="zope.traversing.browser.absoluteurl.AbsoluteURL"
124 />
125
126-</configure>
127\ No newline at end of file
128+ <!--We want a raised django.core.exceptions.ObjectDoesNotExist
129+ object to result in a 404 status code. But we don't
130+ control that class definition, so we can't annotate it.
131+ Instead, we define an adapter from that exception class to the
132+ NotFoundView.-->
133+ <adapter
134+ for="django.core.exceptions.ObjectDoesNotExist
135+ lazr.restful.interfaces.IWebServiceClientRequest"
136+ provides="zope.interface.Interface"
137+ factory="lazr.restful.error.NotFoundView"
138+ name="index.html"
139+ />
140+
141+</configure>

Subscribers

People subscribed via source and target branches