On 9 Apr 2005 03:49:19 -0700, "George Sakkis" <gs*****@rutger s.edu> wrote:
"Michael Spencer" <ma**@telcopart ners.com> wrote:
George,
since you explicit allowed metaprogramming hacks :-), how about
something like this (not tested beyond what you see):
[snipped]
Nice try, but ideally all boilerplate classes would rather be avoided
(at least being written explicitly). Also, it is not obvious in your
solution why and which placeholder classes have to be written (like
World2.Movable ) and which do not. By the way, my current working
solution involves copying and pasting verbatim these classes :-) Below
is an abstracted example; note that the 'declaration string' of each
original class is exactly the same across all different versions after
the first (e.g. "class B(PreviousNames pace.B, A)").
#============= =============== =============== ===========
# version_1.py
class Namespace:
class A(object):
def foo(self): return "version_1.foo( )"
class B(A):
def bar(self): return "version_1.bar( )"
class C(B):
def zen(self): return "version_1.zen( )"
#============= =============== =============== ===========
# version_2.py
from version_1 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
class A(PreviousNames pace.A):
def foo(self): return "version_2.foo( )"
class B(PreviousNames pace.B, A):
pass
class C(PreviousNames pace.C, B):
pass
#============= =============== =============== ===========
# version_3.py
from version_2 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
class A(PreviousNames pace.A):
pass
class B(PreviousNames pace.B, A):
def bar(self): return "version_3.bar( )"
class C(PreviousNames pace.C, B):
pass
#============= =============== =============== ===========
# test.py
# command: python test.py <#version>
def NamespaceFactor y(version):
return __import__("ver sion_%d" % version).Namesp ace
print NamespaceFactor y(2).B().foo() # "version_2.foo( )"
print NamespaceFactor y(3).C().bar() # "version_3.bar( )"
import sys, inspect
namespace = NamespaceFactor y(int(sys.argv[1]))
# print the __mro__ of each 'inner' class
for name,cls in inspect.getmemb ers(namespace,
inspect.isclass ):
print cls
for ancestor in cls.__mro__:
print "\t", ancestor
#============= =============== =============== ===========
See if this does what you want:
(Note that vermeta.py preliminarily writes out the three version_?.py files, so
you can just go to a temp directory and run python24 vermeta.py)
It makes the version files look a little more cluttered with the
open(..).write( '''\ ... ''') wrapping. E.g., 2 & 3 are just
----< version_2.py >-----------
from version_1 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
__metaclass__ = vars(PreviousNa mespace)['__metaclass__']
class A:
def foo(self): return "version_2.foo( )"
-------------------------------
and
----< version_3.py >-----------
from version_2 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
__metaclass__ = vars(PreviousNa mespace)['__metaclass__']
class B:
def bar(self): return "version_3.bar( )"
-------------------------------
And you only have to change one digit in the 3-line boilerplate
and your class definitions don't have to specify inheritance,
but it could test for type classobj (classic class) and only
redefine if so ;-)
There are some limitations I think (;-) but the idea is you just have to
specify three lines of boilerplate for a new version, and then just
the classes and methods you are interested in overriding in the new
version, and you don't have to specify the class inheritance in the new versions,
as the lazystyle metaclass function takes care of that. I hope ;-)
Version_1 has to be hand made, but after that, see what you think.
----< vermeta.py >---------------------------------------------------------------
#============== =============== =============== ==========
# version_1.py
open('version_1 .py','w').write ('''\
NAMESPACE_CLASS NAMES = ['A', 'B', 'C']
def metadeco(nsname , nsbases, nsdict):
# print '--- metadeco ---', nsbases, nsdict['__module__'], __name__
# print 'nsname = %r\\nnsbases = %r\\nnsdict = %s' %(
# nsname, nsbases, ',\\n '.join(str(nsdi ct).split(', ')))
if nsdict['__module__'] != __name__: # exclude this first-version module
# print '--- doing meta stuff for namespace of module %s ---'% nsdict['__module__']
for i, cname in enumerate(NAMES PACE_CLASSNAMES ):
cbases = (vars(nsbases[0])[cname],) + (i and (nsdict[NAMESPACE_CLASS NAMES[i-1]],) or ())
if object not in cbases: cbases += (object,)
if cname in nsdict:
cdict = nsdict[cname].__dict__.copy( )
#cdict['__module__'] = __name__
else:
cdict = {'__doc__': '(Generated by version_1.metad eco)'}
cdict['__module__'] = nsdict['__module__']
nsdict[cname] = type(cname, cbases, cdict)
return type(nsname, nsbases, nsdict)
class Namespace(objec t):
__metaclass__ = metadeco
class A(object):
def foo(self): return "version_1.foo( )"
class B(A):
def bar(self): return "version_1.bar( )"
class C(B):
def zen(self): return "version_1.zen( )"
''')
#============== =============== =============== ==========
# version_2.py
open('version_2 .py','w').write ('''\
from version_1 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
__metaclass__ = vars(PreviousNa mespace)['__metaclass__']
class A:
def foo(self): return "version_2.foo( )"
''')
#============== =============== =============== ==========
# version_3.py
open('version_3 .py','w').write ('''\
from version_2 import Namespace as PreviousNamespa ce
class Namespace(Previ ousNamespace):
__metaclass__ = vars(PreviousNa mespace)['__metaclass__']
class B:
def bar(self): return "version_3.bar( )"
''')
#============== =============== =============== ==========
# test.py
# command: python test.py <#version>
def NamespaceFactor y(version):
return __import__("ver sion_%d" % version).Namesp ace
print NamespaceFactor y(2).B().foo() # "version_2.foo( )"
print NamespaceFactor y(3).C().bar() # "version_3.bar( )"
import sys, inspect
namespace = NamespaceFactor y(int(sys.argv[1]))
# print the __mro__ of each 'inner' class
for name,cls in inspect.getmemb ers(namespace,
inspect.isclass ):
#for name, cls in (t for t in namespace.__dic t__.items() if isinstance(t[1], type)):
print cls
for ancestor in cls.__mro__:
print "\t", ancestor
#============== =============== =============== ==========
---------------------------------------------------------------------------------
Run:
[ 5:33] C:\pywk\clp\sak kis\meta>py24 vermeta.py 3
version_2.foo()
version_3.bar()
<class 'version_3.A'>
<class 'version_3.A'>
<class 'version_2.A'>
<class 'version_1.A'>
<type 'object'>
<class 'version_3.B'>
<class 'version_3.B'>
<class 'version_2.B'>
<class 'version_1.B'>
<class 'version_3.A'>
<class 'version_2.A'>
<class 'version_1.A'>
<type 'object'>
<class 'version_3.C'>
<class 'version_3.C'>
<class 'version_2.C'>
<class 'version_1.C'>
<class 'version_3.B'>
<class 'version_2.B'>
<class 'version_1.B'>
<class 'version_3.A'>
<class 'version_2.A'>
<class 'version_1.A'>
<type 'object'>
<type 'type'>
<type 'type'>
<type 'object'>
Regards,
Bengt Richter