views:

151

answers:

6

I've been thinking about this far too long and haven't gotten any idea, maybe some of you can help.

I have a folder of python scripts, all of which have the same surrounding body (literally, I generated it from a shell script), but have one chunk that's different than all of them. In other words:

Top piece of code (always the same)
Middle piece of code (changes from file to file)
Bottom piece of code (always the same)

And I realized today that this is a bad idea, for example, if I want to change something from the top or bottom sections, I need to write a shell script to do it. (Not that that's hard, it just seems like it's very bad code wise).

So what I want to do, is have one outer python script that is like this:

Top piece of code
Dynamic function that calls the middle piece of code (based on a parameter)
Bottom piece of code

And then every other python file in the folder can simply be the middle piece of code. However, normal module wouldn't work here (unless I'm mistaken), because I would get the code I need to execute from the arguement, which would be a string, and thus I wouldn't know which function to run until runtime.

So I thought up two more solutions:

  1. I could write up a bunch of if statements, one to run each script based on a certain parameter. I rejected this, as it's even worse than the previous design.
  2. I could use:

    os.command(sys.argv[0] scriptName.py)

    which would run the script, but calling python to call python doesn't seem very elegant to me.

So does anyone have any other ideas? Thank you.

+3  A: 

If you know the name of the function as a string and the name of module as a string, then you can do

mod = __import__(module_name)
fn = getattr(mod, fn_name)
fn()
aaronasterling
Hmm...okay, although the script was going to go in the same folder that the rest of the functions were going into, but I guess that shouldn't be much of a problem with the __init__.py file. Although the folder that all of the scripts are located in is called python, I wonder if that's going to be a problem...
Leif Andersen
It shouldn't be a problem as long as the directory is in `sys.path`
aaronasterling
Ah, yes, that worked perfectly. what I did was I changed all of the files to just have one function called func(), I than used mod = __import__(file_name_minus_py) to get the module name, and called mod.func(), thank you.
Leif Andersen
@Leif Andersen, make a module function interface that every available *MIDDLE* module should implemented, eg. all of them should define a function named `process`. You can based your solution on convention or configuration(in that case you may need to add a config file).
Satoru.Logic
A: 

However, normal module wouldn't work here (unless I'm mistaken), because I would get the code I need to execute from the arguement, which would be a string, and thus I wouldn't know which function to run until runtime.

It will work just fine - use __import__ builtin or, if you have very complex layout, imp module to import your script. And then you can get the function by module.__dict__[funcname] for example.

Daniel Kluev
A: 

Importing a module (as explained in other answers) is definitely the cleaner way to do this, but if for some reason that doesn't work, as long as you're not doing anything too weird you can use exec. It basically runs the content of another file as if it were included in the current file at the point where exec is called. It's the closest thing Python has to a source statement of the kind included in many shells. As a bare minimum, something like this should work:

exec(open(filename).read(None))
David Zaslavsky
+4  A: 

Another possible solution is to have each of your repetitive files import the functionality from the main file

from topAndBottom import top, bottom
top()
# do middle stuff
bottom()
cobbal
+1. the best solution yet if it's feasible
aaronasterling
Mm...I don't think that would work, perhaps I didn't make myself clear enough. The code in the top() portion would be setting up a pyunit test case, the middle stuff would be on test in the test case, and the bottom() function would finish up the test case. (If this wasn't in the middle of a class, I would agree that this is the best solution).
Leif Andersen
@Leif not familiar with pyunit, but could you pass the middle portion as a function to be called in the proper context? If it gets too convoluted it may be best to go with the `__import__` solution
cobbal
Ya, I turned every (middle stuff) into a function (giving each file only that funciton), and than I imported the file using __import__(modified_param_name), and called the function in that class.
Leif Andersen
A: 

How about this?

function do_thing_one():
   pass

function do_thing_two():
   pass

dispatch = { "one" : do_thing_one,
             "two" : do_thing_two,
           }

# do something to get your string from the command line (optparse, argv, whatever)
# and put it in variable "mystring"

# do top thing
f = dispatch[mystring]
f()
# do bottom thing
Chris Curvey
This would either involve having all of the functions in one file or importing every module on each run
aaronasterling
+1  A: 

In addition to the several answers already posted, consider the Template Method design pattern: make an abstract class such as

class Base(object):
    def top(self): ...
    def bottom(self): ...
    def middle(self): raise NotImplementedError
    def doit(self):
        self.top()
        self.middle()
        self.bottom()

Every pluggable module then makes a class which inherits from this Base and must override middle with the relevant code.

Perhaps not warranted for this simple case (you do still have to import the right module in order to instantiate its class and call doit on it), but still worth keeping in mind (together with its many Pythonic variations, which I have amply explained in many tech talks now available on youtube) for cases where the number or complexity of "pluggable pieces" keeps growing -- Template Method (despite its horrid name;-) is a solid, well-proven and highly scalable pattern [[sometimes a tad too rigid, but that's exactly what I address in those many tech talks -- and that problem doesn't apply to this specific use case]].

Alex Martelli
Interesting idea. Perhaps it's even more clean than using __import__() (which I eventually did use), thank you though, and I'll keep it in mind the next time I need to do something like this.
Leif Andersen
@Leif, as I mentioned your specific problem is probably simple enough that a class-less, functions-only solution works fine (and using the simplest solution that works fine is a good idea in general;-), but I'm glad you like the Template Method idea for the future.
Alex Martelli