Browse Source

App: Pending configuration should evaluate on any reading operation.

Ask Solem 8 years ago
parent
commit
ae9b152235
2 changed files with 39 additions and 15 deletions
  1. 32 7
      celery/app/base.py
  2. 7 8
      t/unit/app/test_app.py

+ 32 - 7
celery/app/base.py

@@ -113,14 +113,39 @@ class PendingConfiguration(UserDict, AttributeDictMixin):
     # replacing `app.conf` with a concrete settings object.
 
     callback = None
-    data = None
+    _data = None
 
     def __init__(self, conf, callback):
-        object.__setattr__(self, 'data', conf)
+        object.__setattr__(self, '_data', conf)
         object.__setattr__(self, 'callback', callback)
 
-    def __getitem__(self, key):
-        return self.callback(key)
+    def __setitem__(self, key, value):
+        self._data[key] = value
+
+    def clear(self):
+        self._data.clear()
+
+    def update(self, *args, **kwargs):
+        self._data.update(*args, **kwargs)
+
+    def setdefault(self, *args, **kwargs):
+        return self._data.setdefault(*args, **kwargs)
+
+    def __contains__(self, key):
+        # XXX will not show finalized configuration
+        # setdefault will cause `key in d` to happen,
+        # so for setdefault to be lazy, so does contains.
+        return key in self._data
+
+    def __len__(self):
+        return len(self.data)
+
+    def __repr__(self):
+        return repr(self.data)
+
+    @cached_property
+    def data(self):
+        return self.callback()
 
 
 @python_2_unicode_compatible
@@ -250,7 +275,7 @@ class Celery(object):
         self.__autoset('include', include)
         self._conf = Settings(
             PendingConfiguration(
-                self._preconf, self._get_from_conf_and_finalize),
+                self._preconf, self._finalize_pending_conf),
             prefix=self.namespace,
             keys=(_old_key_to_new, _new_key_to_old),
         )
@@ -855,7 +880,7 @@ class Celery(object):
             self.loader)
         return backend(app=self, url=url)
 
-    def _get_from_conf_and_finalize(self, key):
+    def _finalize_pending_conf(self):
         """Get config value by key and finalize loading the configuration.
 
         Note:
@@ -863,7 +888,7 @@ class Celery(object):
                 as soon as you access a key the configuration is read.
         """
         conf = self._conf = self._load_config()
-        return conf[key]
+        return conf
 
     def _load_config(self):
         if isinstance(self.on_configure, Signal):

+ 7 - 8
t/unit/app/test_app.py

@@ -152,7 +152,7 @@ class test_App:
 
     def test_add_defaults(self):
         assert not self.app.configured
-        _conf = {'FOO': 300}
+        _conf = {'foo': 300}
 
         def conf():
             return _conf
@@ -160,19 +160,19 @@ class test_App:
         self.app.add_defaults(conf)
         assert conf in self.app._pending_defaults
         assert not self.app.configured
-        assert self.app.conf.FOO == 300
+        assert self.app.conf.foo == 300
         assert self.app.configured
         assert not self.app._pending_defaults
 
         # defaults not pickled
         appr = loads(dumps(self.app))
         with pytest.raises(AttributeError):
-            appr.conf.FOO
+            appr.conf.foo
 
         # add more defaults after configured
-        conf2 = {'FOO': 'BAR'}
+        conf2 = {'foo': 'BAR'}
         self.app.add_defaults(conf2)
-        assert self.app.conf.FOO == 'BAR'
+        assert self.app.conf.foo == 'BAR'
 
         assert _conf in self.app.conf.defaults
         assert conf2 in self.app.conf.defaults
@@ -330,6 +330,7 @@ class test_App:
 
     def test_pending_configuration__setdefault(self):
         with self.Celery(broker='foo://bar') as app:
+            assert not app.configured
             app.conf.setdefault('worker_agent', 'foo:Bar')
             assert not app.configured
 
@@ -338,11 +339,9 @@ class test_App:
             app.conf.worker_agent = 'foo:Bar'
             assert not app.configured
             assert list(keys(app.conf))
-            assert not app.configured
+            assert app.configured
             assert 'worker_agent' in app.conf
-            assert not app.configured
             assert dict(app.conf)
-            assert app.configured
 
     def test_pending_configuration__raises_ImproperlyConfigured(self):
         with self.Celery(set_as_current=False) as app: