views:

74

answers:

2

I have a python script that I run with 'exec'. When a function is called by the script, I would like it to know the line number and offset in line for that call.

Here is an example. If my script is:

foo1(); foo2(); foo1()
foo3()

And if I have code that prints (line,offset) in every function, I should get

(0,0), (0,8), (0,16), (1,0)

In most cases this can be easily done by getting the stack frame, because it contains the line number and the function name. The only problem is when there are two functions with the same name in a certain line. Unfortunately this is a common case for me. Any ideas?


Ok it seems that changing the original code is the simplest solution.

How would you solve things like

if foo1(7) or foo1(6):

or

foo2(foo1(), foo1())

There are some not very elegant solutions for this, for example, automatically turning the previous example to:

def curpos(pos, func):
  record_curpos(pos)
  return func

curpos(foo2,0)(curpos(foo1,5)(), curpos(foo1,13)())

Let me know if you have simpler ideas.

A: 

You could of course look at the line in the string, and try to find the function name, though that will not be reliable if they alias a function, e.g.:

bar = foo2
foo1(); foo2()

Otherwise it gets really hard. The coverage module does this, and Ned explained the particular technique in a blog post -- basically it involves rewriting the bytecode to separate out expressions into different (fake) line numbers.

You might actually be able to use the coverage module itself for this.

Ian Bicking
Actually, the bytecode rewriting was in a different post: http://nedbatchelder.com/blog/200804/wicked_hack_python_bytecode_tracing.html (I didn't have to resort to that to get branch coverage).
Ned Batchelder
Thanks! looks very relevant (and interesting)
Dani
+1  A: 

Python doesn't provide a lot of information about character offsets in a line.

If you are using exec to execute the Python, then you could re-write the code mechanically before executing it to tell you what you want to know. For example, you could change the original code:

foo1(); foo2(); foo1()
foo3()

into annotated code:

curpos(1,0); foo1(); curpos(1,8); foo2(); curpos(1,16); foo1()
curpos(2,0); foo3()

and then exec the annotated code.

Where curpos(line,char) records or prints the line and character information of that point in the original code. This will be much simpler than mucking around with stack frames.

Ned Batchelder