File indexing completed on 2024-12-01 13:02:57

0001 #!/usr/bin/python2.5
0002 # -*- coding: utf-8 -*-
0003 ############################################################################
0004 # sealed.py                                                                #
0005 #                                                                          #
0006 # Copyright (C) 2007 by Simon Edwards <simon@simonzone.com>                #
0007 #                                                                          #
0008 # This program is free software; you can redistribute it and/or modify     #
0009 # it under the terms of the GNU General Public License as published by     #
0010 # the Free Software Foundation; either version 2 of the License, or        #
0011 # (at your option) any later version.                                      #
0012 ############################################################################
0013 import sys
0014 
0015 def sealed(func):
0016     """Decorator to seal an object after initialisation
0017     
0018     This decorator can be used to enforce the initialisation pattern where
0019     any attributes are initialised in __init__() and no other attributes
0020     can be added once initialisation has been done.
0021     
0022     This decorator should be applied to __init__ methods.
0023     
0024     >>> x = MyNormalClass()
0025     >>> x.new_attribute_y = True   # <- Succeeds as normal
0026     
0027     >>> x = MySealedClass()
0028     >>> x.new_attribute_y = True   # <- Raises an AttributeError
0029     """
0030     def sealing_init(self, *args, **kw):
0031         func(self, *args, **kw)
0032         
0033         # Do not do anything if we are called from the __init__ method of a subclass.
0034         caller = sys._getframe(1)
0035         if caller.f_code.co_name=='__init__':
0036             if 'self' in caller.f_locals:
0037                 if caller.f_locals['self'] is self:
0038                     return
0039 
0040         # Only create the wedge class once.
0041         if sealing_init.wedge_class is None:
0042             class wedge_class(self.__class__):
0043                 def __setattr__(self,name,value):
0044                     getattr(self,name)
0045                     #if name not in self.__dict__:  
0046                     #    raise AttributeError("No new attributes may be added to this object.")
0047                     super(wedge_class,self).__setattr__(name,value)
0048             sealing_init.wedge_class = wedge_class
0049             sealing_init.wedge_class.__name__ = self.__class__.__name__+" @sealed"
0050         # Replace the class of self with a new one that has a modified __setattr__.
0051         self.__class__ = sealing_init.wedge_class
0052         
0053     sealing_init.wedge_class = None
0054     return sealing_init