atom feed13 messages in org.python.python-listDefining a method final
FromSent OnAttachments
Fabio CornetiJun 11, 2002 4:35 am 
holger krekelJun 11, 2002 4:47 am 
Fabio CornetiJun 11, 2002 5:44 am 
Jerzy KarczmarczukJun 11, 2002 5:54 am 
Emile van SebilleJun 11, 2002 6:01 am 
holger krekelJun 11, 2002 6:32 am 
Jerzy KarczmarczukJun 11, 2002 6:42 am 
David LeBlancJun 11, 2002 6:52 am 
David LeBlancJun 11, 2002 7:19 am 
Emile van SebilleJun 11, 2002 7:21 am 
Eric BrunelJun 11, 2002 8:35 am 
Jeff EplerJun 11, 2002 9:20 am 
Richard BarrettJun 11, 2002 10:09 am 
Subject:Defining a method final
From:Jeff Epler (jep@unpythonic.net)
Date:Jun 11, 2002 9:20:18 am
List:org.python.python-list

The following code uses a metaclass to attempt to enforce "final" methods. Warning: Use of metaclasses has exploded brains. Accessing directly through __dict__ or object.__setattr__ can bypass these protections.

def finalmethod(f): """Marks a method as a final method. Use like classmethod(), etc. Only works in subclasses of FinalMethods: class C(FinalMethods): def f(x): ... f = finalmethod(f)""" f.__final__ = 1 return f

class FinalMethodsMeta(type): """Metatype for FinalMethods""" def __init__(cls, name, bases, dict): super(FinalMethodsMeta, cls).__init__(name, bases, dict) for base in cls.__mro__: if base is cls: continue for attrname in base.__dict__: attr = base.__dict__[attrname] if hasattr(attr, "__final__") and attr.__final__: func = getattr(cls, attrname).im_func print attrname, attr, func if func is not attr: raise ValueError, \ "Subclasses of %s may not override the method %s" \ % (base.__name__, attrname)

def __setattr__(cls, name, attr): oldval = getattr(cls, name, None) if hasattr(oldval, "__final__") and oldval.__final__: raise ValueError, "final methods may not be overridden by assignment to
class" super(FinalMethodsMeta, cls).__setattr__(name, attr)

class FinalMethods: """Subclasses of FinalMethods may define a method to be a finalmethod(). This probably only works with instancemethods, not classmethods or staticmethods. A subclass may not override a finalmethod"""

__metaclass__ = FinalMethodsMeta

def __setattr__(self, name, attr): oldval = getattr(self, name, None) if hasattr(oldval, "__final__") and oldval.__final__: raise ValueError, "final methods may not be overridden by assignment to
instance" super(FinalMethods, self).__setattr__(name, attr)

def _test(): class c1(FinalMethods): def f(x): pass f = finalmethod(f)

def g(x): pass

class c2(FinalMethods): def g(x): pass g = finalmethod(g)

# Test that FinalMethods classes can be subclassed when they don't # interfere with each others' final methods class c3(c2, c1): pass assert c3.f.im_func is c1.f.im_func

# Test that a FinalMethods class can be subclassed when the added # methods do not interfere with the super's final methods class c4(c1): def g(x): pass

# Test that when a final method is concealed by a non-final method, # it blows up try: class c5(c1, c2): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" print c5.g, c1.g, c2.g raise RuntimeError

# Test that when a final method is overridden in the subclass, it # blows up try: class c5(c1): def f(x): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print c5.f, c1.f print "Did not catch expected error" raise RuntimeError

# Test that when a final method is overridden in the sub-subclass, # it blows up try: class c6(c3): def f(x): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print c6.f, c3.f print "Did not catch expected error" raise RuntimeError

# Test that assigning to non-final attributes works (class) class c7(c1): pass c7.g = lambda: None

# Test that assigning to final attributes fails (class) try: class c7(c3): pass c7.f = None except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" raise RuntimeError

# Test that assigning to non-final attributes works (instance) i = c1() i.g = None

# Test that assigning to final attributes fails (instance) try: i = c1(); i.f = None except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" raise RuntimeError

print "seems ta work"

if __name__ == '__main__': _test()