From 83e3c00809b38d26fd43a29fe2581c19731ae57e Mon Sep 17 00:00:00 2001 From: David Stanek Date: Thu, 1 Sep 2016 21:28:06 +0000 Subject: [PATCH] Only cache callables in the base manager The base manager had an issue where if a property was accessed through the __getattr__ it would be cached. Closes-Bug: 1620722 Change-Id: Iad7ca87a30fd5fa9f8bc88a0c7f74acca2ae1a56 --- keystone/common/manager.py | 5 ++- keystone/tests/unit/common/test_manager.py | 38 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/keystone/common/manager.py b/keystone/common/manager.py index 4ce9f2a621..569f154c6d 100644 --- a/keystone/common/manager.py +++ b/keystone/common/manager.py @@ -185,7 +185,10 @@ class Manager(object): def __getattr__(self, name): """Forward calls to the underlying driver.""" f = getattr(self.driver, name) - setattr(self, name, f) + if callable(f): + # NOTE(dstanek): only if this is callable (class or function) + # cache this + setattr(self, name, f) return f diff --git a/keystone/tests/unit/common/test_manager.py b/keystone/tests/unit/common/test_manager.py index 7ef91e156e..1cf2c45d77 100644 --- a/keystone/tests/unit/common/test_manager.py +++ b/keystone/tests/unit/common/test_manager.py @@ -38,3 +38,41 @@ class TestCreateLegacyDriver(unit.BaseTestCase): self.assertEqual('N', mock_reporter.call_args[0][2]['remove_in'][0]) self.assertIsInstance(impl, catalog.CatalogDriverV8) + + class Manager(manager.Manager): + + def __init__(self, driver): + # NOTE(dstanek): I am not calling the parent's __init__ on + # purpose. I don't want to trigger the dynamic loading of a + # driver, I want to provide my own. + self.driver = driver + + def test_property_passthru(self): + """Manager delegating property call to a driver through __getattr__.""" + class Driver(object): + + def __init__(self): + self.counter = 0 + + @property + def p(self): + self.counter += 1 + return self.counter + + mgr = self.Manager(Driver()) + # each property call should return a new value + self.assertNotEqual(mgr.p, mgr.p) + + def test_callable_passthru(self): + class Driver(object): + + class Inner(object): + pass + + def method(self): + pass + + drv = Driver() + mgr = self.Manager(drv) + self.assertEqual(drv.Inner, mgr.Inner) + self.assertEqual(drv.method, mgr.method)