Merge lp:~wgrant/launchpad/bug-708999-ff-docs into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12406
Proposed branch: lp:~wgrant/launchpad/bug-708999-ff-docs
Merge into: lp:launchpad
Diff against target: 276 lines (+89/-27)
8 files modified
lib/canonical/launchpad/browser/librarian.py (+3/-3)
lib/lp/services/features/browser/info.py (+7/-0)
lib/lp/services/features/browser/tests/test_feature_info.py (+9/-0)
lib/lp/services/features/flags.py (+37/-16)
lib/lp/services/features/templates/feature-info.pt (+21/-4)
lib/lp/services/features/templates/feature-rules.pt (+5/-1)
lib/lp/services/memcache/client.py (+5/-1)
lib/lp/services/memcache/doc/tales-cache.txt (+2/-2)
To merge this branch: bzr merge lp:~wgrant/launchpad/bug-708999-ff-docs
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+49758@code.launchpad.net

Commit message

[r=lifeless][bug=708999] Document code.branchmergequeue and code.incremental_diffs.enabled feature flags, and clean up the docs for the others.

Description of the change

This branch fixes bug #708999 by documenting the two undocumented feature flags.

It also cleans and reformats the value domains and descriptions of several other flags to be more consistent and informative.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

Thanks for addressing this bug.

To me those explanations don't add a lot beyond the name of the variable.

For instance
  'shows branch merge queues if non-empty'
if the bmq is non-empty?

From looking at the usage of the flags it looks like this means "shows them if the flag is boolean true", and the values are not the strings 'empty' and 'nonempty' but rather you can use any string, which Python will treat as true?

I think that shows we badly need a standard way to express boolean flags.

I would document them as

  if true (a non-empty string), shows the branch merge queue user interface

  if true (a non-empty string), show incremental diffs on merge proposals

Revision history for this message
William Grant (wgrant) wrote :

After much discussion on IRC, I went with a more thorough revamp. I standardised the boolean type to empty/non-empty (which most of the flags already respected, despite their docs), and documented the types. I also expanded the descriptions, documented soyuz.derived-distros-ui.enabled, and added a link to further documentation on the dev wiki.

The new pages can be seen at <http://people.canonical.com/~wgrant/launchpad/feature-flag-docs/>.

Only one flag will require changes on production: "memcache pageid:ProjectGroup:+milestones 13 disabled" needs an s/disabled//.

Revision history for this message
Martin Pool (mbp) wrote :

<poolie> wgrant, also hard_timeout, contrary to its docs, seems to be
in milliseconds
 not seconds

Revision history for this message
Aaron Bentley (abentley) wrote :

I fully agree that we need a standard way of doing boolean FeatureFlags. I believe the lack of it indicates that Feature Flags are incomplete, since enabling/disabling features was described as key functionality.

But I think using empty/non-empty to indicate boolean is pretty gross.

Revision history for this message
William Grant (wgrant) wrote :

Sure, it is really gross, but all except two boolean flags already used that convention. It needs additional thought and work to make booleans work nicely.

Revision history for this message
Martin Pool (mbp) wrote :

On 17 February 2011 03:16, Aaron Bentley <email address hidden> wrote:
> I fully agree that we need a standard way of doing boolean FeatureFlags.  I believe the lack of it indicates that Feature Flags are incomplete, since enabling/disabling features was described as key functionality.
>
> But I think using empty/non-empty to indicate boolean is pretty gross.

I think it's gross too. I filed
<https://bugs.launchpad.net/launchpad/+bug/719182> for that. I think
documenting what currently exists has some value, and making things
consistent also. I'm not really happy about making them consistent
with the worst precedented approach, but doing it one step at a time
is good too.

Revision history for this message
Robert Collins (lifeless) wrote :

FWIW I really like the pithy nature of what we have at the moment.
Please don't lose that.

Revision history for this message
Martin Pool (mbp) wrote :

On 17 February 2011 11:31, Robert Collins <email address hidden> wrote:
> FWIW I really like the pithy nature of what we have at the moment.
> Please don't lose that.

So can we say it should make sense to people who didn't develop the
feature, but still one sentence?

Revision history for this message
Robert Collins (lifeless) wrote :

On Thu, Feb 17, 2011 at 1:49 PM, Martin Pool <email address hidden> wrote:
> On 17 February 2011 11:31, Robert Collins <email address hidden> wrote:
>> FWIW I really like the pithy nature of what we have at the moment.
>> Please don't lose that.
>
> So can we say it should make sense to people who didn't develop the
> feature, but still one sentence?

I don't understand.

Revision history for this message
Martin Pool (mbp) wrote :

On 17 February 2011 13:02, Robert Collins <email address hidden> wrote:
> On Thu, Feb 17, 2011 at 1:49 PM, Martin Pool <email address hidden> wrote:
>> On 17 February 2011 11:31, Robert Collins <email address hidden> wrote:
>>> FWIW I really like the pithy nature of what we have at the moment.
>>> Please don't lose that.
>>
>> So can we say it should make sense to people who didn't develop the
>> feature, but still one sentence?
>
> I don't understand.

Which aspect of pithiness were you talking about?

Revision history for this message
Robert Collins (lifeless) wrote :

On Thu, Feb 17, 2011 at 6:16 PM, Martin Pool <email address hidden> wrote:

> Which aspect of pithiness were you talking about?

the feature flags code itself and the code using it.

Revision history for this message
Robert Collins (lifeless) wrote :

The domain_value_info test is a little weird - is it really looking for the str() of the named tuples to be in the page?

The memcache flag seems a little hard to follow the implementation of now, but other than that this seems ok. I note that the memcache implementation has gotten substantially longer, and that seems undesirable to me. Lets talk.

Revision history for this message
William Grant (wgrant) wrote :

That test is similar to the others. It checks that each member of each namedtuple is displayed.

I agree with your memcache concern, but it was either that or add a default mechanism.

Revision history for this message
Robert Collins (lifeless) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/browser/librarian.py'
2--- lib/canonical/launchpad/browser/librarian.py 2011-01-20 23:55:37 +0000
3+++ lib/canonical/launchpad/browser/librarian.py 2011-02-18 08:19:14 +0000
4@@ -83,8 +83,8 @@
5
6 If the file is public, it will redirect to the files http url.
7
8- Otherwise if the feature flag publicrestrictedlibrarian is set to 'on'
9- this will allocate a token and redirect to the aliases private url.
10+ Otherwise if the feature flag publicrestrictedlibrarian is true this will
11+ allocate a token and redirect to the aliases private url.
12
13 Otherwise it will proxy the file in the appserver.
14
15@@ -179,7 +179,7 @@
16 if not self.context.restricted:
17 # Public file, just point the client at the right place.
18 return RedirectionView(self.context.http_url, self.request), ()
19- if getFeatureFlag(u'publicrestrictedlibrarian') != 'on':
20+ if not getFeatureFlag(u'publicrestrictedlibrarian'):
21 # Restricted file and we have not enabled the public
22 # restricted librarian yet :- deliver inline.
23 self._when_streaming()
24
25=== modified file 'lib/lp/services/features/browser/info.py'
26--- lib/lp/services/features/browser/info.py 2011-01-26 22:07:08 +0000
27+++ lib/lp/services/features/browser/info.py 2011-02-18 08:19:14 +0000
28@@ -15,6 +15,7 @@
29 from lp.services.features.flags import (
30 flag_info,
31 undocumented_flags,
32+ value_domain_info,
33 )
34 from lp.services.features.scopes import (
35 HANDLERS,
36@@ -25,6 +26,7 @@
37
38 # Named tuples to use when passing flag and scope data to the template.
39 Flag = namedtuple('Flag', ('name', 'domain', 'description', 'default'))
40+ValueDomain = namedtuple('ValueDomain', ('name', 'description'))
41 Scope = namedtuple('Scope', ('regex', 'description'))
42
43
44@@ -45,6 +47,11 @@
45 return ', '.join(undocumented_flags)
46
47 @property
48+ def value_domain_info(self):
49+ """A list of flags as named tuples, ready to be rendered."""
50+ return map(ValueDomain._make, value_domain_info)
51+
52+ @property
53 def undocumented_scopes(self):
54 """Scope names referenced during process lifetime but not documented.
55 """
56
57=== modified file 'lib/lp/services/features/browser/tests/test_feature_info.py'
58--- lib/lp/services/features/browser/tests/test_feature_info.py 2011-01-26 22:07:08 +0000
59+++ lib/lp/services/features/browser/tests/test_feature_info.py 2011-02-18 08:19:14 +0000
60@@ -19,6 +19,7 @@
61 flag_info,
62 NullFeatureController,
63 undocumented_flags,
64+ value_domain_info,
65 )
66 from lp.services.features.scopes import (
67 HANDLERS,
68@@ -66,6 +67,14 @@
69 for item in record:
70 self.assertThat(browser.contents, Contains(item))
71
72+ def test_value_domain_documentation_displayed(self):
73+ """The value domain documentation is displayed on the page."""
74+ browser = self.getUserBrowserAsAdmin()
75+ browser.open(self.getFeatureInfoUrl())
76+ for record in value_domain_info:
77+ for item in record:
78+ self.assertThat(browser.contents, Contains(item))
79+
80 def test_scope_documentation_displayed(self):
81 """The scope documentation is displayed on the page."""
82 browser = self.getUserBrowserAsAdmin()
83
84=== modified file 'lib/lp/services/features/flags.py'
85--- lib/lp/services/features/flags.py 2011-02-14 18:21:01 +0000
86+++ lib/lp/services/features/flags.py 2011-02-18 08:19:14 +0000
87@@ -6,6 +6,7 @@
88 'flag_info',
89 'NullFeatureController',
90 'undocumented_flags',
91+ 'value_domain_info',
92 ]
93
94
95@@ -17,36 +18,56 @@
96
97 __metaclass__ = type
98
99+
100+value_domain_info = sorted([
101+ ('boolean',
102+ 'Any non-empty value is true; an empty value is false.'),
103+ ('float',
104+ 'The flag value is set to the given floating point number.'),
105+ ])
106+
107 # This table of flag name, value domain, and prose documentation is used to
108 # generate the web-visible feature flag documentation.
109 flag_info = sorted([
110+ ('code.branchmergequeue',
111+ 'boolean',
112+ 'Enables merge queue pages and lists them on branch pages.',
113+ ''),
114+ ('code.incremental_diffs.enabled',
115+ 'boolean',
116+ 'Shows incremental diffs on merge proposals.',
117+ ''),
118 ('code.recipes_enabled',
119- '[on|off]',
120- 'enable recipes',
121- 'off'),
122+ 'boolean',
123+ 'Enables source package recipes in the API and UI.',
124+ ''),
125 ('hard_timeout',
126 'float',
127- 'sets the hard timeout in seconds',
128+ 'Sets the hard request timeout in milliseconds.',
129 ''),
130 ('malone.advanced-subscriptions.enabled',
131- '[on|off]',
132- 'enables advanced subscriptions features',
133- 'off'),
134+ 'boolean',
135+ 'Enables advanced bug subscription features.',
136+ ''),
137 ('malone.disable_targetnamesearch',
138- '[empty|nonempty]',
139- 'If nonempty targetnames are not consulted during text search.',
140+ 'boolean',
141+ 'If true, disables consultation of target names during bug text search.',
142 ''),
143 ('memcache',
144- '[enabled|disabled]',
145- 'enables/disables memcache',
146+ 'boolean',
147+ 'Enables use of memcached where it is supported.',
148 'enabled'),
149 ('publicrestrictedlibrarian',
150- '[on|off]',
151- 'redirect to private URLs instead of proxying',
152- 'off'),
153+ 'boolean',
154+ 'Redirects to private librarian files instead of proxying them.',
155+ ''),
156+ ('soyuz.derived-series-ui.enabled',
157+ 'boolean',
158+ 'Enables derivative distributions pages.',
159+ ''),
160 ('visible_render_time',
161- 'empty|nonempty',
162- 'enables showing the page render overheads in the login widget',
163+ 'boolean',
164+ 'Shows the server-side page render time in the login widget.',
165 ''),
166 ])
167
168
169=== modified file 'lib/lp/services/features/templates/feature-info.pt'
170--- lib/lp/services/features/templates/feature-info.pt 2011-01-20 18:33:25 +0000
171+++ lib/lp/services/features/templates/feature-info.pt 2011-02-18 08:19:14 +0000
172@@ -29,7 +29,7 @@
173 </tbody>
174 </table>
175
176- <p>
177+ <p/>
178 <h2>Undocumented flags</h2>
179 These flags were referenced during this process' lifetime but are not
180 documented:
181@@ -38,7 +38,24 @@
182 </strong>
183 <strong tal:content="view/undocumented_flags">list of flags</strong>
184
185- <p>
186+ <p/>
187+ <h2>Value domains</h2>
188+ <table class="listing">
189+ <thead>
190+ <tr>
191+ <th>Name</th>
192+ <th>Description</th>
193+ </tr>
194+ </thead>
195+ <tbody>
196+ <tr tal:repeat="info view/value_domain_info">
197+ <td tal:content="info/name">name here</td>
198+ <td tal:content="info/description">description here</td>
199+ </tr>
200+ </tbody>
201+ </table>
202+
203+ <p/>
204 <h2>Scopes</h2>
205
206 The table below describes the currently available scopes. The first column
207@@ -46,7 +63,7 @@
208 "pageid:foo" scopes match the regex "pageid:") and the second gives a
209 description of when the scope is active.
210
211- <p>
212+ <p/>
213 <table class="listing">
214 <thead>
215 <tr>
216@@ -62,7 +79,7 @@
217 </tbody>
218 </table>
219
220- <p>
221+ <p/>
222 <h2>Undocumented scopes</h2>
223 These scopes were referenced during this process' lifetime but are not
224 documented:
225
226=== modified file 'lib/lp/services/features/templates/feature-rules.pt'
227--- lib/lp/services/features/templates/feature-rules.pt 2011-01-26 22:07:08 +0000
228+++ lib/lp/services/features/templates/feature-rules.pt 2011-02-18 08:19:14 +0000
229@@ -18,8 +18,12 @@
230 </p>
231 <tal:diff replace="structure view/diff"/>
232 </div>
233+ <p>
234 For more information about the available feature flags and scopes see
235- the <a href="+feature-info">feature flag info</a>.
236+ the <a href="+feature-info">feature flag info</a>. For other
237+ documentation on feature flags, see the
238+ <a href="https://dev.launchpad.net/FeatureFlags">development wiki</a>.
239+ </p>
240 </div>
241 </div>
242 </div>
243
244=== modified file 'lib/lp/services/memcache/client.py'
245--- lib/lp/services/memcache/client.py 2010-09-12 05:52:41 +0000
246+++ lib/lp/services/memcache/client.py 2011-02-18 08:19:14 +0000
247@@ -36,7 +36,11 @@
248
249 @property
250 def _enabled(self):
251- return features.getFeatureFlag('memcache') != 'disabled'
252+ configured_value = features.getFeatureFlag('memcache')
253+ if configured_value is None:
254+ return True
255+ else:
256+ return configured_value
257
258 def get(self, key):
259 if not self._enabled:
260
261=== modified file 'lib/lp/services/memcache/doc/tales-cache.txt'
262--- lib/lp/services/memcache/doc/tales-cache.txt 2010-11-02 17:47:05 +0000
263+++ lib/lp/services/memcache/doc/tales-cache.txt 2011-02-18 08:19:14 +0000
264@@ -394,10 +394,10 @@
265 ---------
266
267 Memcache in templates can be disabled entirely by setting the memcache flag to
268-'disabled'.
269+the empty string.
270
271 >>> from lp.services.features.testing import FeatureFixture
272- >>> fixture = FeatureFixture({'memcache': 'disabled'})
273+ >>> fixture = FeatureFixture({'memcache': ''})
274 >>> fixture.setUp()
275
276 And now what cached before will not cache.