I tried to create dynamic object to validate my config in fly and present result as object. I tried to achieve this by creating such class:
class SubConfig(object):
def __init__(self, config, key_types):
self.__config = config
self.__values = {}
self.__key_types = key_types
def __getattr__(self, item):
if item in self.__key_types:
return self.__values[item] or None
else:
raise ValueError("No such item to get from config")
def __setattr__(self, item, value):
if self.__config._blocked:
raise ValueError("Can't change values after service has started")
if item in self.__key_types:
if type(value) in self.__key_types[item]:
self.__values[item] = value
else:
raise ValueError("Can't assing value in different type then declared!")
else:
raise ValueError("No such item to set in config")
SubConfig is wrapper for section in config file. Config has switch to kill possibility to change values after program started (you can change values only on initialization).
The problem is when I setting any value it is getting in infinity loop in getattr. As I read __getattr__ shouldn’t behave like that (first take existing attr, then call __getattr__). I was comparing my code with available examples but I can’t get a thing.
I noticed that all problems are generated my constructor.
Cf ecatmur’s answer for the root cause – and remember that
__setattr__is not symetrical to__getattr__– it is unconditionnaly called on each and every attempt to bind an object’s attribute. Overriding__setattr__is tricky and should not be done if you don’t clearly understand the pros and cons.Now for a simple practical solution to your use case: rewrite your initializer to avoid triggering setattr calls:
Note that I renamed your attributes to emulate the name-mangling that happens when using the double leading underscores naming scheme.