Merge lp:~leonardr/lazr.restful/register-operations-when-version-list-is-known into lp:lazr.restful
- register-operations-when-version-list-is-known
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | code | Approve | |
Review via email: mp+19985@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote : | # |
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 | 1162 | "version '%s'" % (method.__name__, version)) | 1162 | "version '%s'" % (method.__name__, version)) |
6 | 1163 | tag = match[0] | 1163 | tag = match[0] |
7 | 1164 | if version is None: | 1164 | if version is None: |
13 | 1165 | # We need to incorporate the version into a Python class name, | 1165 | version = getUtility(IWebServiceConfiguration).active_versions[0] |
9 | 1166 | # but we won't find out the name of the earliest version until | ||
10 | 1167 | # runtime. Use a generic string that won't conflict with a | ||
11 | 1168 | # real version string. | ||
12 | 1169 | version = "__Earliest" | ||
14 | 1170 | 1166 | ||
15 | 1171 | bases = (BaseResourceOperationAdapter, ) | 1167 | bases = (BaseResourceOperationAdapter, ) |
16 | 1172 | if tag['type'] == 'read_operation': | 1168 | if tag['type'] == 'read_operation': |
17 | 1173 | 1169 | ||
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 | 1060 | >>> IResourceGETOperation.implementedBy(read_method_adapter_factory) | 1060 | >>> IResourceGETOperation.implementedBy(read_method_adapter_factory) |
23 | 1061 | True | 1061 | True |
24 | 1062 | 1062 | ||
27 | 1063 | The defined adapter is named GET_<interface>_<exported_name>___Earliest | 1063 | The defined adapter is named GET_<interface>_<exported_name>_beta |
28 | 1064 | and uses the ResourceOperation base class. The "___Earliest" indicates | 1064 | and uses the ResourceOperation base class. The "_beta" indicates |
29 | 1065 | that the adapter will be used in the earliest version of the web | 1065 | that the adapter will be used in the earliest version of the web |
30 | 1066 | service, and any subsequent versions, until a newer implementation | 1066 | service, and any subsequent versions, until a newer implementation |
31 | 1067 | supercedes it. | 1067 | supercedes it. |
32 | 1068 | 1068 | ||
33 | 1069 | >>> from lazr.restful import ResourceOperation | 1069 | >>> from lazr.restful import ResourceOperation |
34 | 1070 | >>> read_method_adapter_factory.__name__ | 1070 | >>> read_method_adapter_factory.__name__ |
36 | 1071 | 'GET_IBookSetOnSteroids_searchBooks___Earliest' | 1071 | 'GET_IBookSetOnSteroids_searchBooks_beta' |
37 | 1072 | >>> issubclass(read_method_adapter_factory, ResourceOperation) | 1072 | >>> issubclass(read_method_adapter_factory, ResourceOperation) |
38 | 1073 | True | 1073 | True |
39 | 1074 | 1074 | ||
40 | @@ -1129,10 +1129,10 @@ | |||
41 | 1129 | >>> IResourcePOSTOperation.implementedBy(write_method_adapter_factory) | 1129 | >>> IResourcePOSTOperation.implementedBy(write_method_adapter_factory) |
42 | 1130 | True | 1130 | True |
43 | 1131 | 1131 | ||
45 | 1132 | The generated adapter class name is POST_<interface>_<operation>___Earliest. | 1132 | The generated adapter class name is POST_<interface>_<operation>_beta. |
46 | 1133 | 1133 | ||
47 | 1134 | >>> print write_method_adapter_factory.__name__ | 1134 | >>> print write_method_adapter_factory.__name__ |
49 | 1135 | POST_IBookOnSteroids_checkout___Earliest | 1135 | POST_IBookOnSteroids_checkout_beta |
50 | 1136 | 1136 | ||
51 | 1137 | The adapter's params property also contains the available parameters | 1137 | The adapter's params property also contains the available parameters |
52 | 1138 | (for which there are none in this case.) | 1138 | (for which there are none in this case.) |
53 | @@ -1178,10 +1178,10 @@ | |||
54 | 1178 | False | 1178 | False |
55 | 1179 | 1179 | ||
56 | 1180 | The generated adapter class name is also | 1180 | The generated adapter class name is also |
58 | 1181 | POST_<interface>_<operation>___Earliest. | 1181 | POST_<interface>_<operation>_beta. |
59 | 1182 | 1182 | ||
60 | 1183 | >>> print write_method_adapter_factory.__name__ | 1183 | >>> print write_method_adapter_factory.__name__ |
62 | 1184 | POST_IBookOnSteroids_checkout___Earliest | 1184 | POST_IBookOnSteroids_checkout_beta |
63 | 1185 | 1185 | ||
64 | 1186 | The adapter's params property also contains the available parameters. | 1186 | The adapter's params property also contains the available parameters. |
65 | 1187 | 1187 | ||
66 | @@ -1253,10 +1253,10 @@ | |||
67 | 1253 | True | 1253 | True |
68 | 1254 | 1254 | ||
69 | 1255 | The generated adapter class name is | 1255 | The generated adapter class name is |
71 | 1256 | DELETE_<interface>_<operation>___Earliest. | 1256 | DELETE_<interface>_<operation>_beta. |
72 | 1257 | 1257 | ||
73 | 1258 | >>> print destructor_method_adapter_factory.__name__ | 1258 | >>> print destructor_method_adapter_factory.__name__ |
75 | 1259 | DELETE_IBookOnSteroids_destroy___Earliest | 1259 | DELETE_IBookOnSteroids_destroy_beta |
76 | 1260 | 1260 | ||
77 | 1261 | 1261 | ||
78 | 1262 | === Destructor === | 1262 | === Destructor === |
79 | @@ -2038,7 +2038,7 @@ | |||
80 | 2038 | >>> method = IMultiVersionMethod['a_method'] | 2038 | >>> method = IMultiVersionMethod['a_method'] |
81 | 2039 | >>> adapter_earliest_factory = generate_operation_adapter(method, None) | 2039 | >>> adapter_earliest_factory = generate_operation_adapter(method, None) |
82 | 2040 | >>> print adapter_earliest_factory.__name__ | 2040 | >>> print adapter_earliest_factory.__name__ |
84 | 2041 | GET_IMultiVersionMethod_a_method___Earliest | 2041 | GET_IMultiVersionMethod_a_method_beta |
85 | 2042 | 2042 | ||
86 | 2043 | >>> method_earliest = adapter_earliest_factory(data_object, request) | 2043 | >>> method_earliest = adapter_earliest_factory(data_object, request) |
87 | 2044 | >>> print method_earliest.call(required="foo") | 2044 | >>> print method_earliest.call(required="foo") |
88 | @@ -2537,15 +2537,15 @@ | |||
89 | 2537 | >>> adapter_registry.lookup( | 2537 | >>> adapter_registry.lookup( |
90 | 2538 | ... (IBookSetOnSteroids, request_interface), | 2538 | ... (IBookSetOnSteroids, request_interface), |
91 | 2539 | ... IResourceGETOperation, 'searchBooks') | 2539 | ... IResourceGETOperation, 'searchBooks') |
93 | 2540 | <class '...GET_IBookSetOnSteroids_searchBooks___Earliest'> | 2540 | <class '...GET_IBookSetOnSteroids_searchBooks_beta'> |
94 | 2541 | >>> adapter_registry.lookup( | 2541 | >>> adapter_registry.lookup( |
95 | 2542 | ... (IBookSetOnSteroids, request_interface), | 2542 | ... (IBookSetOnSteroids, request_interface), |
96 | 2543 | ... IResourcePOSTOperation, 'create_book') | 2543 | ... IResourcePOSTOperation, 'create_book') |
98 | 2544 | <class '...POST_IBookSetOnSteroids_create_book___Earliest'> | 2544 | <class '...POST_IBookSetOnSteroids_create_book_beta'> |
99 | 2545 | >>> adapter_registry.lookup( | 2545 | >>> adapter_registry.lookup( |
100 | 2546 | ... (IBookOnSteroids, request_interface), | 2546 | ... (IBookOnSteroids, request_interface), |
101 | 2547 | ... IResourcePOSTOperation, 'checkout') | 2547 | ... IResourcePOSTOperation, 'checkout') |
103 | 2548 | <class '...POST_IBookOnSteroids_checkout___Earliest'> | 2548 | <class '...POST_IBookOnSteroids_checkout_beta'> |
104 | 2549 | 2549 | ||
105 | 2550 | There is also a 'index.html' view on the IWebServiceClientRequest | 2550 | There is also a 'index.html' view on the IWebServiceClientRequest |
106 | 2551 | registered for the InvalidEmail exception. | 2551 | registered for the InvalidEmail exception. |
107 | @@ -2603,17 +2603,17 @@ | |||
108 | 2603 | ... export_as_webservice_entry() | 2603 | ... export_as_webservice_entry() |
109 | 2604 | ... @operation_parameters(arg=TextLine()) | 2604 | ... @operation_parameters(arg=TextLine()) |
110 | 2605 | ... @export_operation_as('already_been_removed') | 2605 | ... @export_operation_as('already_been_removed') |
112 | 2606 | ... @operation_removed_in_version("1.0") | 2606 | ... @operation_removed_in_version("2.0") |
113 | 2607 | ... @operation_parameters(arg=Float()) | 2607 | ... @operation_parameters(arg=Float()) |
114 | 2608 | ... @export_read_operation() | 2608 | ... @export_read_operation() |
116 | 2609 | ... @operation_for_version("2.0") | 2609 | ... @operation_for_version("1.0") |
117 | 2610 | ... def method(arg): | 2610 | ... def method(arg): |
118 | 2611 | ... """A method.""" | 2611 | ... """A method.""" |
119 | 2612 | 2612 | ||
120 | 2613 | >>> register_test_module('annotatingremoved', AnnotatingARemovedMethod) | 2613 | >>> register_test_module('annotatingremoved', AnnotatingARemovedMethod) |
121 | 2614 | Traceback (most recent call last): | 2614 | Traceback (most recent call last): |
122 | 2615 | ... | 2615 | ... |
127 | 2616 | ZopeXMLConfigurationError: ... | 2616 | ConfigurationExecutionError: ... Method "method" contains |
128 | 2617 | AssertionError: Method "method" contains annotations for version | 2617 | annotations for version "2.0", even though it's not published in |
129 | 2618 | "1.0", even though it's not published in that version. The bad | 2618 | that version. The bad annotations are: "params", "as". |
130 | 2619 | annotations are: "params", "as". | 2619 | ... |
131 | 2620 | 2620 | ||
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 | 201 | ) | 201 | ) |
137 | 202 | else: | 202 | else: |
138 | 203 | raise AssertionError('Unknown export type: %s' % tag['type']) | 203 | raise AssertionError('Unknown export type: %s' % tag['type']) |
143 | 204 | register_webservice_operations(context, interface) | 204 | context.action( |
144 | 205 | 205 | discriminator=('webservice versioned operations', interface), | |
145 | 206 | 206 | args=(context, interface), | |
146 | 207 | def register_webservice_operations(context, interface): | 207 | callable=generate_and_register_webservice_operations) |
147 | 208 | |||
148 | 209 | |||
149 | 210 | def generate_and_register_webservice_operations(context, interface): | ||
150 | 208 | """Create and register adapters for all exported methods. | 211 | """Create and register adapters for all exported methods. |
151 | 209 | 212 | ||
152 | 210 | Different versions of the web service may publish the same | 213 | Different versions of the web service may publish the same |
153 | @@ -218,11 +221,8 @@ | |||
154 | 218 | 221 | ||
155 | 219 | # First, make sure that this method was annotated with the | 222 | # First, make sure that this method was annotated with the |
156 | 220 | # versions in the right order. | 223 | # versions in the right order. |
162 | 221 | context.action( | 224 | ensure_correct_version_ordering( |
163 | 222 | discriminator=('webservice version ordering', interface, method), | 225 | interface.__name__ + '.' + method.__name__, tag.dict_names) |
159 | 223 | callable=ensure_correct_version_ordering, | ||
160 | 224 | args=(interface.__name__ + '.' + method.__name__, tag.dict_names) | ||
161 | 225 | ) | ||
164 | 226 | 226 | ||
165 | 227 | operation_name = None | 227 | operation_name = None |
166 | 228 | # If an operation's name does not change between version n and | 228 | # If an operation's name does not change between version n and |
167 | @@ -294,21 +294,19 @@ | |||
168 | 294 | # version, or if the operation was removed in this | 294 | # version, or if the operation was removed in this |
169 | 295 | # version, we need to block lookups of the previous name | 295 | # version, we need to block lookups of the previous name |
170 | 296 | # from working. | 296 | # from working. |
171 | 297 | check = (previous_operation_name, previous_operation_provides, | ||
172 | 298 | operation_name, operation_provides, version, factory) | ||
173 | 299 | if (operation_name != previous_operation_name | 297 | if (operation_name != previous_operation_name |
174 | 300 | and previous_operation_name is not None): | 298 | and previous_operation_name is not None): |
179 | 301 | _add_versioned_adapter_action( | 299 | register_adapter_for_version( |
180 | 302 | context, interface, previous_operation_provides, | 300 | _mask_adapter_registration, interface, version, |
181 | 303 | previous_operation_name, version, | 301 | previous_operation_provides, previous_operation_name, |
182 | 304 | _mask_adapter_registration) | 302 | context.info) |
183 | 305 | 303 | ||
184 | 306 | # If the operation exists in this version (ie. its name is | 304 | # If the operation exists in this version (ie. its name is |
185 | 307 | # not None), register it using this version's name. | 305 | # not None), register it using this version's name. |
186 | 308 | if operation_name is not None: | 306 | if operation_name is not None: |
190 | 309 | _add_versioned_adapter_action( | 307 | register_adapter_for_version( |
191 | 310 | context, interface, operation_provides, operation_name, | 308 | factory, interface, version, operation_provides, |
192 | 311 | version, factory) | 309 | operation_name, context.info) |
193 | 312 | previous_operation_name = operation_name | 310 | previous_operation_name = operation_name |
194 | 313 | previous_operation_provides = operation_provides | 311 | previous_operation_provides = operation_provides |
195 | 314 | 312 | ||
196 | @@ -322,30 +320,6 @@ | |||
197 | 322 | return None | 320 | return None |
198 | 323 | 321 | ||
199 | 324 | 322 | ||
200 | 325 | def _add_versioned_adapter_action( | ||
201 | 326 | context, interface, provides, name, version, factory): | ||
202 | 327 | """Helper function to register a versioned operation factory. | ||
203 | 328 | |||
204 | 329 | :param context: The context on which to add a registration action. | ||
205 | 330 | :param interface: The IEntry subclass that publishes the named | ||
206 | 331 | operation. | ||
207 | 332 | :param provides: The IResourceOperation subclass provided by the | ||
208 | 333 | factory. | ||
209 | 334 | :param name: The name of the named operation in this version. | ||
210 | 335 | :param version: The version of the web service that publishes this | ||
211 | 336 | named operation. | ||
212 | 337 | :param factory: The object that handles invocations of the named | ||
213 | 338 | operation. | ||
214 | 339 | """ | ||
215 | 340 | context.action( | ||
216 | 341 | discriminator=( | ||
217 | 342 | 'webservice versioned adapter', | ||
218 | 343 | (interface, IWebServiceClientRequest), provides, name, version), | ||
219 | 344 | callable=register_adapter_for_version, | ||
220 | 345 | args=(factory, interface, version, provides, name, context.info), | ||
221 | 346 | ) | ||
222 | 347 | |||
223 | 348 | |||
224 | 349 | def register_exception_view(context, exception): | 323 | def register_exception_view(context, exception): |
225 | 350 | """Register WebServiceExceptionView to handle exception on the webservice. | 324 | """Register WebServiceExceptionView to handle exception on the webservice. |
226 | 351 | """ | 325 | """ |
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 IWebServiceConf iguration 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.)