← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~mbp/launchpad/flags-webapp into lp:launchpad/devel

 

Martin Pool has proposed merging lp:~mbp/launchpad/flags-webapp into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


This makes the feature flag developer documentation more accessible in the Pydoctor web view.
-- 
https://code.launchpad.net/~mbp/launchpad/flags-webapp/+merge/32967
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~mbp/launchpad/flags-webapp into lp:launchpad/devel.
=== modified file 'lib/lp/services/features/__init__.py'
--- lib/lp/services/features/__init__.py	2010-08-05 23:40:32 +0000
+++ lib/lp/services/features/__init__.py	2010-08-18 08:57:45 +0000
@@ -1,10 +1,125 @@
 # Copyright 2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-"""lp.services.features provide dynamically configurable feature flags.
-
-These can be turned on and off by admins, and can affect particular
-defined scopes such as "beta users" or "production servers."
+"""Dynamic feature configuration.
+
+Introduction
+============
+
+The point of feature flags is to let us turn some features of Launchpad on
+and off without changing the code or restarting the application, and to
+expose different features to different subsets of users.
+
+See U{https://dev.launchpad.net/LEP/FeatureFlags} for more discussion and
+rationale.
+
+The typical use for feature flags is within web page requests but they can
+also be used in asynchronous jobs or apis or other parts of Launchpad.
+
+Internal model for feature flags
+================================
+
+A feature flag maps from a I{name} to a I{value}.  The specific value used
+for a particular request is determined by a set of zero or more I{scopes}
+that apply to that request, by finding the I{rule} with the highest
+I{priority}.
+
+Flags are defined by a I{name} that typically looks like a Python
+identifier, for example C{notification.global.text}.  A definition is
+given for a particular I{scope}, which also looks like a dotted identifier,
+for example C{user.beta} or C{server.edge}.  This is just a naming
+convention, and they do not need to correspond to Python modules.
+
+The value is stored in the database as just a Unicode string, and it might
+be interpreted as a boolean, number, human-readable string or whatever.
+
+The default for flags is to be None if they're not set in the database, so
+that should be a sensible baseline default state.
+
+Performance model
+=================
+
+Flags are supposed to be cheap enough that you can introduce them without
+causing a performance concern.
+
+If the page does not check any flags, no extra work will be done.  The
+first time a page checks a flag, all the rules will be read from the
+database and held in memory for the duration of the request.
+
+Scopes may be expensive in some cases, such as checking group membership.
+Whether a scope is active or not is looked up the first time it's needed
+within a particular request.
+
+The standard page footer identifies the flags and scopes that were
+actually used by the page.
+
+Naming conventions
+==================
+
+We have naming conventions for feature flags and scopes, so that people can
+understand the likely impact of a particular flag and so they can find all
+the flags likely to affect a feature.
+
+So for any flag we want to say:
+
+  - What application area does this affect? (malone, survey, questions,
+    code, etc)
+
+  - What specific feature does it change?
+
+  - What affect does it have on this feature?  The most common is "enabled"
+    but for some other we want to specify a specific value as well such as
+    "date" or "size".
+
+These are concatenated with dots so the overall feature name looks a bit
+like a Python module name.
+
+A similar approach is used for scopes.
+
+Checking flags in page templates
+================================
+
+You can conditionally show some text like this::
+
+  <tal:survey condition="features/user_survey.enabled">
+    &nbsp;&bull;&nbsp;
+    <a href="http://survey.example.com/";>Take our survey!</a>
+  </tal:survey>
+
+You can use the built-in TAL feature of prepending C{not:} to the
+condition, and for flags that have a value you could use them in
+C{tal:replace} or C{tal:attributes}.
+
+If you just want to simply insert some text taken from a feature, say
+something like::
+
+  Message of the day: ${motd.text}
+
+Templates can also check whether the request is in a particular scope, but
+before using this consider whether the code will always be bound to that
+scope or whether it would be more correct to define a new feature::
+
+  <p tal:condition="feature_scopes/server.staging">
+    Staging server: all data will be discarded daily!</p>
+
+Checking flags in code
+======================
+
+The Zope traversal code establishes a `FeatureController` for the duration
+of a request.  The object can be obtained through either
+`request.features` or `lp.services.features.per_thread.features`.  This
+provides various useful methods including `getFlag` to look up one feature
+(memoized), and `isInScope` to check one scope (also memoized).
+
+As a convenience, `lp.services.features.getFeatureFlag` looks up a single
+flag in the thread default controller.
+
+Debugging feature usage
+=======================
+
+The flags active during a page request, and the scopes that were looked
+up are visible in the comment at the bottom of every standard Launchpad
+page.
 """
 
 import threading

=== modified file 'lib/lp/services/features/doc/features.txt'
--- lib/lp/services/features/doc/features.txt	2010-07-27 09:30:45 +0000
+++ lib/lp/services/features/doc/features.txt	2010-08-18 08:57:45 +0000
@@ -1,127 +0,0 @@
-****************************
-Feature Flag Developer Guide
-****************************
-
-Introduction
-************
-
-The point of feature flags is to let us turn some features of Launchpad on
-and off without changing the code or restarting the application, and to
-expose different features to different subsets of users.
-
-See <https://dev.launchpad.net/LEP/FeatureFlags> for more discussion and
-rationale.
-
-The typical use for feature flags is within web page requests but they can
-also be used in asynchronous jobs or apis or other parts of Launchpad.
-
-Internal model for feature flags
-********************************
-
-A feature flag maps from a *name* to a *value*.  The specific value used
-for a particular request is determined by a set of zero or more *scopes*
-that apply to that request, by finding the *rule* with the highest
-*priority*.
-
-Flags are defined by a *name* that typically looks like a Python
-identifier, for example ``notification.global.text``.  A definition is
-given for a particular *scope*, which also looks like a dotted identifier,
-for example ``user.beta`` or ``server.edge``.  This is just a naming
-convention, and they do not need to correspond to Python modules.
-
-The value is stored in the database as just a Unicode string, and it might
-be interpreted as a boolean, number, human-readable string or whatever.
-
-The default for flags is to be None if they're not set in the database, so
-that should be a sensible baseline default state.
-
-Performance model
-*****************
-
-Flags are supposed to be cheap enough that you can introduce them without
-causing a performance concern.
-
-If the page does not check any flags, no extra work will be done.  The
-first time a page checks a flag, all the rules will be read from the
-database and held in memory for the duration of the request.
-
-Scopes may be expensive in some cases, such as checking group membership.
-Whether a scope is active or not is looked up the first time it's needed
-within a particular request.
-
-The standard page footer identifies the flags and scopes that were
-actually used by the page.
-
-Naming conventions
-******************
-
-We have naming conventions for feature flags and scopes, so that people can
-understand the likely impact of a particular flag and so they can find all
-the flags likely to affect a feature.
-
-So for any flag we want to say:
-
-* What application area does this affect? (malone, survey, questions,
-  code, etc)
-
-* What specific feature does it change?
-
-* What affect does it have on this feature?  The most common is "enabled"
-  but for some other we want to specify a specific value as well such as
-  "date" or "size".
-
-These are concatenated with dots so the overall feature name looks a bit
-like a Python module name.
-
-A similar approach is used for scopes.
-
-Checking flags in page templates
-********************************
-
-You can conditionally show some text like this::
-
-  <tal:survey condition="features/user_survey.enabled">
-    &nbsp;&bull;&nbsp;
-    <a href="http://survey.example.com/";>Take our survey!</a>
-  </tal:survey>
-
-You can use the built-in TAL feature of prepending ``not:`` to the
-condition, and for flags that have a value you could use them in
-``tal:replace`` or ``tal:attributes``.
-
-If you just want to simply insert some text taken from a feature, say
-something like::
-
-  Message of the day: ${motd.text}
-
-Templates can also check whether the request is in a particular scope, but
-before using this consider whether the code will always be bound to that
-scope or whether it would be more correct to define a new feature::
-
-  <p tal:condition="feature_scopes/server.staging">
-    Staging server: all data will be discarded daily!</p>
-
-Checking flags in code
-**********************
-
-The Zope traversal code establishes a `FeatureController` for the duration
-of a request.  The object can be obtained through either
-`request.features` or `lp.services.features.per_thread.features`.  This
-provides various useful methods including `getFlag` to look up one feature
-(memoized), and `isInScope` to check one scope (also memoized).
-
-As a convenience, `lp.services.features.getFeatureFlag` looks up a single
-flag in the thread default controller.
-
-Debugging feature usage
-***********************
-
-The flags active during a page request, and the scopes that were looked
-up are visible in the comment at the bottom of every standard Launchpad
-page.
-
-Defining scopes
-***************
-
-
-.. vim: ft=rst

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2010-08-10 04:35:41 +0000
+++ lib/lp/services/features/flags.py	2010-08-18 08:57:45 +0000
@@ -46,8 +46,10 @@
     """A FeatureController tells application code what features are active.
 
     It does this by meshing together two sources of data:
-    - feature flags, typically set by an administrator into the database
-    - feature scopes, which would typically be looked up based on attributes
+
+      - feature flags, typically set by an administrator into the database
+
+      - feature scopes, which would typically be looked up based on attributes
       of the current web request, or the user for whom a job is being run, or
       something similar.
 
@@ -72,14 +74,14 @@
     The controller is then supposed to be held in a thread-local and reused
     for the duration of the request.
 
-    See <https://dev.launchpad.net/LEP/FeatureFlags>
+    @see: U{https://dev.launchpad.net/LEP/FeatureFlags}
     """
 
     def __init__(self, scope_check_callback):
         """Construct a new view of the features for a set of scopes.
 
         :param scope_check_callback: Given a scope name, says whether
-            it's active or not.
+        it's active or not.
         """
         self._known_scopes = Memoize(scope_check_callback)
         self._known_flags = Memoize(self._checkFlag)
@@ -91,8 +93,9 @@
         """Get the value of a specific flag.
         
         :param flag: A name to lookup. e.g. 'recipes.enabled'
+
         :return: The value of the flag determined by the highest priority rule
-            that matched.
+        that matched.
         """
         return self._known_flags.lookup(flag)
 


Follow ups