diff --git a/marconi/common/decorators.py b/marconi/common/decorators.py index 4b1bbd906..12b0b3963 100644 --- a/marconi/common/decorators.py +++ b/marconi/common/decorators.py @@ -13,6 +13,32 @@ # See the License for the specific language governing permissions and # limitations under the License. +import functools + + +def cached_getattr(meth): + """Caches attributes returned by __getattr__ + + It can be used to cache results from + __getattr__ and reduce the debt of calling + it again when the same attribute is accessed. + + This decorator caches attributes by setting + them in the object itself. + + The wrapper returned by this decorator won't alter + the returned value. + + :returns: A wrapper around the decorated method. + """ + + @functools.wraps(meth) + def wrapper(self, method_name): + attr = meth(self, method_name) + setattr(self, method_name, attr) + return attr + return wrapper + def lazy_property(write=False, delete=True): """Creates a lazy property. diff --git a/marconi/common/pipeline.py b/marconi/common/pipeline.py index 85a999f7c..9d7cfaba0 100644 --- a/marconi/common/pipeline.py +++ b/marconi/common/pipeline.py @@ -34,6 +34,7 @@ import functools import six +from marconi.common import decorators import marconi.openstack.common.log as logging LOG = logging.getLogger(__name__) @@ -47,6 +48,7 @@ class Pipeline(object): def append(self, stage): self._pipeline.append(stage) + @decorators.cached_getattr def __getattr__(self, name): return functools.partial(self.consume_for, name) diff --git a/tests/unit/common/test_decorators.py b/tests/unit/common/test_decorators.py new file mode 100644 index 000000000..936b94b6a --- /dev/null +++ b/tests/unit/common/test_decorators.py @@ -0,0 +1,33 @@ +# Copyright (c) 2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from marconi.common import decorators +from marconi.tests import base + + +class TestDecorators(base.TestBase): + + def test_cached_getattr(self): + + class TestClass(object): + + @decorators.cached_getattr + def __getattr__(self, name): + return name + + instance = TestClass() + result = instance.testing + self.assertEqual(result, 'testing') + self.assertIn('testing', instance.__dict__)