@staticmethod, backward compatibility?

Duncan Booth duncan.booth at invalid.invalid
Tue Sep 27 14:22:55 CEST 2005


Neal Becker wrote:

> How can I write code to take advantage of new decorator syntax, while
> allowing backward compatibility?
> 
> I almost want a preprocessor.
> 
> #if PYTHON_VERSION >= 2.4
> @staticmethod
> ...
> 
> 
> Since python < 2.4 will just choke on @staticmethod, how can I do this?
> 
> 
Here's one way to do it. The restrictions are:

 it only works on modules, so you can't use decorators in the
 main script;
 it only handles one decorate per function (but shouldn't be 
 too hard to extend);
 decorators are assumed to only occupy a single line.

Example of use:

----- example.py --------
import decorate23
import test

test.C().fred('it', 'works')
----- end example.py ----

----- test.py -----------
class C:
    @staticmethod
    def fred(a, b):
        print a, b
        return None

    @classmethod
    def freda(cls, a, b):
        print a, b
        return None
----- end test.py -------

The contents of decorate23 are, of course, left as an exercise 
for the reader.









































Only kidding:

----- decorate23.py -----
# Automatically apply decorators for pre-2.4 code
import sys
import re


if sys.version_info < (2,4):
    from imputil import _compile, _suffix_char
    import imp, sys, marshal, struct, __builtin__
    import imputil

    PATTERN = re.compile('^(?P<indent>[\\s]*)@(?P<decorator>.*)\n'
    	'(?P<body>\\1def (?P<id>[a-zA-Z_0-9]+)\\(.*(?:\n\\1.*)*)',
    	re.MULTILINE)
    def autodecorate(source):
        def replace(match):
            decorator = match.group('decorator')
            indent = match.group('indent')
            id = match.group('id')
            body = match.group('body')

            return "%(body)s\n%(indent)s%(id)s = %(decorator)s(%(id)s)\n" % locals()

        return PATTERN.sub(replace, source)

    def hook_compile(pathname, timestamp):
        """Compile (and cache) a Python source file.

        The file specified by <pathname> is compiled to a code object and
        returned.

        Presuming the appropriate privileges exist, the bytecodes will be
        saved back to the filesystem for future imports. The source file's
        modification timestamp must be provided as a Long value.
        """
        codestring = open(pathname, 'rU').read()

        if codestring and codestring[-1] != '\n':
            codestring = codestring + '\n'
        codestring = autodecorate(codestring)

        code = __builtin__.compile(codestring, pathname, 'exec')

        # try to cache the compiled code
        try:
            f = open(pathname + _suffix_char, 'wb')
        except IOError:
            pass
        else:
            f.write('\0\0\0\0')
            f.write(struct.pack('<I', timestamp))
            marshal.dump(code, f)
            f.flush()
            f.seek(0, 0)
            f.write(imp.get_magic())
            f.close()

        return code

    imputil._compile = hook_compile
    imputil.ImportManager().install()
----- end decorate23.py -

The overhead shouldn't be too bad as the hook will only kick in when 
the code needs recompiling, but the line numbers in tracebacks
refer to the modified code so they'll be slightly out.



More information about the Python-list mailing list