views:

90

answers:

2

I have some code I developed in python 2.4++ and you bet, I have to backport it to python 2.1!

function decorators were so attractive that I have used @classmethod at a few spots, without taking notice of the fact that it is only available starting at version 2.4. the same functionality is offered by function modifiers but these appeared in python 2.2. cfr: http://www.python.org/dev/peps/pep-0318/

now some of our customers appear to be still dependent on python 2.1 (ArcGIS 9.1 ships with it and makes it not upgradable), where not even the function modifiers are available...

I have been looking for some function modifier definitions in python 2.1, but I did not find any (I mean: working). anybody successfully solved this problem?

to be more concrete, I need a way to run this 2.4 code in python 2.1:

Python 2.4.6 (#2, Mar 19 2009, 10:00:53) 
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class sic:
...   def f(cls):
...     print cls.__name__
...   f = classmethod(f)
... 
>>> sic().f()
sic
>>> sic.f()
sic
>>>
+1  A: 

If just the @classmethod need to be backported to Python 2.1.
There is a recipe of Classmethod emulation in python2.1
Hope this help.

sunqiang
nope: this was one of the recipes I found. it doesn't work. »In python 2.2, with class methods, you can write things like: Foo.bar(). The same thing with the previous classmethod function raise: unbound method must be called with instance as first argument.«
mariotomo
as indicated in recipe comment you need nested scopes: from __future__ import nested_scopes
Łukasz
from ``__future__`` import nested_scopes
Łukasz
from `__future__` import nested_scopes # this time should look right
Łukasz
that was not the main problem: without that import the code doesn't work *at all*. with that import the code still does not do what it is intended to do and causes an exception when calling the modified function.
mariotomo
+2  A: 

Just like in my old recipe for 2.1 staticmethod:

class staticmethod:
    def __init__(self, thefunc): self.f = thefunc
    def __call__(self, *a, **k): return self.f(*a, **k)

you should be able to do a 2.1 classmethod as:

class classmethod:
    def __init__(self, thefunc): self.f = thefunc
    def __call__(self, obj, *a, **k): return self.f(obj.__class__, *a, **k)

No @-syntax of course, but rather the old way (like in 2.2):

class sic:
  def f(cls): ...
  f = classmethod(f)

If this doesn't work (sorry, been many many years since I had a Python 2.1 around to test), the class will need to be supplied more explicitly -- and since you call classmethod before the class object exists, it will need to be by name -- assuming a global class,

class classmethod2:
    def __init__(self, thefunc, clsnam):
        self.f = thefunc
        self.clsnam = clsnam
    def __call__(self, *a, **k):
        klass = globals()[self.clsnam]
        return self.f(klass, *a, **k)

class sic2:
  def f(cls): ...
  f = classmethod2(f, 'sic2')

It's really hard to find elegant ways to get the class object (suppressing the need for self is the easy part, and what suffices for staticmethod: you just need to wrap the function into a non-function callable) since 2.1 had only legacy (old-style classes), no usable metaclasses, and thus no really good way to have sic2.f() magically get that cls.

Since the lack of @ syntax in 2.1 inevitably requires editing of code that uses @classmethod, an alternative is to move the functionality (decorating some methods to be "class" ones) to right AFTER the end of the class statement (the advantage is that the class object does exist at that time).

class classmethod3:
    def __init__(self, thefunc, klass):
        self.f = thefunc
        self.klass = klass
    def __call__(self, *a, **k):
        return self.f(self.klass, *a, **k)

def decorate(klass, klassmethodnames):
  for n in klassmethodnames:
    thefunc = klass.__dict__[n]
    setattr(klass, n, classmethod3(thefunc, klass))

class sic2:
  def f(cls): ...
  def g(self): ...
  def h(cls): ...
decorate(sic2, ['f', 'h'])
Alex Martelli
you tested your code in python2.1?here it doesn't work, it gives in either case »TypeError: __call__() takes at least 2 arguments (1 given)«
mariotomo
Sorry, no Python 2.1 around to test (since many, many years ago).
Alex Martelli
OK, edited the answer to add a couple of alternatives (one grotty, one less so) that might work better! (Still can't test on 2.1, but ancient musty memories are starting to come back...;-).
Alex Martelli
p.s.: in classmethod2 »klass = globals()['clsnam']« should be »klass = globals()[self.clsnam]«
mariotomo
Yep tx, editing answer accordingly.
Alex Martelli
seen and checked. methods 2 and 3 are both working, thanks!
mariotomo