From a06b2ce9467504d86844f0c466c1b3a14cc0bf1f Mon Sep 17 00:00:00 2001 From: Isman Firmansyah Date: Mon, 13 Oct 2014 05:58:22 +0700 Subject: [PATCH 1/2] add custom handler to add inline feature flags --- docs/contrib.rst | 52 +++++++++++++++++++ flask_featureflags/contrib/inline/__init__.py | 18 +++++++ tests/contrib/test_inline_handler.py | 42 +++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 flask_featureflags/contrib/inline/__init__.py create mode 100644 tests/contrib/test_inline_handler.py diff --git a/docs/contrib.rst b/docs/contrib.rst index ee9ccdb..0fed5fc 100644 --- a/docs/contrib.rst +++ b/docs/contrib.rst @@ -30,3 +30,55 @@ It will automatically create a table to store your flags in, or you can override ff.add_handler(SQLAlchemyFeatureFlags(db, model=MyModel)) + +Inline +------ + +``InlineFeatureFlag`` checks for any flag in app's config with `FEATURE_FLAGS_X` format, +where `X` is the name of a specific feature. + +The difference between this handler and default handler is, +instead of defining flag in ``dict``-style: + +.. sourcecode:: python + + FEATURE_FLAGS { + 'finished': False, + } + +the feature name must use uppercased plain string: + +.. sourcecode:: python + + FEATURE_FLAGS_FINISHED = False + +The motivation behind this inline handler is to interopt with other Flask extensions +that rely on environment variable, e.g. `Flask-AppConfig `_. + +Usage ++++++ + +A typical usage is as trivial as the following snippet: + +.. sourcecode:: python + + from flask import Flask + import flask_featureflags as feature_flags + from flask_featureflags.contrib.inline import InlineFeatureFlag + + # feature flags config + FEATURE_FLAGS_FINISHED = False + + app = Flask(__name__) + app.config.from_object(__name__) + ff = feature_flags.FeatureFlag(app) + ff.add_handler(InlineFeatureFlag()) + + @app.route("/") + def index(): + return "Homepage" + + @app.route("/new") + @feature_flags.is_active_feature("FINISHED", redirect_to="/") + def new(): + return "New feature" diff --git a/flask_featureflags/contrib/inline/__init__.py b/flask_featureflags/contrib/inline/__init__.py new file mode 100644 index 0000000..ce1fd42 --- /dev/null +++ b/flask_featureflags/contrib/inline/__init__.py @@ -0,0 +1,18 @@ +from flask import current_app +from flask.ext.featureflags import FEATURE_FLAGS_CONFIG +from flask.ext.featureflags import NoFeatureFlagFound +from flask.ext.featureflags import log + + +class InlineFeatureFlag(object): + def __call__(self, feature): + if not current_app: + log.warn(u"Got a request to check for {feature} but we're outside the request context. Returning False".format(feature=feature)) + return False + + feature_cfg = "{prefix}_{feature}".format(prefix=FEATURE_FLAGS_CONFIG, feature=feature) + + try: + return current_app.config[feature_cfg] + except KeyError: + raise NoFeatureFlagFound() diff --git a/tests/contrib/test_inline_handler.py b/tests/contrib/test_inline_handler.py new file mode 100644 index 0000000..ff3b9fb --- /dev/null +++ b/tests/contrib/test_inline_handler.py @@ -0,0 +1,42 @@ +import unittest + +import flask_featureflags as feature_flags +from flask_featureflags.contrib.inline import InlineFeatureFlag + +from tests.fixtures import app +from tests.fixtures import feature_setup + + +inline_feature_flag = InlineFeatureFlag() + + +class InlineFeatureFlagTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + feature_setup.add_handler(inline_feature_flag) + + @classmethod + def tearDownClass(cls): + feature_setup.clear_handlers() + + def setUp(self): + self.app_ctx = app.app_context() + self.app_ctx.push() + app.config["FEATURE_FLAGS_ACTIVE"] = True + app.config["FEATURE_FLAGS_INACTIVE"] = False + + def tearDown(self): + self.app_ctx.pop() + + def test_flag_active(self): + self.assertTrue(feature_flags.is_active("ACTIVE")) + + def test_flag_inactive(self): + self.assertFalse(feature_flags.is_active("INACTIVE")) + + def test_flag_not_found(self): + self.assertFalse(feature_flags.is_active("NOT_FOUND")) + + def test_flag_not_found_raise_handler_exception(self): + self.assertRaises(feature_flags.NoFeatureFlagFound, + inline_feature_flag, "NOT_FOUND") From 45a7bd8511cd4ab15ddebe33b8964f9e28869a38 Mon Sep 17 00:00:00 2001 From: Isman Firmansyah Date: Mon, 13 Oct 2014 06:08:07 +0700 Subject: [PATCH 2/2] fix indentation and conforms to style guide --- flask_featureflags/contrib/inline/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask_featureflags/contrib/inline/__init__.py b/flask_featureflags/contrib/inline/__init__.py index ce1fd42..cb4a413 100644 --- a/flask_featureflags/contrib/inline/__init__.py +++ b/flask_featureflags/contrib/inline/__init__.py @@ -7,8 +7,8 @@ class InlineFeatureFlag(object): def __call__(self, feature): if not current_app: - log.warn(u"Got a request to check for {feature} but we're outside the request context. Returning False".format(feature=feature)) - return False + log.warn(u"Got a request to check for {feature} but we're outside the request context. Returning False".format(feature=feature)) + return False feature_cfg = "{prefix}_{feature}".format(prefix=FEATURE_FLAGS_CONFIG, feature=feature)