launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03480
[Merge] lp:~mbp/launchpad/feature-scope-cleanup into lp:launchpad
Martin Pool has proposed merging lp:~mbp/launchpad/feature-scope-cleanup into lp:launchpad with lp:~mbp/launchpad/mail-cleanup as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~mbp/launchpad/feature-scope-cleanup/+merge/59460
This does a minor cleanup to the feature flag scopes code. Previously it was a bit hardcoded to the idea of scopes being keyed only off web requests, but we already have some that apply during cron scripts, I soon want to add some for mail requests, and other things are possible in the future.
Now the only scope controller that has the concept of a .request are those that are actually relevant to it. That seems cleaner than having it present but often None.
I removed the global WEBAPP_SCOPE_HANDLERS and SCRIPT_SCOPE_HANDLERS because they're a bit redundant with the classes that know how to build them, and they're not used anywhere else.
I tested with './bin/test -m feature', and make lint is clean.
--
https://code.launchpad.net/~mbp/launchpad/feature-scope-cleanup/+merge/59460
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~mbp/launchpad/feature-scope-cleanup into lp:launchpad.
=== modified file 'lib/lp/services/features/scopes.py'
--- lib/lp/services/features/scopes.py 2011-03-29 00:11:57 +0000
+++ lib/lp/services/features/scopes.py 2011-04-29 07:22:29 +0000
@@ -1,7 +1,12 @@
# Copyright 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-"""Connect Feature flags into webapp requests."""
+"""Connect feature flags into scopes where they can be used.
+
+The most common is flags scoped by some attribute of a web request, such as
+the page ID or the server name. But other types of scope can also match code
+run from cron scripts and potentially also other places.
+"""
__all__ = [
'HANDLERS',
@@ -35,9 +40,6 @@
# scope. Also used on +feature-info.
pattern = None
- def __init__(self, request):
- self.request = request
-
@cachedproperty
def compiled_pattern(self):
"""The compiled scope matching regex. A small optimization."""
@@ -58,7 +60,14 @@
return True
-class PageScope(BaseScope):
+class BaseWebRequestScope(BaseScope):
+ """Base class for scopes that key off web request attributes."""
+
+ def __init__(self, request):
+ self.request = request
+
+
+class PageScope(BaseWebRequestScope):
"""The current page ID.
Pageid scopes are written as 'pageid:' + the pageid to match. Pageids
@@ -153,7 +162,6 @@
pattern = r'script:'
def __init__(self, script_name):
- super(ScriptScope, self).__init__(None)
self.script_scope = self.pattern + script_name
def lookup(self, scope_name):
@@ -161,25 +169,21 @@
return scope_name == self.script_scope
-# Handlers for the scopes that may occur in the webapp.
-WEBAPP_SCOPE_HANDLERS = [DefaultScope, PageScope, TeamScope, ServerScope]
-
-
-# Handlers for the scopes that may occur in scripts.
-SCRIPT_SCOPE_HANDLERS = [DefaultScope, ScriptScope]
-
-
-# These are the handlers for all of the allowable scopes. Any new scope will
+# These are the handlers for all of the allowable scopes, listed here so that
+# we can for example show all of them in an admin page. Any new scope will
# need a scope handler and that scope handler has to be added to this list.
# See BaseScope for hints as to what a scope handler should look like.
-HANDLERS = set(WEBAPP_SCOPE_HANDLERS + SCRIPT_SCOPE_HANDLERS)
+HANDLERS = set([DefaultScope, PageScope, TeamScope, ServerScope, ScriptScope])
class MultiScopeHandler():
- """A scope handler that combines multiple `BaseScope`s."""
-
- def __init__(self, request, scopes):
- self.request = request
+ """A scope handler that combines multiple `BaseScope`s.
+
+ The ordering in which they're added is arbitrary, because precedence is
+ determined by the ordering of rules.
+ """
+
+ def __init__(self, scopes):
self.handlers = scopes
def _findMatchingHandlers(self, scope_name):
@@ -190,11 +194,11 @@
if handler.compiled_pattern.match(scope_name)]
def lookup(self, scope_name):
- """Determine if scope_name applies to this request.
+ """Determine if scope_name applies.
This method iterates over the configured scope hanlders until it
- either finds one that claims the requested scope name is a match for
- the current request or the handlers are exhuasted, in which case the
+ either finds one that claims the requested scope name matches,
+ or the handlers are exhuasted, in which case the
scope name is not a match.
"""
matching_handlers = self._findMatchingHandlers(scope_name)
@@ -213,13 +217,17 @@
"""Identify feature scopes based on request state."""
def __init__(self, request):
- super(ScopesFromRequest, self).__init__(
- request, [f(request) for f in WEBAPP_SCOPE_HANDLERS])
+ super(ScopesFromRequest, self).__init__([
+ DefaultScope(),
+ PageScope(request),
+ TeamScope(),
+ ServerScope()])
class ScopesForScript(MultiScopeHandler):
"""Identify feature scopes for a given script."""
def __init__(self, script_name):
- super(ScopesForScript, self).__init__(
- None, [DefaultScope(None), ScriptScope(script_name)])
+ super(ScopesForScript, self).__init__([
+ DefaultScope(),
+ ScriptScope(script_name)])
=== modified file 'lib/lp/services/features/tests/test_scopes.py'
--- lib/lp/services/features/tests/test_scopes.py 2011-03-29 00:11:57 +0000
+++ lib/lp/services/features/tests/test_scopes.py 2011-04-29 07:22:29 +0000
@@ -9,13 +9,14 @@
from lp.testing import TestCaseWithFactory
from lp.services.features.scopes import (
BaseScope,
+ BaseWebRequestScope,
MultiScopeHandler,
ScopesForScript,
ScriptScope,
)
-class FakeScope(BaseScope):
+class FakeScope(BaseWebRequestScope):
pattern = r'fake:'
def lookup(self, scope_name):
@@ -38,17 +39,18 @@
def test_MultiScopeHandler_lookup_ignores_unmatched_scope(self):
scope_name = self.factory.getUniqueString()
- handler = MultiScopeHandler(None, [FakeScope(scope_name)])
+ fake_scope = FakeScope(scope_name)
+ handler = MultiScopeHandler([fake_scope])
self.assertFalse(handler.lookup("other:other"))
def test_MultiScopeHandler_lookup_ignores_inapplicable_scope(self):
scope_name = self.factory.getUniqueString()
- handler = MultiScopeHandler(None, [FakeScope(scope_name)])
+ handler = MultiScopeHandler([FakeScope(scope_name)])
self.assertFalse(handler.lookup("fake:other"))
def test_MultiScopeHandler_lookup_finds_matching_scope(self):
scope_name = self.factory.getUniqueString()
- handler = MultiScopeHandler(None, [FakeScope(scope_name)])
+ handler = MultiScopeHandler([FakeScope(scope_name)])
self.assertTrue(handler.lookup("fake:" + scope_name))
def test_ScopesForScript_includes_default_scope(self):