New Python class helpers.overlay.CfgCache.
* New: In CRL/helpers/overlay.py, CfgCache class to hold a set of configuration parameters and apply it on demand. It has a different behavior than Configuration.
This commit is contained in:
parent
3996a8e15d
commit
1e3788d93e
|
@ -1,13 +1,31 @@
|
||||||
|
# -*- mode:Python; explicit-buffer-name: "__init__.py<crlcore/helpers>" -*-
|
||||||
|
#
|
||||||
|
# This file is part of the Coriolis Software.
|
||||||
|
# Copyright (c) UPMC 2012-2018, All Rights Reserved
|
||||||
|
#
|
||||||
|
# +-----------------------------------------------------------------+
|
||||||
|
# | C O R I O L I S |
|
||||||
|
# | C o r i o l i s / C h a m s B u i l d e r |
|
||||||
|
# | |
|
||||||
|
# | Author : Jean-Paul Chaput |
|
||||||
|
# | E-mail : Jean-Paul.Chaput@lip6.fr |
|
||||||
|
# | =============================================================== |
|
||||||
|
# | Python : "./crlcore/helpers/overlay.py" |
|
||||||
|
# +-----------------------------------------------------------------+
|
||||||
|
#
|
||||||
|
# Those classes are based on the work of Jock Tanner from Libre-SOC.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Overlay to make some C++ objects provide a more Pythonic interface.
|
Overlay to make some C++ objects provide a more Pythonic interface.
|
||||||
Contains:
|
Contains:
|
||||||
|
|
||||||
* ``overlay.UpdateSession`` : to be used in ``with`` construct.
|
* ``overlay.UpdateSession`` : to be used in ``with`` construct.
|
||||||
* ``overlay.Cfg`` : to be used in ``with`` construct.
|
* ``overlay.Configuration`` : to be used in ``with`` construct.
|
||||||
|
* ``overlay.CfgCache`` : A cache for Cfg parameters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
import Cfg
|
import Cfg
|
||||||
import Hurricane
|
import Hurricane
|
||||||
|
|
||||||
|
@ -64,6 +82,7 @@ class Configuration:
|
||||||
Cfg.getParamInt(attr).setInt( val )
|
Cfg.getParamInt(attr).setInt( val )
|
||||||
elif isinstance(val, long):
|
elif isinstance(val, long):
|
||||||
p = Cfg.getParamInt( attr ) # all params have a type
|
p = Cfg.getParamInt( attr ) # all params have a type
|
||||||
|
p.setInt( val )
|
||||||
elif isinstance(val, float):
|
elif isinstance(val, float):
|
||||||
p = Cfg.getParamDouble( attr ).setDouble( val )
|
p = Cfg.getParamDouble( attr ).setDouble( val )
|
||||||
elif '%' in val:
|
elif '%' in val:
|
||||||
|
@ -75,3 +94,167 @@ class Configuration:
|
||||||
if self._priority is not None:
|
if self._priority is not None:
|
||||||
Cfg.Configuration.popDefaultPriority()
|
Cfg.Configuration.popDefaultPriority()
|
||||||
|
|
||||||
|
|
||||||
|
class CfgCache ( object ):
|
||||||
|
"""
|
||||||
|
CgfCache cache a set of configuration parameters. The values of the
|
||||||
|
parameters are not set in the system *until* the ``apply()`` function
|
||||||
|
is called.
|
||||||
|
|
||||||
|
If a parameter do not exists in the ``Cfg`` module, it is created
|
||||||
|
when ``apply()`` is called. Be aware that it is not able to guess
|
||||||
|
the right type between Double and Percentage or Int and Enumerate.
|
||||||
|
It will, by default, only create Double or Int. So, when setting
|
||||||
|
Percentage or Enumerate, be sure that they exists beforehand in
|
||||||
|
the ``Cfg`` module.
|
||||||
|
|
||||||
|
The attributes of CfgCache exactly mimic the behavior of the
|
||||||
|
``Cfg`` parameter string identifiers. For example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Direct access to a Cfg parameter.
|
||||||
|
p = Cfg.getParamInt('katana.eventsLimit').setInt( 4000000 )
|
||||||
|
|
||||||
|
# Setup of a CfgCache parameter.
|
||||||
|
cache = CfgCache('')
|
||||||
|
cache.katana.eventsLimit = 4000000
|
||||||
|
|
||||||
|
# ...
|
||||||
|
# Effective setting of the Cfg parameter.
|
||||||
|
cache.apply()
|
||||||
|
|
||||||
|
|
||||||
|
This is done by overloading ``__setattr__()`` and ``__getattr__()``
|
||||||
|
which recursively create CfgCache objects for intermediate levels
|
||||||
|
attributes (in the previous example, a CfgCache for ``katana``
|
||||||
|
will automatically be created). To separate between attributes
|
||||||
|
that are part of configuration parameters and attributes belonging
|
||||||
|
to CfgCache itself, we prepend a '_' to the laters.
|
||||||
|
|
||||||
|
.. note:: It is important to understand the difference of behavior
|
||||||
|
with ``Configuration``, the former set the parameters
|
||||||
|
at once, it directly act on the ``Cfg`` settings.
|
||||||
|
The later keep a state and set the ``Cfg`` parameters
|
||||||
|
*only* when ``apply()`` is called.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setCfgParameter ( paramPath, value ):
|
||||||
|
""""
|
||||||
|
Set the value of parameter ``paramPath`` to ``value`` in Cfg.
|
||||||
|
Percentage are set as Double and Enumerate as Int.
|
||||||
|
"""
|
||||||
|
if Cfg.hasParameter(paramPath):
|
||||||
|
confDb = Cfg.Configuration.get()
|
||||||
|
p = confDb.getParameter( paramPath )
|
||||||
|
else:
|
||||||
|
if isinstance(value,bool ): p = Cfg.getParamBool ( paramPath )
|
||||||
|
elif isinstance(value,int ): p = Cfg.getParamInt ( paramPath )
|
||||||
|
elif isinstance(value,long ): p = Cfg.getParamInt ( paramPath )
|
||||||
|
elif isinstance(value,float): p = Cfg.getParamDouble( paramPath )
|
||||||
|
else: p = Cfg.getParamString( paramPath )
|
||||||
|
|
||||||
|
if p.type == Cfg.Parameter.Type.Enumerate: p.setInt ( value )
|
||||||
|
elif p.type == Cfg.Parameter.Type.Int: p.setInt ( value )
|
||||||
|
elif p.type == Cfg.Parameter.Type.Bool: p.setBool ( value )
|
||||||
|
elif p.type == Cfg.Parameter.Type.Double: p.setDouble( value )
|
||||||
|
elif p.type == Cfg.Parameter.Type.Percentage: p.setDouble( value*100.0 )
|
||||||
|
else: p.setString( str(value) )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getDefaultCfgParameter ( paramPath ):
|
||||||
|
""""Get the value of parameter ``paramPath`` from Cfg."""
|
||||||
|
if not Cfg.hasParameter(paramPath):
|
||||||
|
raise AttributeError( 'CfgCache.getDefaultCfgParameter(): Undefined "{}"'.format(paramPath) )
|
||||||
|
confDb = Cfg.Configuration.get()
|
||||||
|
p = confDb.getParameter( paramPath )
|
||||||
|
if p:
|
||||||
|
if p.type == Cfg.Parameter.Type.Enumerate: return p.asInt()
|
||||||
|
if p.type == Cfg.Parameter.Type.Int: return p.asInt()
|
||||||
|
if p.type == Cfg.Parameter.Type.Bool: return p.asBool()
|
||||||
|
if p.type == Cfg.Parameter.Type.String: return p.asString()
|
||||||
|
if p.type == Cfg.Parameter.Type.Double: return p.asDouble()
|
||||||
|
if p.type == Cfg.Parameter.Type.Percentage: return p.asDouble()/100.0
|
||||||
|
return p.asString()
|
||||||
|
|
||||||
|
def __init__ ( self, path ):
|
||||||
|
"""Create a new CfgCache with a ``path`` as parent path."""
|
||||||
|
self._path = path
|
||||||
|
self._rattr = {}
|
||||||
|
return
|
||||||
|
|
||||||
|
def __setattr__ ( self, attr, v ):
|
||||||
|
"""
|
||||||
|
Recursively set an attribute. Attributes names starting by an '_' are
|
||||||
|
treated as belonging to *this* object (self).
|
||||||
|
|
||||||
|
How does the recursive attributes/CfgCache works? Assumes that we
|
||||||
|
are doing:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Setup of a CfgCache parameter.
|
||||||
|
cache = CfgCache('')
|
||||||
|
cache.katana.eventsLimit = 4000000
|
||||||
|
|
||||||
|
The explicit call sequence will be:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
cache.__getattr__('katana').__setattr__( 'eventsLimit', 4000000 )
|
||||||
|
|
||||||
|
1. For the intermediate hierarchy level ``katana``, it is __getattr__()
|
||||||
|
which is called, if the attribute do not exists, we create a new
|
||||||
|
CfgCache().
|
||||||
|
|
||||||
|
2. Second, and only then, __setattr__() is called, which will create a
|
||||||
|
parameter entry named ``eventsLimit``.
|
||||||
|
|
||||||
|
The decision of whether create a parameter entry *or* a CfgCache
|
||||||
|
intermediate level will always be correctly handled because prior
|
||||||
|
to any access, an attribute needs to be set. So we always have
|
||||||
|
first a call chain of __getattr__() with one final __setattr__().
|
||||||
|
For any subsequent access to ``cache.katana.eventsLimit``, as
|
||||||
|
the attribute already exists, there is no type creation problem.
|
||||||
|
"""
|
||||||
|
if attr[0] == '_':
|
||||||
|
object.__setattr__( self, attr, v )
|
||||||
|
return
|
||||||
|
if v is None:
|
||||||
|
v = CfgCache.getDefaultCfgParameter( self._path+'.'+attr )
|
||||||
|
self._rattr[ attr ] = v
|
||||||
|
return
|
||||||
|
|
||||||
|
def __getattr__ ( self, attr ):
|
||||||
|
"""
|
||||||
|
Get an attribute, if it doesn't exists, then we are in an intermediate
|
||||||
|
level like ``katana``, so create a new sub CfgCache for that attribute.
|
||||||
|
"""
|
||||||
|
if not self._rattr.has_key(attr):
|
||||||
|
path = self._path+'.'+attr if len(self._path) else attr
|
||||||
|
self._rattr[attr] = CfgCache( path )
|
||||||
|
return self._rattr[attr]
|
||||||
|
|
||||||
|
def apply ( self, priority=Cfg.Parameter.Priority.UserFile ):
|
||||||
|
"""Apply the parameters values stored in the cache to the ``Cfg`` database."""
|
||||||
|
if not len(self._path):
|
||||||
|
Cfg.Configuration.pushDefaultPriority( priority )
|
||||||
|
for attrName in self._rattr.keys():
|
||||||
|
if isinstance(self._rattr[attrName],CfgCache):
|
||||||
|
self._rattr[attrName].apply()
|
||||||
|
continue
|
||||||
|
CfgCache.setCfgParameter( self._path+'.'+attrName,self._rattr[attrName] )
|
||||||
|
if not len(self._path):
|
||||||
|
Cfg.Configuration.popDefaultPriority()
|
||||||
|
|
||||||
|
def display ( self ):
|
||||||
|
"""Print all the parameters stored in that CfgCache."""
|
||||||
|
if not len(self._path):
|
||||||
|
print( 'Configuration (Cfg) cache:' )
|
||||||
|
for attrName in self._rattr.keys():
|
||||||
|
if isinstance(self._rattr[attrName],CfgCache):
|
||||||
|
self._rattr[attrName].display()
|
||||||
|
continue
|
||||||
|
print( '* {}.{} = {}'.format(self._path,attrName,self._rattr[attrName]) )
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue