How can I replace a class with another class from another module in a lot of files without a lot of manual editing?

1.2k Views Asked by At

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)?

5

There are 5 best solutions below

0
On

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.

2
On

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.

2
On

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

2
On

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).

0
On

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