class Calattr(dict):
    """Class of overloading __getatr__ and __setattr__ and __delattr__
    __getitem__ and __delitem__
    
    It creates a dictionary where members can be accessed as attributes
    it allows build-in methods executable
    
    """
    # reserves space for the declared variables and prevents the automatic
    # creation of __dict__ and __weakref__ for each instance.
    # (Added in Python version 2.2)
    # Or it allow to get rid of __dict__ object, which created by
    # interpreter for each instance of class.
    #
    # Note that __slots__ is read-only (but now read-and-writable?) class atttribute
    
    __slots__ = ['default', '__dict__']

    def __init__(self, default=None):
        dict.__init__(self)
        
        self.default = default

        #after initialisation, setting attributes is the same as setting an item
        
    def __getattr__(self, item):
        """Maps values to attributes.
        Only called if there *isn't* an attribute with this name
        """
        #set to lowercase
        item=item.lower()
        if item in self:  #for puthon2.2 or later
            return dict.__getitem__(self, item) #self.__getitem__(item)
        else:
            return self.default


    def __setattr__(self, item, value):
        """Maps attributes to values.
        Only if we are initialised
        """
        #set to lowercase
        item=item.lower()
        if not '_calattr_initialised' in self.__dict__.keys():  # this test allows attributes to be set in the __init__ method
            dict.__setattr__(self, item, value)
        elif item in self.__dict__.keys():  #self.__dict__.has_key(item):       # any normal attributes are handled normally
            dict.__setattr__(self, item, value)
        else:
            self.__setitem__(item, value)
            

    def __delattr__(self, item):
        """Delete the item with the name in the attributes.
        Only called if there *isn't* an attribute with this name
        """
        
        #set to lowercase
        item=item.lower()
        try:
            if item in self.__dict__.keys():
                del self.__dict__[item]
            elif item in self:
                dict.__delattr__(self, item)
            else:
                print ("KeyWarn: ", repr(str(item))," not found" )
        except KeyError:
            raise AttributeError(item)


    @property            
    def dictitems(self):
        "Returns a list of (key, value) or (key, tuple-value) tuple pairs."
        return self.items()
    @property            
    def attrdicts(self):
        "Returns a dict of 'key:value' or 'key:tuple-value' pairs."
        return self.__dict__
    
class Caldict(dict):

    __slots__ = ['default', '__dict__'] 
    
    def __init__(self, default=None):
        dict.__init__(self)
        
        self.default = default
    def __getitem__(self, key):
        """ python 2.2 or later for for key in dict """

        if key in self:
            return dict.__getitem__(self, key)
        else:
            return self.default
        
    def __delitem__(self, item):
        """Delete the key with the name in the dict
        Only called if there *isn't* a key with this name
        """
        if item in self:
            dict.__delitem__(self, item)
        else:
            print ("KeyWarn:", repr(str(item))," not found" )
            


    def merge(self, other):
        for key in other:
            if key not in self:
                self[key] = other[key]
                
    @property            
    def dictitems(self):
        "Returns a list of (key, value) or (key, tuple-value) tuple pairs."
        return self.items()
    @property            
    def attrdicts(self):
        "Returns a dict of 'key:value' or 'key:tuple-value' pairs."
        return self.__dict__
  
    
def test_dict():
    a = Caldict(default=-1) 
    print ("""a["noway"]: """,a["noway"]  ) #-1
    a.default = -1000
    print ("""a["noway"]: """,a["noway"]  ) #-1000
    print ("a: ",a  )#{}
    
    print ("a.key: ", a.keys() )
    a["z1"] = 100
    a["xqh"] = "I'm"
    print ("a.keys(): ", a.keys() ) #['z1']
    print ("a.items(): ", a.items()) #['z1']

    a.x1=23
    a.x2=32
    print ("a.__dict__: ", a.__dict__ ) #{'x2': 32, 'x1': 23, '_attr': -1000}
    print ("a: ",a)

    a[1] = 3.25
    a.merge({1:100, 2:200})  
    print ("a after merge: ", a ) #{1: 3.25, 2: 200}
    print ("a.keys(): ", a.keys())
    print ("""a["noway2"]: """, a["noway2"]  )

    print ("a.attrdicts: ",a.attrdicts ) #{'x2': 32, 'x1': 23}
    print ("a.dictitems: ",a.dictitems ) #[('xqh', "I'm"), (1, 3.25), (2, 200), ('z1', 100)]
    a.grating="GRAT"
    a["grating"]="dictGRAT"
    print ("grat, a.attrdicts: ",a.attrdicts)
    print ("grat, a.dictitems: ",a.dictitems)
    del a.grating
    del a["grating"]
    print ("post-del-grat,a.attrdicts: ",a.attrdicts)
    print ("post-del-grat,a.dictitems: ",a.dictitems)
    del a["nograting"]
    
def test_itemattr():
    a = Calattr() 
    print ("""a["noway"]: """,a["noway"]  )#None
    print ("a.x: ", a.x  )#None
    a.default = -1000
    print ("""a["noway"]: """,a["noway"]  )#-1000
    print ("a: ",a ) #{}

    print ("a.key: ", a.keys() )#[]
    a["z1"] = 100
    a["xqh"] = "I'm"
    print ("a.keys(): ", a.keys() )#['z1']
    print ("a.items(): ", a.items() )#['z1']

    a.x1=23
    a.x2=32
    print ("a.__dict__: ", a.__dict__ )#{'x2': 32, 'x1': 23, '_attr': -1000}
    print ("a: ",a)

    print ("a.attrdicts: ",a.attrdicts)
    print ("a.dictitems: ",a.dictitems)
    a.grating="GRAT"
    a["grating"]="dictGRAT"
    print ("grat, a.attrdicts: ",a.attrdicts)
    print ("grat, a.dictitems: ",a.dictitems)
    del a.grating
    del a["grating"]
    print ("post-del-grat,a.attrdicts: ",a.attrdicts)
    print ("post-del-grat,a.dictitems: ",a.dictitems)
    del a["nograting"]
    del a.nograting
def test_attr():
    a = Calattr() 
    print ("a.x: ", a.x  )#None
    a.default = -1000
    print ("a: ",a  )#{}

    print ("a.keys(): ", a.keys() )#[]
    a["z1"] = 100
    a["xqh"] = "I'm"
    print ("a.keys(): ", a.keys() )#['z1']
    print ("a.items(): ", a.items() )#['z1']

    a.x1=23
    a.x2=32,'mm'
    print ("a.__dict__:         ", a.__dict__) #{'x2': (32, 'mm'), 'x1': 23}
    print ("a.__dict__.items(): ", a.__dict__.items() )#[('x2', (32, 'mm')), ('x1', 23)]
    print ("a: ",a)

    print ("a.attrdicts: ",a.attrdicts )#{'x2': (32, 'mm'), 'x1': 23}

    print ("a.dictitems: ",a.dictitems )#[('xqh', "I'm"), ('z1', 100)]
    a.grating="GRAT"
    a["grating"]="dictGRAT"
    print ("grat, a.attrdicts: ",a.attrdicts)
    print ("grat, a.dictitems: ",a.dictitems)
    del a.grating
    del a["grating"]
    print ("post-del-grat,a.attrdicts: ",a.attrdicts)
    print ("post-del-grat,a.dictitems: ",a.dictitems)
    del a.nograting

def test_libattr():
    par = Calattr() #attribute='setparam')
    #print (par.attr

    par.tele= "chandra"
    par.inst= 'acis'
    par.prod='badpix'
    par.start="2010-10-06T23:19:00"
    par.start="2010-10-06T15:19:00"
    par.stop="2010-10-06T23:19:00"
    par.fidal= 'High'
    par.qual= 2
    par.grating= "none"
    par.fp_temp= 145.0,"K"
    print ("qual=",par.qual)
    print ("fidal=",par.fidal)
    print ("grating=",par.grating)
    print ("Get all:\n",par.getall)
    del par.grating
    del par.qual
    print ("Get all:\n",par.getall)
    allpars=par.getall
    if "prod" not in allpars:
        print ("error: prod")
    else:
        print (par.prod)

def test_slots():
    par = Itemattr() #attribute='setparam')
    slots= par.__slots__
    print ("slots: ", slots)
    par.__slots__ +="x","y"
    print ("updated slots: ", par.__slots__)
    
if __name__ ==  '__main__':
    #test_dict()
    test_attr()
    #test_slots()
