]> git.openstreetmap.org Git - osqa.git/blobdiff - forum/modules/decorators.py
Prevents questions on which the slug comes out empty to enter a redirect loop.
[osqa.git] / forum / modules / decorators.py
index a4c1fadd7dfacac0deb7250c85133732f453187d..13bddac89357785c8df24e9402483de3b5724e02 100644 (file)
 import inspect\r
 \r
 class DecoratableObject(object):\r
-    def __init__(self, fn):\r
+    MODE_OVERRIDE = 0\r
+    MODE_PARAMS = 1\r
+    MODE_RESULT = 2\r
+\r
+    def __init__(self, fn, is_method=False):\r
         self._callable = fn\r
+        self.is_method = is_method\r
+\r
+        self._params_decoration = None\r
+        self._result_decoration = None\r
+\r
+    def _decorate(self, fn, mode, **kwargs):\r
+        if mode == self.MODE_OVERRIDE:\r
+            self._decorate_full(fn, **kwargs)\r
+        elif mode == self.MODE_PARAMS:\r
+            self._decorate_params(fn)\r
+        elif mode == self.MODE_RESULT:\r
+            self._decorate_result(fn, **kwargs)\r
 \r
-    def decorate(self, fn, needs_origin):\r
+    def _decorate_full(self, fn, needs_origin=True):\r
         origin = self._callable\r
 \r
         if needs_origin:\r
-            self._callable = lambda *args, **kwargs: fn(origin, *args, **kwargs)\r
+            if self.is_method:\r
+                self._callable = lambda inst, *args, **kwargs: fn(inst, origin, *args, **kwargs)\r
+            else:\r
+                self._callable = lambda *args, **kwargs: fn(origin, *args, **kwargs)\r
         else:\r
-            self._callable = lambda *args, **kwargs: fn(*args, **kwargs)\r
+            self._callable = fn\r
+\r
+    def _decorate_params(self, fn):\r
+        if not self._params_decoration:\r
+            self._params_decoration = []\r
+\r
+        self._params_decoration.append(fn)\r
+\r
+    def _decorate_result(self, fn, needs_params=False):\r
+        if not self._result_decoration:\r
+            self._result_decoration = []\r
+\r
+        fn._needs_params = needs_params\r
+        self._result_decoration.append(fn)\r
 \r
     def __call__(self, *args, **kwargs):\r
-        return self._callable(*args, **kwargs)\r
+        if self._params_decoration:\r
+            for dec in self._params_decoration:\r
+                args, kwargs = dec(*args, **kwargs)\r
+\r
+        res = self._callable(*args, **kwargs)\r
+\r
+        if self._result_decoration:\r
+            for dec in self._result_decoration:\r
+                if dec._needs_params:\r
+                    res = dec(res, *args, **kwargs)\r
+                else:\r
+                    res = dec(res)\r
+\r
+        return res\r
+\r
+def _check_decoratable(origin, install=True):\r
+    if not isinstance(origin, DecoratableObject):\r
+        if inspect.ismethod(origin) and not hasattr(origin, '_decoratable_obj'):\r
+            decoratable = DecoratableObject(origin)\r
+\r
+            def decoratable_method(self, *args, **kwargs):\r
+                return decoratable(self, *args, **kwargs)\r
+\r
+            decoratable_method._decoratable_obj = decoratable\r
+\r
+            def decoratable_decorate(fn, mode, **kwargs):\r
+                decoratable._decorate(fn, mode, **kwargs)\r
+\r
+            decoratable_method._decorate = decoratable_decorate\r
+\r
+            if install:\r
+                setattr(origin.im_class, origin.__name__, decoratable_method)\r
 \r
+            return decoratable_method\r
+                \r
+        elif inspect.isfunction(origin):\r
+            decoratable = DecoratableObject(origin)\r
 \r
-def decoratable(fn):\r
-    return DecoratableObject(fn)\r
+            if install:\r
+                setattr(inspect.getmodule(origin), origin.__name__, decoratable)\r
 \r
-def decoratable_method(fn):\r
-    obj = DecoratableObject(fn)\r
-    def decorated(self, *args, **kwargs):\r
-        return obj(self, *args, **kwargs)\r
+            return decoratable\r
 \r
-    decorated.__obj = obj\r
-    return decorated\r
+    return origin\r
 \r
-decoratable.method = decoratable_method\r
 \r
 def decorate(origin, needs_origin=True):\r
-    if not isinstance(origin, DecoratableObject):\r
-        if hasattr(origin, '__obj'):\r
-            def decorator(fn):\r
-                origin.__obj.decorate(fn, needs_origin)\r
-                return origin\r
-            return decorator\r
+    origin = _check_decoratable(origin)\r
+\r
+    def decorator(fn):\r
+        origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=needs_origin)\r
+        \r
+    return decorator\r
 \r
-        raise TypeError('Not a decoratable function: %s' % origin.__name__)\r
+\r
+def _decorate_params(origin):\r
+    origin = _check_decoratable(origin)\r
 \r
     def decorator(fn):\r
-        origin.decorate(fn, needs_origin)\r
-        return origin\r
+        origin._decorate(fn, DecoratableObject.MODE_PARAMS)\r
 \r
     return decorator\r
 \r
+decorate.params = _decorate_params\r
+\r
+def _decorate_result(origin, needs_params=False):\r
+    origin = _check_decoratable(origin)\r
+\r
+    def decorator(fn):\r
+        origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
 \r
-def decorate_all(module):\r
-    [setattr(module, n, decoratable(f)) for n, f in\r
-        [(n, getattr(module, n)) for n in dir(module)]\r
-        if (callable(f)) and (not inspect.isclass(f)) and (f.__module__ == module.__name__)]\r
+    return decorator\r
 \r
+decorate.result = _decorate_result\r
 \r
+def _decorate_with(fn):\r
+    def decorator(origin):\r
+        origin = _check_decoratable(origin)\r
+        origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=True)\r
+        return origin\r
+    return decorator\r
 \r
+decorate.withfn = _decorate_with\r
 \r
+def _decorate_result_with(fn, needs_params=False):\r
+    def decorator(origin):\r
+        origin = _check_decoratable(origin)\r
+        origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
+        return origin\r
+    return decorator\r
 \r
+decorate.result.withfn = _decorate_result_with\r
 \r
+def _decorate_params_with(fn):\r
+    def decorator(origin):\r
+        origin = _check_decoratable(origin)\r
+        origin._decorate(fn, DecoratableObject.MODE_PARAMS)\r
+        return origin\r
+    return decorator\r
 \r
+decorate.params.withfn = _decorate_params_with
\ No newline at end of file