tags:

views:

56

answers:

4

I'd like to write a program that modify python programs in this way:

change

"some literal string %" % SOMETHING

to

functioncall("some literal string %") % SOMETHING

Thanks,

A: 

You can solve this with having to write a program. Instead simply use the best editor ever made: Emacs. Worth learning if you haven't already. With it you can solve this by using its regex-replace capability. The only trouble is that I rarely use regex's so I always forget the details of the cryptic syntax and have to still look it up :P I'll try to figure it again for you. Here's a link to the Search & Replace Info for Emacs - scroll down for using regex's

Khorkrak
I need a way to do it melodramatically, so I can include it in another program as a kind of preprocessor.
Juanjo Conti
[Melodrama](http://en.wikipedia.org/wiki/Melodrama)tically, huh? But if you want to do it code-wise, then just use python's `re` module to handle the regular expressions.
JAB
Melodramatically I typed? F*ing spell checker. I was programatically.
Juanjo Conti
I'd expect you could just use re.sub in python to find matching patterns and replace them with whatever you want. AST is deprecated in python 2.6 and is adios in 3.0 so... [Python compiler.ast doc](http://docs.python.org/library/compiler.html)
Khorkrak
A: 

It may be simpler with tokenize -- adapting the example in the docs,

import cStringIO
import tokenize

class Lookahead(object):

  def __init__(self, s):
    self._t = tokenize.generate_tokens(cStringIO.StringIO(s).readline)
    self.lookahead = next(self._t, None)

  def __iter__(self):
    return self

  def next(self):
    result = self.lookahead
    if result is None: raise StopIteration
    self.lookahead = next(self._t, None)
    return result


def doit(s):
  toks = Lookahead(s)
  result = []
  for toktype, tokvalue, _, _, _ in toks:
    if toktype == tokenize.STRING:
      pk = toks.lookahead
      if pk is not None and pk[0] == tokenize.OP and pk[1] == '%':
        result.extend([
            (tokenize.NAME, 'functioncall'),
            (tokenize.OP, '('),
            (tokenize.STRING, repr(tokvalue)),
            (tokenize.OP, ')')
        ])
        continue
    result.append((toktype, tokvalue))
  return tokenize.untokenize(result)


print doit('"some literal string %" % SOMETHING')

This prints functioncall ('"some literal string %"')%SOMETHING. The spacing is pretty peculiar (it takes much more effort to get the spacing just right -- but that is even worse for reconstructing sources from a modified AST), but it's just fine if all you're going to do is import / run the resulting code (not so fine if you want to get nicely readable and editable code -- but that's a big enough problem that I'd suggest a separate Q;-).

Alex Martelli
A: 

Here is another SO question that may be useful.

I gather that the ast module doesn't have a facility for returning to source code, but Armin Ronacher has written a module codegen which implements a to_source function for doing just that for ast nodes.

I haven't tried to do this myself.

Matt Anderson
The codegen module doesnt work for every python code :(
Juanjo Conti
A: 
import re

pattern = r'(".+? %")(?= %)'
oldstr = '"some literal string %" % SOMETHING'

newstr = re.sub(pattern, r'functioncall(\1)', oldstr)

Try something like that. (Though with file I/O, of course.) I haven't worked with ast at all yet, so I don't really know if using that would be any easier for something like this, but it seems to me that if you're just doing a simple search-replace and not actually doing a lot of complex parsing then there's no need to use ast.

JAB