Merge lp:~leonardr/lazr.restful/refactor-tag-request-with-version into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/refactor-tag-request-with-version
Merge into: lp:lazr.restful
Diff against target: 177 lines (+107/-6)
3 files modified
src/lazr/restful/NEWS.txt (+1/-1)
src/lazr/restful/docs/webservice-declarations.txt (+98/-2)
src/lazr/restful/metazcml.py (+8/-3)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/refactor-tag-request-with-version
Reviewer Review Type Date Requested Status
Gavin Panella code Approve
Review via email: mp+20547@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

Ignore the name of this branch; I took care of that feature in another branch.

This branch fixes some edge cases around a recent change to lazr.restful in which mutator methods are no longer published as named operations. The tricky part is that getting rid of the feature altogether would destroy backwards compatibility. I created a configuration variable "last_version_with_mutator_named_operations". The old behavior is maintained in that version and all previous versions. The new behavior holds after that version.

As an example, when I integrate this version of lazr.restful into Launchpad, "last_version_with_mutator_named_operations" will be "beta", and the effect will be that the 'beta' version will not change, but in the '1.0' version, method like 'transitionToStatus' that merely duplicate a mutator will no longer be present.

So, when preparing to do the Launchpad integration I noticed some bugs in edge cases. If you set l_v_w_m_n_o to None, it's supposed to disable the feature altogether, but that wasn't working. And if you set l_v_w_m_n_o to the latest active version, you're supposed to get the old behavior back, but that wasn't working either. I fixed both bugs and added tests for them.

The first changes in webservice-declarations.txt (changing __name__ to __class__.__name__) fix unrelated test failures that I believe are present in the current trunk.

Revision history for this message
Gavin Panella (allenap) wrote :

This looks good. Only one comment: the doc test is way too long. I don't expect you to change that, it's just my opinion.

review: Approve (code)
Revision history for this message
Leonard Richardson (leonardr) wrote :

I played around with the doctest some more but couldn't find a good way to reduce the size.

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 2010-03-03 13:05:13 +0000
3+++ src/lazr/restful/NEWS.txt 2010-03-03 13:51:18 +0000
4@@ -15,7 +15,7 @@
5 By default, mutator methods are no longer separately published as
6 named operations. To maintain backwards compatibility (or if you just
7 want this feature back), put the name of the most recent version of
8-your web service in the "last_version_with_named_mutator_operations"
9+your web service in the "last_version_with_mutator_named_operations"
10 field of your IWebServiceConfiguration implementation.
11
12 0.9.21 (2010-02-23)
13
14=== modified file 'src/lazr/restful/docs/webservice-declarations.txt'
15--- src/lazr/restful/docs/webservice-declarations.txt 2010-02-25 21:18:49 +0000
16+++ src/lazr/restful/docs/webservice-declarations.txt 2010-03-03 13:51:18 +0000
17@@ -2722,6 +2722,12 @@
18 ...
19 ComponentLookupError: ...
20
21+Edge cases
22+**********
23+
24+You can't immediately reinstate a mutator operation as a named operation
25+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27 Here's one that shows a limitation of the software. This method
28 defines a mutator 'set_value' for version 1.0, which will be removed
29 in version 2.0. It *also* defines a named operation to be published
30@@ -2764,7 +2770,7 @@
31 The mutator is accessible for version 1.0, as you'd expect.
32
33 >>> context = MutatorPlusNamedOperation()
34- >>> print operation_for(context, '1.0', 'set_value').__name__
35+ >>> print operation_for(context, '1.0', 'set_value').__class__.__name__
36 POST_IMutatorPlusNamedOperationEntry_set_value_1_0
37
38 But the named operation that replaces the mutator in version 1.0 is
39@@ -2778,9 +2784,99 @@
40 The named operation of the same name defined in version 3.0 _is_
41 accessible.
42
43- >>> print operation_for(context, '3.0', 'set_value').__name__
44+ >>> print operation_for(context, '3.0', 'set_value').__class__.__name__
45 POST_IMutatorPlusNamedOperationEntry_set_value_3_0
46
47 So, in the version that gets rid of named operations for mutator
48 methods, you can't define a named operation with the same name as one
49 of the outgoing mutator methods.
50+
51+Removing mutator named operations altogether
52+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
53+
54+You can remove this behavior altogether (such that mutators are never
55+named operations) by setting the value of the configuration variable
56+'last_version_with_mutator_named_operations' to None.
57+
58+ >>> config = getUtility(IWebServiceConfiguration)
59+ >>> config.last_version_with_mutator_named_operations = None
60+
61+Here's a class identical to IBetaMutatorEntry: it defines a mutator in
62+the 'beta' version of the web service. (We have to redefine the class
63+to avoid conflicting registrations.)
64+
65+ >>> class IBetaMutatorEntry2(IBetaMutatorEntry):
66+ ... export_as_webservice_entry()
67+ ...
68+ ... field = exported(TextLine(readonly=True))
69+ ...
70+ ... @mutator_for(field)
71+ ... @export_write_operation()
72+ ... @operation_parameters(new_value=TextLine())
73+ ... def set_value(new_value):
74+ ... pass
75+
76+ >>> class BetaMutator2:
77+ ... implements(IBetaMutatorEntry2)
78+
79+ >>> module = register_test_module(
80+ ... 'betamutator2', IBetaMutatorEntry2, BetaMutator2)
81+
82+ >>> module = register_test_module(
83+ ... 'betamutator', IBetaMutatorEntry, BetaMutator)
84+
85+Back when last_version_with_mutator_named_operations was '1.0', the
86+'set_value' named operation on IBetaMutatorEntry was accessible in
87+'beta' but not in '1.0' or later versions. Now, IBetaMutatorEntry2's
88+'set_value' mutator is not even accessible in 'beta'.
89+
90+ >>> context = BetaMutator2()
91+ >>> operation_for(context, 'beta', 'set_value')
92+ Traceback (most recent call last):
93+ ...
94+ ComponentLookupError: ...
95+
96+Getting the old behavior back
97+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
98+
99+You can bring back the old behavior (in which mutators are always
100+named operations) by setting 'last_version_with_mutator_named_operations'
101+to the last active version.
102+
103+ >>> config.last_version_with_mutator_named_operations = (
104+ ... config.active_versions[-1])
105+
106+Again, we have to publish a new entry class, to avoid conflicting
107+registrations.
108+
109+ >>> class IBetaMutatorEntry3(Interface):
110+ ... export_as_webservice_entry()
111+ ...
112+ ... field = exported(TextLine(readonly=True))
113+ ...
114+ ... @mutator_for(field)
115+ ... @export_write_operation()
116+ ... @operation_parameters(new_value=TextLine())
117+ ... def set_value(new_value):
118+ ... pass
119+
120+ >>> class BetaMutator3:
121+ ... implements(IBetaMutatorEntry3)
122+
123+ >>> module = register_test_module(
124+ ... 'betamutator3', IBetaMutatorEntry3, BetaMutator3)
125+
126+Back when last_version_with_mutator_named_operations was '1.0', the
127+'set_value' mutator on IBetaMutatorEntry was not accessible in any
128+version past '1.0'. Now, the corresponding IBetaMutatorEntry3 mutator
129+is accessible in every version.
130+
131+ >>> context = BetaMutator3()
132+ >>> operation_for(context, 'beta', 'set_value')
133+ <...POST_IBetaMutatorEntry3_set_value_beta...>
134+ >>> operation_for(context, '1.0', 'set_value')
135+ <...POST_IBetaMutatorEntry3_set_value_beta...>
136+ >>> operation_for(context, '2.0', 'set_value')
137+ <...POST_IBetaMutatorEntry3_set_value_beta...>
138+ >>> operation_for(context, '3.0', 'set_value')
139+ <...POST_IBetaMutatorEntry3_set_value_beta...>
140
141=== modified file 'src/lazr/restful/metazcml.py'
142--- src/lazr/restful/metazcml.py 2010-02-25 19:22:00 +0000
143+++ src/lazr/restful/metazcml.py 2010-03-03 13:51:18 +0000
144@@ -222,9 +222,11 @@
145 else:
146 no_mutator_operations_after = config.active_versions.index(
147 config.last_version_with_mutator_named_operations)
148- if len(config.active_versions) > no_mutator_operations_after:
149+ if len(config.active_versions) > no_mutator_operations_after+1:
150 block_mutator_operations_as_of_version = config.active_versions[
151 no_mutator_operations_after+1]
152+ else:
153+ block_mutator_operations_as_of_version = None
154
155 for name, method in interface.namesAndDescriptions(True):
156 tag = method.queryTaggedValue(LAZR_WEBSERVICE_EXPORTED)
157@@ -309,7 +311,9 @@
158 this_version_index = config.active_versions.index(
159 version)
160 if (tag.get('is_mutator', False)
161- and no_mutator_operations_after < this_version_index):
162+ and (no_mutator_operations_after is None
163+ or no_mutator_operations_after < this_version_index)):
164+
165 # This is a mutator method, and in this version,
166 # mutator methods are not published as named
167 # operations at all. Block any lookup of the named
168@@ -345,7 +349,8 @@
169 previous_operation_name = operation_name
170 previous_operation_provides = operation_provides
171
172- if operation_registered_as_mutator:
173+ if (operation_registered_as_mutator and
174+ block_mutator_operations_as_of_version is not None):
175 # The operation was registered as a mutator, and it never
176 # got de-registered. De-register it now.
177 register_adapter_for_version(

Subscribers

People subscribed via source and target branches