From fb7d6c605c46aa6373e8b3cf121527acf011b980 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Tue, 12 Jul 2016 00:19:37 +0200 Subject: Server/Plugin: Move OnDemandDict to helpers The OnDemandDict could be used by different plugins. --- src/lib/Bcfg2/Server/Plugin/helpers.py | 48 +++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'src/lib/Bcfg2/Server/Plugin/helpers.py') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 6b521dfd6..17363a675 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -13,7 +13,7 @@ import Bcfg2.Server import Bcfg2.Options import Bcfg2.Server.FileMonitor from Bcfg2.Logger import Debuggable -from Bcfg2.Compat import CmpMixin, wraps +from Bcfg2.Compat import CmpMixin, MutableMapping, wraps from Bcfg2.Server.Plugin.base import Plugin from Bcfg2.Server.Plugin.interfaces import Generator, TemplateDataProvider from Bcfg2.Server.Plugin.exceptions import SpecificityError, \ @@ -1698,3 +1698,49 @@ class GroupSpool(Plugin, Generator): return reqid = self.fam.AddMonitor(name, self) self.handles[reqid] = relative + + +class OnDemandDict(MutableMapping): + """ This maps a set of keys to a set of value-getting functions; + the values are populated on-the-fly by the functions as the values + are needed (and not before). This is for example used by + :func:`Bcfg2.Server.Plugins.Packages.Packages.get_additional_data`; + see the docstring for that function for details on why. + + Unlike a dict, you should not specify values for the righthand + side of this mapping, but functions that get values. E.g.: + + .. code-block:: python + + d = OnDemandDict(foo=load_foo, + bar=lambda: "bar"); + """ + + def __init__(self, **getters): + self._values = dict() + self._getters = dict(**getters) + + def __getitem__(self, key): + if key not in self._values: + self._values[key] = self._getters[key]() + return self._values[key] + + def __setitem__(self, key, getter): + self._getters[key] = getter + + def __delitem__(self, key): + del self._values[key] + del self._getters[key] + + def __len__(self): + return len(self._getters) + + def __iter__(self): + return iter(self._getters.keys()) + + def __repr__(self): + rv = dict(self._values) + for key in self._getters.keys(): + if key not in rv: + rv[key] = 'unknown' + return str(rv) -- cgit v1.2.3-1-g7c22 From 257ba8d92eda8be0a347f3d68b174a2354782578 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Tue, 12 Jul 2016 00:24:23 +0200 Subject: Server/Plugin: Support functions and values for OnDemandDict Now you can also specify simple values for the OnDemandDict. --- src/lib/Bcfg2/Server/Plugin/helpers.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/lib/Bcfg2/Server/Plugin/helpers.py') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 17363a675..9ef4a2527 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -1707,13 +1707,14 @@ class OnDemandDict(MutableMapping): :func:`Bcfg2.Server.Plugins.Packages.Packages.get_additional_data`; see the docstring for that function for details on why. - Unlike a dict, you should not specify values for the righthand - side of this mapping, but functions that get values. E.g.: + Unlike a dict, you can specify values or functions for the + righthand side of this mapping. If you specify a function, it will + be evaluated, when you access the value for the first time. E.g.: .. code-block:: python d = OnDemandDict(foo=load_foo, - bar=lambda: "bar"); + bar="bar") """ def __init__(self, **getters): @@ -1722,7 +1723,11 @@ class OnDemandDict(MutableMapping): def __getitem__(self, key): if key not in self._values: - self._values[key] = self._getters[key]() + if callable(self._getters[key]): + self._values[key] = self._getters[key]() + else: + self._values[key] = self._getters[key] + return self._values[key] def __setitem__(self, key, getter): -- cgit v1.2.3-1-g7c22 From 9af8a7ea4b1de56ac6b8800277518e9159fb7dca Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Fri, 15 Jul 2016 20:27:19 +0200 Subject: Server/Plugin: CallableDict is an OnDemandDict without caching Add a CallableDict (like OnDemandDict, but without caching the results) to have a dict-like class, that can be cached with the client metadata without caching the dynamic values. --- src/lib/Bcfg2/Server/Plugin/helpers.py | 62 +++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 16 deletions(-) (limited to 'src/lib/Bcfg2/Server/Plugin/helpers.py') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 9ef4a2527..762d018eb 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -1700,7 +1700,7 @@ class GroupSpool(Plugin, Generator): self.handles[reqid] = relative -class OnDemandDict(MutableMapping): +class CallableDict(MutableMapping): """ This maps a set of keys to a set of value-getting functions; the values are populated on-the-fly by the functions as the values are needed (and not before). This is for example used by @@ -1709,32 +1709,27 @@ class OnDemandDict(MutableMapping): Unlike a dict, you can specify values or functions for the righthand side of this mapping. If you specify a function, it will - be evaluated, when you access the value for the first time. E.g.: + be evaluated everytime you access the value. E.g.: .. code-block:: python - d = OnDemandDict(foo=load_foo, + d = CallableDict(foo=load_foo, bar="bar") """ def __init__(self, **getters): - self._values = dict() self._getters = dict(**getters) def __getitem__(self, key): - if key not in self._values: - if callable(self._getters[key]): - self._values[key] = self._getters[key]() - else: - self._values[key] = self._getters[key] - - return self._values[key] + if callable(self._getters[key]): + return self._getters[key]() + else: + return self._getters[key] def __setitem__(self, key, getter): self._getters[key] = getter def __delitem__(self, key): - del self._values[key] del self._getters[key] def __len__(self): @@ -1743,9 +1738,44 @@ class OnDemandDict(MutableMapping): def __iter__(self): return iter(self._getters.keys()) - def __repr__(self): - rv = dict(self._values) + def _current_data(self): + """ Return a dict with the current available static data + and ``unknown`` for all callable values. + """ + rv = dict() for key in self._getters.keys(): - if key not in rv: + if callable(self._getters[key]): rv[key] = 'unknown' - return str(rv) + else: + rv[key] = self._getters[key] + return rv + + def __repr__(self): + return str(self._current_data()) + + +class OnDemandDict(CallableDict): + """ This is like a :class:`CallableDict` but it will cache + the results of the callable getters, so that it is only evaluated + once when you first access it. + """ + + def __init__(self, **getters): + CallableDict.__init__(self, **getters) + self._values = dict() + + def __getitem__(self, key): + if key not in self._values: + self._values[key] = super(OnDemandDict, self).__getitem__(key) + return self._values[key] + + def __delitem__(self, key): + super(OnDemandDict, self).__delitem__(key) + del self._values[key] + + def _current_data(self): + rv = super(OnDemandDict, self)._current_data() + for (key, value) in rv.items(): + if key in self._values: + rv[key] = value + return rv -- cgit v1.2.3-1-g7c22