Merge lp:~benji/lazr.restful/bug-539070 into lp:lazr.restful

Proposed by Benji York
Status: Merged
Approved by: Edwin Grubbs
Approved revision: 159
Merged at revision: 161
Proposed branch: lp:~benji/lazr.restful/bug-539070
Merge into: lp:lazr.restful
Diff against target: 119 lines (+59/-6)
3 files modified
src/lazr/restful/_resource.py (+18/-3)
src/lazr/restful/tales.py (+9/-2)
src/lazr/restful/tests/test_error.py (+32/-1)
To merge this branch: bzr merge lp:~benji/lazr.restful/bug-539070
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+40003@code.launchpad.net

Description of the change

Bug 539070 describes a case where an error message doesn't give much of a hint as to where to start looking for a problem. This branch enhances the error message with the interface in question so the developer knows where to look.

To post a comment you must log in.
lp:~benji/lazr.restful/bug-539070 updated
157. By Benji York <benji@benji-laptop>

checkpoint

158. By Benji York

finish up reworking the error reporting

159. By Benji York

remove some dead code that snuck in

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Hi Benji,

The changes you made look good for showing which interface attribute tries to set the schema to a class that doesn't have an IEntry adapter. I just have one comment below.

-Edwin

>=== modified file 'src/lazr/restful/tales.py'
>--- src/lazr/restful/tales.py 2010-08-24 18:57:50 +0000
>+++ src/lazr/restful/tales.py 2010-11-04 19:25:16 +0000
>@@ -29,6 +29,7 @@
> from lazr.restful import (
> EntryResource, ResourceJSONEncoder, CollectionResource,
> EntryAdapterUtility, IObjectLink, RESTUtilityBase)
>+from lazr.restful._resource import UnknownEntryAdapter
> from lazr.restful.interfaces import (
> ICollection, ICollectionField, IEntry, IJSONRequestCache,
> IReferenceChoice, IResourceDELETEOperation, IResourceGETOperation,
>@@ -540,8 +541,15 @@
> raise TypeError("Field is not of a supported type.")
> assert schema is not IObject, (
> "Null schema provided for %s" % self.field.__name__)
>- return EntryAdapterUtility.forSchemaInterface(
>- schema, get_current_web_service_request())
>+ try:
>+ return EntryAdapterUtility.forSchemaInterface(
>+ schema, get_current_web_service_request())
>+ except UnknownEntryAdapter, e:
>+ e.whence = (
>+ 'Encountered as a result of the entry interface %r, field %r.'
>+ % (self.field.interface, self.field.getName()))
>+ raise e
>+ return adapter.entry_page_type_link

Can you remove this line that can never be reached?

review: Approve (code)
Revision history for this message
Benji York (benji) wrote :

I removed the dead code. Thanks.

Revision history for this message
Jonathan Lange (jml) wrote :

Thanks so much for working on this change Benji – it promises to save me much hair-pulling in the future.

Perhaps it would also be worth adding a similar try/except/whence thingummy around WadlTopLevelEntryLinkAPI.type_link? (pasted below for convenience)

class WadlTopLevelEntryLinkAPI(RESTUtilityBase):
    """Namespace for WADL functions that operate on top-level entry links."""

    def __init__(self, entry_link):
        self.entry_link = entry_link

    def type_link(self):
        return EntryAdapterUtility.forSchemaInterface(
            self.entry_link.entry_type,
            get_current_web_service_request()).type_link

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/restful/_resource.py'
--- src/lazr/restful/_resource.py 2010-10-25 16:31:09 +0000
+++ src/lazr/restful/_resource.py 2010-11-04 19:39:42 +0000
@@ -2060,6 +2060,22 @@
2060 request)2060 request)
20612061
20622062
2063class UnknownEntryAdapter(Exception):
2064 """Exception that signals no adaper could be found for an interface."""
2065
2066 def __init__(self, adapted_interface_name, version):
2067 self.adapted_interface_name = adapted_interface_name
2068 self.version = version
2069 self.whence = ''
2070
2071 def __str__(self):
2072 message = ("No IEntry adapter found for %s (web service version: %s)."
2073 % (self.adapted_interface_name, self.version))
2074 if self.whence:
2075 message += ' ' + self.whence
2076 return message
2077
2078
2063class EntryAdapterUtility(RESTUtilityBase):2079class EntryAdapterUtility(RESTUtilityBase):
2064 """Useful information about an entry's presence in the web service.2080 """Useful information about an entry's presence in the web service.
20652081
@@ -2078,9 +2094,8 @@
2078 IWebServiceVersion, name=request.version)2094 IWebServiceVersion, name=request.version)
2079 entry_class = getGlobalSiteManager().adapters.lookup(2095 entry_class = getGlobalSiteManager().adapters.lookup(
2080 (entry_interface, request_interface), IEntry)2096 (entry_interface, request_interface), IEntry)
2081 assert entry_class is not None, (2097 if entry_class is None:
2082 ("No IEntry adapter found for %s (web service version: %s)."2098 raise UnknownEntryAdapter(entry_interface.__name__, request.version)
2083 % (entry_interface.__name__, request.version)))
2084 return EntryAdapterUtility(entry_class)2099 return EntryAdapterUtility(entry_class)
20852100
2086 @classmethod2101 @classmethod
20872102
=== modified file 'src/lazr/restful/tales.py'
--- src/lazr/restful/tales.py 2010-08-24 18:57:50 +0000
+++ src/lazr/restful/tales.py 2010-11-04 19:39:42 +0000
@@ -29,6 +29,7 @@
29from lazr.restful import (29from lazr.restful import (
30 EntryResource, ResourceJSONEncoder, CollectionResource,30 EntryResource, ResourceJSONEncoder, CollectionResource,
31 EntryAdapterUtility, IObjectLink, RESTUtilityBase)31 EntryAdapterUtility, IObjectLink, RESTUtilityBase)
32from lazr.restful._resource import UnknownEntryAdapter
32from lazr.restful.interfaces import (33from lazr.restful.interfaces import (
33 ICollection, ICollectionField, IEntry, IJSONRequestCache,34 ICollection, ICollectionField, IEntry, IJSONRequestCache,
34 IReferenceChoice, IResourceDELETEOperation, IResourceGETOperation,35 IReferenceChoice, IResourceDELETEOperation, IResourceGETOperation,
@@ -540,8 +541,14 @@
540 raise TypeError("Field is not of a supported type.")541 raise TypeError("Field is not of a supported type.")
541 assert schema is not IObject, (542 assert schema is not IObject, (
542 "Null schema provided for %s" % self.field.__name__)543 "Null schema provided for %s" % self.field.__name__)
543 return EntryAdapterUtility.forSchemaInterface(544 try:
544 schema, get_current_web_service_request())545 return EntryAdapterUtility.forSchemaInterface(
546 schema, get_current_web_service_request())
547 except UnknownEntryAdapter, e:
548 e.whence = (
549 'Encountered as a result of the entry interface %r, field %r.'
550 % (self.field.interface, self.field.getName()))
551 raise e
545552
546553
547 @property554 @property
548555
=== modified file 'src/lazr/restful/tests/test_error.py'
--- src/lazr/restful/tests/test_error.py 2010-09-06 19:25:17 +0000
+++ src/lazr/restful/tests/test_error.py 2010-11-04 19:39:42 +0000
@@ -10,8 +10,9 @@
1010
11from van.testing.layer import zcml_layer11from van.testing.layer import zcml_layer
12from zope.component import getMultiAdapter12from zope.component import getMultiAdapter
13from zope.interface import Interface
1314
14from lazr.restful._resource import ReadWriteResource15from lazr.restful._resource import ReadWriteResource, UnknownEntryAdapter
15from lazr.restful.error import expose, ClientErrorView, SystemErrorView16from lazr.restful.error import expose, ClientErrorView, SystemErrorView
16from lazr.restful.interfaces import IClientErrorView17from lazr.restful.interfaces import IClientErrorView
17from lazr.restful.testing.webservice import FakeRequest18from lazr.restful.testing.webservice import FakeRequest
@@ -72,6 +73,36 @@
72 self.assertRaises(ValueError, expose, 'x')73 self.assertRaises(ValueError, expose, 'x')
73 self.assertRaises(ValueError, expose, RuntimeError)74 self.assertRaises(ValueError, expose, RuntimeError)
7475
76 def test_missing_adapter(self):
77 # If there is no multi-adapter from the entry interface (IMyEntry) and
78 # a request to IEntry an exception is raised.
79 class IMyEntry(Interface):
80 pass
81
82 # Since the test wants to inspect the exception message (below) we're
83 # not using self.assertRaises).
84 try:
85 EntryAdapterUtility.forSchemaInterface(
86 IMyEntry, self.beta_request)
87 except Exception, e:
88 pass
89 self.assert_(isinstance(e, Exception))
90
91 # The exception's message explains what went wrong.
92 self.assertTrue(str(e),
93 'No IEntry adapter found for IMyEntry (web service version: beta).')
94
95 def test_missing_adapter_whence(self):
96 # The UnknownEntryAdapter exception has a "whence" attribute that
97 # higher-level code can set to give the reader of the exeption message
98 # a hint about where in the code the mistake was made that triggered
99 # the exception.
100 exception = UnknownEntryAdapter('IInterfaceInQuestion', '2.0')
101 exception.whence = 'Encounterd as a result of badness in foo.py.'
102 self.assertEquals(str(exception),
103 'No IEntry adapter found for IInterfaceInQuestion (web service '
104 'version: 2.0). Encounterd as a result of badness in foo.py.')
105
75106
76class FunctionalLayer:107class FunctionalLayer:
77 allow_teardown = False108 allow_teardown = False

Subscribers

People subscribed via source and target branches