5

When I pass the options in the program (a computational biology experiment) I usually pass them through a .py file.
So I have this .py file that reads like:

starting_length=9
starting_cell_size=1000
LengthofExperiments=5000000

Then I execute the file and get the data. Since the program is all on my machine and no one else has access to it, it is secure in a trivial way.
I can also write a similar file very easily:

def writeoptions(directory):
    options=""
    options+="starting_length=%s%s"%(starting_length,os.linesep)
    options+="starting_cell_size=%s%s"%(starting_cell_size,os.linesep)
    options+="LengthofExperiments=%s%s"%(LengthofExperiments,os.linesep)
...
    open("%s%soptions.py"%(directory,os.sep),'w').write(options)

I want to pass a function as one of the parameters:

starting_length=9
starting_cell_size=1000
LengthofExperiments=5000000

def pippo(a,b):
    return a+b
functionoperator=pippo

And of course in the real experiment the function pippo will be much more complex. And different from experiment to experiment.

But what I am unable to do is to write the function automatically. In short I don't know how to generalise the writeoptions function to keep on writing the options, if one of the options is a function. I could of course copy the original file, but this is inelegant, inefficient (because it contains a lot of extra options that are not being used), and generally does not solve the question.

How do you get python to write down the code of a function, as it writes down the value of a variable?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Pietro Speroni
  • 3,131
  • 11
  • 44
  • 55
  • If the options are ALREADY in Python, why write them to another file? If the options are not in Python, then I'm not clear on what you can't do. It would help if you could include pseudo-code for the thing you can't do. – S.Lott Dec 30 '08 at 11:22
  • See http://stackoverflow.com/questions/48211/free-python-decompiler-that-is-not-an-online-service – S.Lott Dec 30 '08 at 16:14
  • See http://stackoverflow.com/questions/334851/print-the-code-which-defined-a-lambda-function – S.Lott Dec 30 '08 at 16:16

5 Answers5

15
vinko@mithril$ more a.py

def foo(a):
  print a

vinko@mithril$ more b.py

import a
import inspect

a.foo(89)
print inspect.getsource(a.foo)

vinko@mithril$ python b.py
89
def foo(a):
  print a

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • Thanks, but this solution would require that I have an extra file for each function that I pass in the parameter. Plus this has essentially the problems I explained about simply copying the original file. Let's see if someone can find a different solution, thanks though. Pietro – Pietro Speroni Dec 30 '08 at 13:41
  • Huh? Why would it require an extra file per each function? – Vinko Vrsalovic Dec 30 '08 at 14:11
  • Note that this (usually) doesn't work for retrieving source code if you deleted or edited the source file. `inspect.getsource` has to read the source file - Python functions don't save their own source code in memory. There's some caching involved, but unless the file contents happen to be cached in `linecache`, this will only work if the source file still exists. – user2357112 Oct 16 '20 at 16:44
2

You might also consider some other means of data persistence. In my own (astronomy) research, I've been experimenting with two different means of storing scripts for reproducibility. The first is to have them exclusively inside a subversion repository, and then have the job submission script automatically commit them. For instance, if you just wanted to do this in bash:

alias run_py='svn ci -m "Commit before running"; python2.5 $*'

and inside the script, have the output prefixed by the current subversion revision number for that file, you'd have a record of each script that was run and what the input was. You could pull this back out of subversion as need be.

Another, substantially less full-featured, means of tracking the input to a function could be via something like LodgeIt, a pastebin that accepts XML-RPC input and comes with Python bindings. (It can be installed locally, and has support for replying to and updating existing pastes.)

But, if you are looking for a relatively small amount of code to be included, Vinko's solution using inspect should work quite well. Doug Hellman covered the inspect module in his Python Module of the Week series. You could create a decorator that examines each option and argument and then prints it out as appropriate (I'll use inspect.getargspec to get the names of the arguments.)

import inspect
from functools import wraps

def option_printer(func):
    @wraps(func)
    def run_func(*args, **kwargs):
        for name, arg in zip(inspect.getargspec(func)[0], args) \
                       + sorted(kwargs.items()):
            if isinstance(arg, types.FunctionType): 
                print "Function argument '%s' named '%s':\n" % (name, func.func_name)
                print inspect.getsource(func)
            else:
                print "%s: %s" % (name, arg)
        return func(*args, **kwargs)
    return run_func

This could probably be made a bit more elegant, but in my tests it works for simple sets of arguments and variables. Additionally, it might have some trouble with lambdas.

Matt
  • 737
  • 5
  • 10
0

Are you asking about this?

def writeoptions(directory):
    options=""
    options+="starting_length=%s%s"%(starting_length,os.linesep)
    options+="starting_cell_size=%s%s"%(starting_cell_size,os.linesep)
    options+="LengthofExperiments=%s%s"%(LengthofExperiments,os.linesep)
    options+="def pippo(a,b):%s" % ( os.linesep, )
    options+="    '''Some version of pippo'''%s" % ( os.linesep, )
    options+="    return 2*a+b%s" % ( os.linesep, )
    open("%s%soptions.py"%(directory,os.sep),'w').write(options)

Or something else?

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • essentially something else. Your code would not take the function pippo as a parameter from which to write the new options file. Essentially I could as well have the function Pippo in the code itself, since I would have to change the function writeoptions in each case. Is it more clear now? – Pietro Speroni Dec 30 '08 at 13:44
  • Not clear at all. Do you have the source for pippo? If so, then you don't need to write it to a file -- you already have the source. – S.Lott Dec 30 '08 at 16:07
  • Every experiment is placed in a different directory, and I copy there all and only the information I need. A copy of the program, of the data, and of the options(!). But the options file does not get copied but produced to avoid unnecessary parts. Thus the question. Beside the theorethical interest. – Pietro Speroni Dec 31 '08 at 09:44
  • "copy of the program, of the data, and of the options(!). But the options file does not get copied" Okay. Whatever you say. Options both copied and not copied. – S.Lott Dec 31 '08 at 11:12
  • LOL; let me try again. The word 'copy' has two separate meaning. As a verb it means that we make a take one object and produce another like in "cp a.by b.py". But as an adverb (THIS is a copy of THAT) can both means that it was produced through a copy or that it is identical. So the confusion. – Pietro Speroni Jan 01 '09 at 10:10
  • "I copy there all and only the information I need" Should have been: I copy the program and the data and I PRODUCE an alternative options file with all and only the options that are meaningful and active for this experiment. You are probably tired by this question by now. I just wanted to clarify. – Pietro Speroni Jan 01 '09 at 10:11
  • You HAVE the program and an options file and a file with the function pippo, and you want to also reproduce the function pippo rather than simply import the function pippo from a file you already have. Makes sense to me. – S.Lott Jan 01 '09 at 14:13
  • Yes, because if I simply copy it from the original file I would also copy all the comments, and the alternative versions that are present. Thanks, – Pietro Speroni Jan 02 '09 at 07:12
0

While it is possible to do what you ask (as Vinko has shown), I'd say it is cleaner to share code. Put pippo and his buddies in a submodule that both programs can access.

Deestan
  • 16,738
  • 4
  • 32
  • 48
  • Thanks, but as I explained I am not very satisfied with Vinko solution, for reasons I have explained above. Said that, the whole structure of the code is to have a main program, a module file for each type of system, and an option file for each option. (continues) – Pietro Speroni Dec 30 '08 at 13:47
  • I am not really looking foreward to have an extra file for each module, for each functions I want to pass in the options. – Pietro Speroni Dec 30 '08 at 13:48
  • In my programs I tend to save the code that is acting every time, for reasons too long to explain here. So they don't share modules around the computer, and I can pass an experiemnt to a collegue easily. – Pietro Speroni Dec 30 '08 at 13:51
  • But on the side of all this I find the challenge to be able to save a function, as you save a parameter, to be interesting in its own right. – Pietro Speroni Dec 30 '08 at 13:52
0

Instead of diving into the subject of disassemblers and bytecodes (e.g inspect), why don't you just save the generated Python source in a module (file.py), and later, import it?

I would suggest looking into a more standard way of handling what you call options. For example, you can use the JSON module and save or restore your data. Or look into the marshal and pickle modules.

gimel
  • 83,368
  • 10
  • 76
  • 104
  • Thanks. While it is possible that my self-funded way to store the options is sub-optimal, the main reason why I want to learn to write the source code of a function is for its own sake. As a general investigation in the concept of programs that write their own code. – Pietro Speroni Dec 30 '08 at 14:00
  • Thanks for the links, though. I have read the inspect one, and found part of the solution I was looking for. As such I am on my way. :) – Pietro Speroni Dec 30 '08 at 14:01