Merge lp:~leonardr/lazr.restful/register-operations-when-version-list-is-known into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/register-operations-when-version-list-is-known
Merge into: lp:lazr.restful
Diff against target: 226 lines (+36/-66)
3 files modified
src/lazr/restful/declarations.py (+1/-5)
src/lazr/restful/docs/webservice-declarations.txt (+19/-19)
src/lazr/restful/metazcml.py (+16/-42)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/register-operations-when-version-list-is-known
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+19985@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

The web service registration code runs in the first stage of ZCML processing. Originally, the adapter classes for entries, collections, and named operations were all generated in this first stage. Then came the multi-version code, and there was no way to generate a set of entries without knowing the list of active versions published by the web site.

Unfortunately, the list of active versions is a property of the IWebServiceConfiguration utility, and utilities are not available in the first stage of ZCML processing. So I wrote code that used context.action() to defer the entry generation code until the second stage of ZCML processing, when utilities are available.

The project I'm working on now (no longer exporting mutator methods as named operations) requires that the list of active versions be available when generating the adapters for named operations. This branch does the same trick for named operations as I did earlier for entries: uses context.action() to defer the generation code until the second stage of ZCML processing.

A side effect of this is that when naming the adapters for named operations, we can use the actual name of the first version instead of "__Earliest". I got rid of almost all instances of "__Earliest" in the tests. (There was one I couldn't get rid of, and since that's not the point of my project and it wasn't hurting anything, I've left it alone for now.)

Revision history for this message
Brad Crittenden (bac) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lazr/restful/declarations.py'
2--- src/lazr/restful/declarations.py 2010-02-18 15:17:09 +0000
3+++ src/lazr/restful/declarations.py 2010-02-23 17:50:32 +0000
4@@ -1162,11 +1162,7 @@
5 "version '%s'" % (method.__name__, version))
6 tag = match[0]
7 if version is None:
8- # We need to incorporate the version into a Python class name,
9- # but we won't find out the name of the earliest version until
10- # runtime. Use a generic string that won't conflict with a
11- # real version string.
12- version = "__Earliest"
13+ version = getUtility(IWebServiceConfiguration).active_versions[0]
14
15 bases = (BaseResourceOperationAdapter, )
16 if tag['type'] == 'read_operation':
17
18=== modified file 'src/lazr/restful/docs/webservice-declarations.txt'
19--- src/lazr/restful/docs/webservice-declarations.txt 2010-02-16 16:14:01 +0000
20+++ src/lazr/restful/docs/webservice-declarations.txt 2010-02-23 17:50:32 +0000
21@@ -1060,15 +1060,15 @@
22 >>> IResourceGETOperation.implementedBy(read_method_adapter_factory)
23 True
24
25-The defined adapter is named GET_<interface>_<exported_name>___Earliest
26-and uses the ResourceOperation base class. The "___Earliest" indicates
27+The defined adapter is named GET_<interface>_<exported_name>_beta
28+and uses the ResourceOperation base class. The "_beta" indicates
29 that the adapter will be used in the earliest version of the web
30 service, and any subsequent versions, until a newer implementation
31 supercedes it.
32
33 >>> from lazr.restful import ResourceOperation
34 >>> read_method_adapter_factory.__name__
35- 'GET_IBookSetOnSteroids_searchBooks___Earliest'
36+ 'GET_IBookSetOnSteroids_searchBooks_beta'
37 >>> issubclass(read_method_adapter_factory, ResourceOperation)
38 True
39
40@@ -1129,10 +1129,10 @@
41 >>> IResourcePOSTOperation.implementedBy(write_method_adapter_factory)
42 True
43
44-The generated adapter class name is POST_<interface>_<operation>___Earliest.
45+The generated adapter class name is POST_<interface>_<operation>_beta.
46
47 >>> print write_method_adapter_factory.__name__
48- POST_IBookOnSteroids_checkout___Earliest
49+ POST_IBookOnSteroids_checkout_beta
50
51 The adapter's params property also contains the available parameters
52 (for which there are none in this case.)
53@@ -1178,10 +1178,10 @@
54 False
55
56 The generated adapter class name is also
57-POST_<interface>_<operation>___Earliest.
58+POST_<interface>_<operation>_beta.
59
60 >>> print write_method_adapter_factory.__name__
61- POST_IBookOnSteroids_checkout___Earliest
62+ POST_IBookOnSteroids_checkout_beta
63
64 The adapter's params property also contains the available parameters.
65
66@@ -1253,10 +1253,10 @@
67 True
68
69 The generated adapter class name is
70-DELETE_<interface>_<operation>___Earliest.
71+DELETE_<interface>_<operation>_beta.
72
73 >>> print destructor_method_adapter_factory.__name__
74- DELETE_IBookOnSteroids_destroy___Earliest
75+ DELETE_IBookOnSteroids_destroy_beta
76
77
78 === Destructor ===
79@@ -2038,7 +2038,7 @@
80 >>> method = IMultiVersionMethod['a_method']
81 >>> adapter_earliest_factory = generate_operation_adapter(method, None)
82 >>> print adapter_earliest_factory.__name__
83- GET_IMultiVersionMethod_a_method___Earliest
84+ GET_IMultiVersionMethod_a_method_beta
85
86 >>> method_earliest = adapter_earliest_factory(data_object, request)
87 >>> print method_earliest.call(required="foo")
88@@ -2537,15 +2537,15 @@
89 >>> adapter_registry.lookup(
90 ... (IBookSetOnSteroids, request_interface),
91 ... IResourceGETOperation, 'searchBooks')
92- <class '...GET_IBookSetOnSteroids_searchBooks___Earliest'>
93+ <class '...GET_IBookSetOnSteroids_searchBooks_beta'>
94 >>> adapter_registry.lookup(
95 ... (IBookSetOnSteroids, request_interface),
96 ... IResourcePOSTOperation, 'create_book')
97- <class '...POST_IBookSetOnSteroids_create_book___Earliest'>
98+ <class '...POST_IBookSetOnSteroids_create_book_beta'>
99 >>> adapter_registry.lookup(
100 ... (IBookOnSteroids, request_interface),
101 ... IResourcePOSTOperation, 'checkout')
102- <class '...POST_IBookOnSteroids_checkout___Earliest'>
103+ <class '...POST_IBookOnSteroids_checkout_beta'>
104
105 There is also a 'index.html' view on the IWebServiceClientRequest
106 registered for the InvalidEmail exception.
107@@ -2603,17 +2603,17 @@
108 ... export_as_webservice_entry()
109 ... @operation_parameters(arg=TextLine())
110 ... @export_operation_as('already_been_removed')
111- ... @operation_removed_in_version("1.0")
112+ ... @operation_removed_in_version("2.0")
113 ... @operation_parameters(arg=Float())
114 ... @export_read_operation()
115- ... @operation_for_version("2.0")
116+ ... @operation_for_version("1.0")
117 ... def method(arg):
118 ... """A method."""
119
120 >>> register_test_module('annotatingremoved', AnnotatingARemovedMethod)
121 Traceback (most recent call last):
122 ...
123- ZopeXMLConfigurationError: ...
124- AssertionError: Method "method" contains annotations for version
125- "1.0", even though it's not published in that version. The bad
126- annotations are: "params", "as".
127+ ConfigurationExecutionError: ... Method "method" contains
128+ annotations for version "2.0", even though it's not published in
129+ that version. The bad annotations are: "params", "as".
130+ ...
131
132=== modified file 'src/lazr/restful/metazcml.py'
133--- src/lazr/restful/metazcml.py 2010-02-15 02:24:19 +0000
134+++ src/lazr/restful/metazcml.py 2010-02-23 17:50:32 +0000
135@@ -201,10 +201,13 @@
136 )
137 else:
138 raise AssertionError('Unknown export type: %s' % tag['type'])
139- register_webservice_operations(context, interface)
140-
141-
142-def register_webservice_operations(context, interface):
143+ context.action(
144+ discriminator=('webservice versioned operations', interface),
145+ args=(context, interface),
146+ callable=generate_and_register_webservice_operations)
147+
148+
149+def generate_and_register_webservice_operations(context, interface):
150 """Create and register adapters for all exported methods.
151
152 Different versions of the web service may publish the same
153@@ -218,11 +221,8 @@
154
155 # First, make sure that this method was annotated with the
156 # versions in the right order.
157- context.action(
158- discriminator=('webservice version ordering', interface, method),
159- callable=ensure_correct_version_ordering,
160- args=(interface.__name__ + '.' + method.__name__, tag.dict_names)
161- )
162+ ensure_correct_version_ordering(
163+ interface.__name__ + '.' + method.__name__, tag.dict_names)
164
165 operation_name = None
166 # If an operation's name does not change between version n and
167@@ -294,21 +294,19 @@
168 # version, or if the operation was removed in this
169 # version, we need to block lookups of the previous name
170 # from working.
171- check = (previous_operation_name, previous_operation_provides,
172- operation_name, operation_provides, version, factory)
173 if (operation_name != previous_operation_name
174 and previous_operation_name is not None):
175- _add_versioned_adapter_action(
176- context, interface, previous_operation_provides,
177- previous_operation_name, version,
178- _mask_adapter_registration)
179+ register_adapter_for_version(
180+ _mask_adapter_registration, interface, version,
181+ previous_operation_provides, previous_operation_name,
182+ context.info)
183
184 # If the operation exists in this version (ie. its name is
185 # not None), register it using this version's name.
186 if operation_name is not None:
187- _add_versioned_adapter_action(
188- context, interface, operation_provides, operation_name,
189- version, factory)
190+ register_adapter_for_version(
191+ factory, interface, version, operation_provides,
192+ operation_name, context.info)
193 previous_operation_name = operation_name
194 previous_operation_provides = operation_provides
195
196@@ -322,30 +320,6 @@
197 return None
198
199
200-def _add_versioned_adapter_action(
201- context, interface, provides, name, version, factory):
202- """Helper function to register a versioned operation factory.
203-
204- :param context: The context on which to add a registration action.
205- :param interface: The IEntry subclass that publishes the named
206- operation.
207- :param provides: The IResourceOperation subclass provided by the
208- factory.
209- :param name: The name of the named operation in this version.
210- :param version: The version of the web service that publishes this
211- named operation.
212- :param factory: The object that handles invocations of the named
213- operation.
214- """
215- context.action(
216- discriminator=(
217- 'webservice versioned adapter',
218- (interface, IWebServiceClientRequest), provides, name, version),
219- callable=register_adapter_for_version,
220- args=(factory, interface, version, provides, name, context.info),
221- )
222-
223-
224 def register_exception_view(context, exception):
225 """Register WebServiceExceptionView to handle exception on the webservice.
226 """

Subscribers

People subscribed via source and target branches