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. -- http://mail.python.org/mailman/listinfo/python-list