예제 #1
0
    def __init__(self, app=None, **configuration):
        r"""
        :param Flask app:
            The Flask app (or Blueprint) to wrap.
        :param \**configuration:
            Configuration options that define in what way a :any:`MicronMethod`
            that is created using this Micron instance must behave. These
            configuration options can be overridden by method-specific
            configuration options, defined in the ``@micron.method()``
            decorator.

        Example::

            from flask import Flask
            from flask_micron import Micron

            app = Flask(__name__)
            micron = Micron(app)

            @micron.method()
            def hello():
                return "Hello, world!"
        """
        self.config = MicronMethodConfig(**configuration)

        self.plugins = plugin.Container(json_input.Plugin(),
                                        normalize_input.Plugin(),
                                        call_function.Plugin(),
                                        json_output.Plugin())

        self.app = None
        if app is not None:
            self.init_app(app)
예제 #2
0
 def test_OptionNamesProvidesListOfAllOptionsNamesInHierarchy(self):
     level1 = MicronMethodConfig(my='option')
     level2 = MicronMethodConfig(level1, another='option')
     level3 = MicronMethodConfig(level2, final='option')
     self.assertEqual({'my'}, level1.option_names)
     self.assertEqual({'my', 'another'}, level2.option_names)
     self.assertEqual({'my', 'another', 'final'}, level3.option_names)
예제 #3
0
    def test_DerivedConfigCanOverrideValueWithNoneValue(self):
        level1 = MicronMethodConfig().configure(option1=2, option2=3)
        level2 = MicronMethodConfig(parent=level1).configure(option1=None)

        self.assertEqual(2, level1.option1)
        self.assertEqual(3, level1.option2)

        self.assertEqual(None, level2.option1)
        self.assertEqual(3, level2.option2)
예제 #4
0
    def test_WhenValueIsNotSet_ValueIsRetrievedFromParent(self):
        level1 = MicronMethodConfig(option1=2, option2=3)
        level2 = MicronMethodConfig(parent=level1, option1='a')
        level3 = MicronMethodConfig(parent=level2, option2='x')

        self.assertEqual(2, level1.option1)
        self.assertEqual(3, level1.option2)

        self.assertEqual('a', level2.option1)
        self.assertEqual(3, level2.option2)

        self.assertEqual('a', level3.option1)
        self.assertEqual('x', level3.option2)
예제 #5
0
    def test_CompileDerivedPlugin(self):
        class DerivedPlugin(flask_micron.Plugin):
            def normalize_input(self, ctx):
                ctx.input = "DerivedPlugin input %s" % ctx.config.option1

            def process_output(self, ctx):
                ctx.output = "%s %s" % (ctx.config.option1, ctx.config.option2)

        hooks = flask_micron.plugin.Compiler().compile(DerivedPlugin())

        # Check for extraction of the correct hook functions.
        self.assertEqual({'normalize_input', 'process_output'},
                         set(hooks.keys()))

        # Check if the compiled hook functions can be called.
        config = MicronMethodConfig(option1='value1', option2='value2')
        ctx = flask_micron.plugin.Context()
        ctx.config = config
        ctx.input = 'orig input'
        hooks['normalize_input'](ctx)
        self.assertEqual('DerivedPlugin input value1', ctx.input)
        self.assertEqual(None, ctx.output)
        hooks['process_output'](ctx)
        self.assertEqual('DerivedPlugin input value1', ctx.input)
        self.assertEqual('value1 value2', ctx.output)
예제 #6
0
 def test_HookMethodsInPluginsCanBeRun(self):
     container = plugin.Container()
     container.add(Dummy())
     config = MicronMethodConfig(dommy='yo')
     ctx = plugin.Context()
     ctx.config = config
     container.call_all(ctx, 'process_output')
     self.assertEqual('I made it, yo', ctx.output)
예제 #7
0
 def test_FlattenedProvidesResolvedDictOfAllOptions(self):
     level1 = MicronMethodConfig(first='value1', one=1)
     level2 = MicronMethodConfig(level1, one='one', second='value2')
     level3 = MicronMethodConfig(level2, second='value two', third='value3')
     self.assertEqual({'first': 'value1', 'one': 1}, level1.flattened)
     self.assertEqual({
         'first': 'value1',
         'one': 'one',
         'second': 'value2'
     }, level2.flattened)
     self.assertEqual(
         {
             'first': 'value1',
             'one': 'one',
             'second': 'value two',
             'third': 'value3'
         }, level3.flattened)
예제 #8
0
 def test_PluginsCanBeDuckTyped(self):
     container = plugin.Container()
     container.add(Ducky())
     config = MicronMethodConfig()
     ctx = plugin.Context()
     ctx.config = config
     container.call_all(ctx, 'normalize_input')
     container.call_all(ctx, 'process_output')
     self.assertEqual('Quack!', ctx.output)
예제 #9
0
    def test_CompileDuckTypedPlugin(self):
        class DuckTypedPlugin(object):
            def normalize_input(self, ctx):
                duck = ctx.config.duck
                ctx.input = "DuckTypedPlugin input %s" % duck

        hooks = flask_micron.plugin.Compiler().compile(DuckTypedPlugin())

        # Check for extraction of the correct hook functions.
        self.assertEqual({'normalize_input'}, set(hooks.keys()))

        # Check if the compiled hook functions can be called.
        config = MicronMethodConfig(duck='Dagobert')
        ctx = flask_micron.plugin.Context()
        ctx.config = config
        hooks['normalize_input'](ctx)
        self.assertEqual('DuckTypedPlugin input Dagobert', ctx.input)
예제 #10
0
    def test_CompileDictPlugin(self):
        def process_output(ctx):
            simple = ctx.config.simple
            ctx.output = simple * simple

        dict_plugin = {'process_output': process_output}

        hooks = flask_micron.plugin.Compiler().compile(dict_plugin)

        # Check for extraction of the correct hook functions.
        self.assertEqual({'process_output'}, set(hooks.keys()))

        # Check if the compiled hook functions can be called.
        config = MicronMethodConfig(simple=11)
        ctx = flask_micron.plugin.Context()
        ctx.config = config
        hooks['process_output'](ctx)
        self.assertEqual(121, ctx.output)
예제 #11
0
    def test_CompileConstructedPlugin(self):
        def process_output(ctx):
            ctx.output = ctx.config.drill

        class ConstructedPlugin:
            def __init__(self):
                self.process_output = process_output

        hooks = flask_micron.plugin.Compiler().compile(ConstructedPlugin())

        # Check for extraction of the correct hook functions.
        self.assertEqual({'process_output'}, set(hooks.keys()))

        # Check if the compiled hook functions can be called.
        config = MicronMethodConfig(drill=True)
        ctx = flask_micron.plugin.Context()
        ctx.config = config
        hooks['process_output'](ctx)
        self.assertEqual(True, ctx.output)
예제 #12
0
 def test_ValuesCanBeSetUsingConfigure(self):
     config = MicronMethodConfig().configure(option1=2, option2=3)
     self.assertEqual(2, config.option1)
     self.assertEqual(3, config.option2)
예제 #13
0
class Micron(object):
    """Used to decorate a regular function, to become an all singing and all
    dancing :any:`MicronMethod`, which is plugged into the routing of a
    `Flask`_ application.
    """
    def __init__(self, app=None, **configuration):
        r"""
        :param Flask app:
            The Flask app (or Blueprint) to wrap.
        :param \**configuration:
            Configuration options that define in what way a :any:`MicronMethod`
            that is created using this Micron instance must behave. These
            configuration options can be overridden by method-specific
            configuration options, defined in the ``@micron.method()``
            decorator.

        Example::

            from flask import Flask
            from flask_micron import Micron

            app = Flask(__name__)
            micron = Micron(app)

            @micron.method()
            def hello():
                return "Hello, world!"
        """
        self.config = MicronMethodConfig(**configuration)

        self.plugins = plugin.Container(json_input.Plugin(),
                                        normalize_input.Plugin(),
                                        call_function.Plugin(),
                                        json_output.Plugin())

        self.app = None
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """Initializes a Flask app as a Micron app.

        :param Flask app:
            The Flask app to initialize Micron for.

        Example::

            from flask import Flask
            from flask_micron import Micron

            micron = Micron()
            app = Flask(__name__)
            micron.init_app(app)
        """
        self.app = app

    def plugin(self, plugin_object):
        """Adds a :class:`Plugin <flask_micron.Plugin>` to this
        Micron object. See :ref:`user_plugins` for information on writing
        and using plugins.

        :param flask_micron.Plugin plugin_object:
            The plugin to add to this Micron object.

        :returns:
            This Micron instance, useful for fluent syntax.

        Example::

            from flask import Flask
            from flask_micron import Micron
            import my_stuff

            app = Flask(__name__)
            micron = Micron(app)

            my_plugin = my_stuff.Plugin()
            micron.plugin(my_plugin)
        """
        self.plugins.add(plugin_object)
        return self

    def configure(self, **configuration):
        r"""Updates the configuration for this Micron instance.

        :param \**configuration:
            Configuration options that define in what way Micron methods that
            are created using this Micron instance must behave. These
            configuration options can be overridden by method-specific
            configuration options, defined in the @micron.method(...)
            decorator.

        :returns:
            This Micron instance, useful for fluent syntax.

        Example::

            from flask import Flask
            from flask_micron import Micron

            app = Flask(__name__)
            micron = Micron(app).configure(option_name=some_value)

        Note: the last line is equivalent to::

            micron = Micron(app, option_name=some_value)
        """
        self.config.configure(**configuration)
        return self

    def method(self, rule=None, **configuration):
        r"""Decorates a function to make it work as a Micron method.

        :param string rule:
            The URL rule to use for this method. Default value:
            /<name of decorated function>
        :param \**configuration:
            Configuration options that define in what way the Micron method
            must behave. These configuration options can be used to override
            the default configuration as set for the Micron object.

        :returns:
            A decorator that will take care of embedding the Micron method
            in the Flask application and hooking it up with the Micron
            request handling.

        Example::

            from flask import Flask
            from flask_micron import Micron

            app = Flask(__name__)
            micron = Micron(app, x="default config value")

            @micron.method(x="function-specific config value")
            def hello(who='World'):
                return 'Hello, %s' % who
        """
        if self.app is None:
            raise ImplementationError(
                'The @micron.method decorator can only be used when '
                'the Micron class is linked to a Flask app')

        def _decorator(func, rule=rule):
            if rule is None:
                rule = _create_url_rule(func)
            wrapped = MicronMethod(self, func).configure(**configuration)
            self.app.add_url_rule(rule, view_func=wrapped, methods=['POST'])
            return func

        return _decorator
예제 #14
0
 def test_EmptyConfigCanBeCreated(self):
     MicronMethodConfig()
예제 #15
0
 def test_InvalidIdentifier_RaisesException(self):
     with self.assertRaises(ImplementationError):
         MicronMethodConfig(**{"I N V A L I D": "I D E N T I F I E R"})
예제 #16
0
 def test_NewOptionsCanBeAdded(self):
     level1 = MicronMethodConfig()
     level2 = MicronMethodConfig(level1)
     level1.my = 'option'
     self.assertEqual('option', level1.my)
     self.assertEqual('option', level2.my)
예제 #17
0
 def test_GivenEmptyConfigValue_WhenRetrievingValue_ExceptionIsRaised(self):
     with self.assertRaises(KeyError):
         MicronMethodConfig().nosuchvalue
예제 #18
0
 def test_ValuesCanBeSetInTheConstructor(self):
     config = MicronMethodConfig(option1=2, option2=3)
     self.assertEqual(2, config.option1)
     self.assertEqual(3, config.option2)
예제 #19
0
 def test_ValuesCanBeSetUsingSetters(self):
     config = MicronMethodConfig()
     config.option1 = 2
     config.option2 = 3
     self.assertEqual(2, config.option1)
     self.assertEqual(3, config.option2)