views:

87

answers:

5

Basically, I have a lot of Python classes (representing our database schema) that look something like this:

from foo import xyz, b, c

class bar(object):
    x = xyz()
    y = b()
    z = c()

...and I want to change it to this:

from foo import b, c
from baz import foobar

class bar(object):
    x = foobar()
    y = b()
    z = c()

Essentially, I just want to replace all instances of xyz with foobar. It's acceptable to me to leave the import of a in, so this would also be fine:

from foo import a, b, c
from baz import foobar

class bar(object):
    x = foobar()
    y = b()
    z = c()

It seems trivial to do a sed s/xyz/foobar/ on this, but then I'd still have to go back and change the import statements. I'm fine with doing some manual work, but I'd like to learn new ways to minimize the amount of it.

So how would you do this change? Is there anything I can do with sed to do this? Or rope (I don't see anything obvious that would help me here)?

+1  A: 

sed s/a/m would be disasterous since bar would be changed to bmr.

If the variable names are truly short and/or non-unique, non-regex-able, then perhaps the easiest thing to do would be to insert

from baz import m as a

Then you don't have to change any other code further down in the file.

You could use sed to change

from foo import a,b,c

to

from foo import b,c

though

from foo import a,b,c 
from baz import m as a

would work too, since the last import wins.

unutbu
Actually, they're more unique than that (and unlikely to appear in other places). I was just using `m` and `a` for simplicity's sake.
Jason Baker
I've updated the page to make it a bit more clear (albeit a bit more cheesey). And the sed method wouldn't work because I may also run into things like `from foo import b, c, xyz`.
Jason Baker
A: 

I have not used rope but can't you move a to baz then rename baz.a to baz.m You can in other refactoring tools for other languages and the rope page suggests it can.

For minimal edits - but probably worse style and maintainability make foo.a call baz.m

Mark
Actually, baz represents my own library and foo represents a third-party library. It's not feasible to move stuff back and forth between them.
Jason Baker
You can still do the move - your code will be edited and use baz.a in the code calling it but revert back to unchanged foo and make the implementation of a in baz by the monkey patch Alex Martelli suggested.
Mark
+2  A: 

Monkey-patching would be the quick and dirty way to do it -- before you do any other import, perform the following preliminary:

import foo
import baz
foo.a = baz.m

now, every subsequent use of attribute a of module foo will actually be using class m of module baz, as required, rather than the original class a of module foo. Not particularly clean, but potentially quite effective. Just DO make sure the monkey-patching happens BEFORE any other import (you can also chase throughout the object graph to locate every reference to foo.a that was put in place before the patching and change it into a baz.m, but that's way heavier and trickier).

Alex Martelli
Unfortunately, I do still need to use a in a couple of places. I'm a bit hesitant to do much more trickery to get around this as foo is a third-party library.
Jason Baker
@Jason, ah well -- maybe you can "reverse monkeypatch" foo's original a (which you can obviously stash aside before you reassign it) in the few places where it's needed? Otherwise it's surely possible to do it by altering the code, but, way trickier (unless the names are unmistakable, as you seem to indicate with your edit to xyz instead of a;-).
Alex Martelli
+1  A: 

You might try this sed script:

sed -r 's/(^from foo import.*)(xyz, |, xyz)(.*)/\1\3/; T; a\from baz import foobar'

or, equivalently:

sed 's/\(^from foo import.*\)\(xyz, \|, xyz\)\(.*\)/\1\3/; T; a\from baz import foobar'

If you try it like this, you'll get the results shown:

$ echo "from foo import xyz, b, c"|sed -r 's/(^from foo import.*)(xyz, |, xyz)(.*)/\1\3/; T; a\from baz import foobar'
from foo import b, c
from baz import foobar

$ echo "from foo import b, xyz, c"|sed -r 's/(^from foo import.*)(xyz, |, xyz)(.*)/\1\3/; T; a\from baz import foobar'
from foo import b, c
from baz import foobar

$ echo "from foo import b, c, xyz"|sed -r 's/(^from foo import.*)(xyz, |, xyz)(.*)/\1\3/; T; a\from baz import foobar'
from foo import b, c
from baz import foobar

The T command in sed branches to a label (or the end if no label is given) if no substitution is made. In this example, the "from baz" line is only appended once:

$ echo "from foo import d, e, f
from foo import xyz, b, c
from bar import g, h, i"|sed -r 's/(^from foo import.*)(xyz, |, xyz)(.*)/\1\3/;a\from baz import foobar'
from foo import d, e, f
from foo import b, c
from baz import foobar
from bar import g, h, i
Dennis Williamson
A: 

I wouldn't use monkeypatching, I'd implement my own functions:

import foo

def xyz():
    return foo.xyz()

def b():
    return foo.b()

def c():
    return foo.c()

Now I can change xyz() to make it do anything that I want, and if I ever want to explicitly call foo.xyz(), I can.

Also, if I stick that code in a module, I can globally replace from foo import with from my_foo import in all of the modules that presently use foo.

Robert Rossney