I'm trying to build an interactive IDE...
the IDE has a file testing interface which displays a tree of the called functions in relation to the data in the file being tested.

what I want to do is when you select a function referenced in the tree, it'll highlight that function in the code.

I'm currently using this to get the line number:

import inspect as _insp

def trace(func):
    def inner():
        f = _insp.currentframe().f_back
        #mod = f.f_code.co_filename
        print 'You called %s at [ %d, %d ]' % (func.__name__, f.f_lineno, 0)
        return func()
    return inner

^ that 0 is a placeholder for the column number...

I'm using the trace function as a decorator:

from API.externals import trace

@trace
def ugeSetBone(): return

here's the tested results:

>>> from API.backend.FORMAT._rig import *
>>> t = ugeSetBone()
You called ugeSetBone at [ 1, 0 ]

what I'm hoping for is the return string to be:
'You called ugeSetBone at [ 1, 4 ]'

I'm expecting this to work in manners as such:
t,t2 = ugeSetBone(),ugeSetBone()

results being:
'You called ugeSetBone at [ 1, 8 ]'
'You called ugeSetBone at [ 1, 21 ]'

thanks :)

Recommended Answers

All 15 Replies

ok, I think I have an idea about testing the line for a previous call and creating an index, but I'm not sure how to get the index for a code like:

i = 0
while i!=4:
    data, data2 = bu8(), bu8()

    i = ...

what for the idea, I would expect to see something like:

{ 3: [ 'bu8', 'bu8', 'bu8', 'bu8', ... ] }

but how do I track the position in a loop??
so I can get this:

{ 3: [ ['bu8', 'bu8'], ['bu8', 'bu8'], ... ] }

hmm... maybe I won't need to track the position in the loop...

just keep track of the last line and test for an identical function name on the current function.
(basically, keeping track of everything blindly)

my mind's spiraling out of control right now, but I think I can figure it out from here. :P

EDIT:
one thing's for sure, I'd need the count of the function calls for this function on this line.

I see 2 tools which could give you more detail:

  1. You can use sys.settrace() to define your own trace function and count the number of calls in each line (there is some programming effort to get something here).
  2. You can use f.f_lasti on the calling frame to get the precise bytecode instruction where the call was done. The bytecode could help you compute the position in source code.

Also, read the important Note here, and delete explicitely frames assigned to a variable.

Also, read the important Note here, and delete explicitely frames assigned to a variable.

I was actually just informed of that last night and rebuilt the decorator before going to bed. ;D
(possible memory leak)

def decorator(func):
        def wrapper( *args, **kwargs ):
            CE = sys.modules['UMC_SIDE_CODEEDIT']
            frame = inspect.currentframe()
            try:
                # do something with the frame
                f = frame.f_back
                print 'You called %s at [ %d, %d ]' % (func.__name__, f.f_lineno, 0)
            finally: del frame

            return func( *args,**kwargs )

        return wrapper

also wanted to mention, I've tried f.f_lasti before on both "ugeSetBone()" and "t = ugeSetBone()", but got 3 in both cases.

this may have been because I was using IDLE.

as for defining my own trace function, I might, but as for right now, I think I have it down with a few globals. ;)

I can use a regex to get the column index of the function call based on the call index.

all I have to do is increase a count to keep track of which call we are on, on this line, to get the call index.
(I can do that with just a few small tests on the current line with func.name supplied with the decorator)

I'm using sys.modules[] for defining more accurate globals.
(I've fallen in love with this method) :P

don't worry, I know about the possible conflicts, so I'm using long names that aren't likely to be used anywhere else. ;)

I'm even thinking about defining my own globals dict to play things even safer in this area. :)
(sys.modules['UGE_GLOBAL_DEFINITIONS']['my global'] = blah)

I've had many wierd occurrences using the global statement where things don't work as expected...

EDIT:
forgot to mention...
CE.data is the code for the script in the current code editor.

You can perhaps use tokenize.generate_tokens() which can parse a line or any piece of codes and give you token position (column).

commented: forgot to up-vote ;D +2

You can perhaps use tokenize.generate_tokens() which can parse a line or any piece of codes and give you token position (column).

wow I am an idiot XD

I'm already doing that for drawing the indent markers in the code editor.

I hadn't thought to use it on the current line, this would definately help.
thanks :)

how does this look?? :)

def decorator(func):
    def wrapper( *args, **kwargs ):
        C = sys.modules['UMC_SIDE_GO_CODE']
        frame = inspect.currentframe()
        try:
            # do something with the frame
            f = frame.f_back
            columns,index = C[f.f_lineno][func.__name__]

            ( func.__name__, f.f_lineno, columns[index] )
            # set tree data here ( displays the function name as well as stores the name and position )

            if index+1 == len(columns): C[f.f_lineno][func.__name__][1] = 0
            else: C[f.f_lineno][func.__name__][1] += 1

        finally: del frame

        return func( *args,**kwargs )

    return wrapper


def Go(self): # run the import and export functions in the script
    lines = self.codeeditor.data.splitlines(1)
    sys.modules['UMC_SIDE_GO_CODE'] = [{}]*len(lines)
    for token in tokenize.generate_tokens( iter(lines).next ):
        toktype, toktext, (srow, scol), (erow, ecol), line = token
        if toktype == tokenize.NAME:
            try: sys.modules['UMC_SIDE_GO_CODE'][srow][toktext][0].append(scol)
            except KeyError: sys.modules['UMC_SIDE_GO_CODE'][srow][toktext] = [[scol],0]


    externals.trace = self.decorator # update the wrapper before executing

    NS = backend # TODO: supply functions based on script type
    exec self.codeeditor.data in NS, {}

    externals.trace = externals._decorator # reset the wrapper
    # ^ we don't want the function calls to get executed when testing for new data types

^ yes this code was part of a class

if you can think of anything that would result in a major improvment, let me know ;)

erg... as I feared

first off, I updated:

externals.trace = self.decorator # update the wrapper before executing

NS = backend # TODO: supply functions based on script type
exec self.codeeditor.data in NS, {}

externals.trace = externals._decorator # reset the wrapper

to:

externals.trace = self.decorator # update the wrapper before executing

def Header(T,F,L,I): pass
NS = {'Header':Header, 'UGE_MODEL':0}
NS.update(backend.models) # TODO: supply functions based on any script type
exec self.codeeditor.data+'\nImportModel(".typ",[])' in NS, {}

externals.trace = externals._decorator # reset the wrapper

^just to get the thing running when clicking the go button,
but the problem I feared was that it would operate using the old wrapper.

sure enough, clicking the go button causes the functions in the script to call the wrong wrapper...

what's a decent method I could use on all of my functions to make sure they call using the correct wrapper??

You could perhaps use the aspects module. Sometimes I use a simple tracing module which I wrote using aspects. The advantage of aspects is that methods are wrapped in a non intrusive way. See this answer to ihatehippies in a thread.

ehh... not sure I understand it...
from the looks of it, it's wrapping for classes... or something... idk

how exactly would I need to use your module??
or use aspects in your module??

I'm so confused... x.x

just tried something different and left the main decorator as is:

def trace(func):
    def wrapper( *args, **kwargs ):
        if sys.modules['UMC_EXTERNAL'] == 'UMC_SIDE':
            C = sys.modules['UMC_SIDE_GO_CODE']
            frame = inspect.currentframe()
            try:
                # do something with the frame
                f = frame.f_back
                name = func.__name__
                if name == '__init__': name = func.im_class

                columns,index = C[f.f_lineno][name]

                print 'Name, Row, Height:', ( func.__name__, f.f_lineno, columns[index] )
                # set tree data here ( displays the function name as well as stores the name and position )

                if index+1 == len(columns):
                    C[f.f_lineno][func.__name__][1] = 0
                else:
                    C[f.f_lineno][func.__name__][1] += 1

            finally: del frame

        return func( *args,**kwargs )
    return wrapper

^this of course works, but "init" != "bu32", so I'm getting an error :P

gonna have to change that to where it's "bu_4" instead of "bu32" because:

bu32 = bu(4)

^ bu32.class >>> main.bu_4

lovely... :AwesomeFace:

also, I know func.im_class is bad and returns an error...
I'm following documentation from google (SO) and just said F-it :P
(because no reference to what I really need)

aha!
I got it ^_^

Traceback (most recent call last):
  File "C:\Documents and Settings\Administrator\Nitrous\universal-model-converter\UMC_v3.0\UMC_SIDE.py", line 151, in Go
    exec self.codeeditor.data+'\nImportModel(".typ",[])' in NS, {}
  File "<string>", line 27, in <module>
  File "<string>", line 20, in ImportModel
  File "C:\Documents and Settings\Administrator\Nitrous\universal-model-converter\UMC_v3.0\API\externals.py", line 28, in wrapper
    columns,index = C[f.f_lineno][name]
KeyError: 'bu_4'

^yay

functools.wraps:

def trace(func):
    @wraps(func)
    def wrapper( *args, **kwargs ):
        if sys.modules['UMC_EXTERNAL'] == 'UMC_SIDE':
            C = sys.modules['UMC_SIDE_GO_CODE']
            frame = inspect.currentframe()
            try:
                # do something with the frame
                f = frame.f_back
                name = func.__name__
                if name == '__init__': name = args[0].__class__.__name__

                columns,index = C[f.f_lineno][name]

                print 'Name, Row, Height:', ( func.__name__, f.f_lineno, columns[index] )
                # set tree data here ( displays the function name as well as stores the name and position )

                if index+1 == len(columns):
                    C[f.f_lineno][func.__name__][1] = 0
                else:
                    C[f.f_lineno][func.__name__][1] += 1

            finally: del frame

        return func( *args,**kwargs )
    return wrapper

^all that's left to do is fix up the sys.modules['UMC_SIDE_GO_CODE'] data to contain 'bu_4' instead of 'bu32' :)

got it working perfectly ^_^
http://tcll5850.proboards.com/thread/144/public-umc-discussion-thread?page=1&scrollTo=957
I completely forgot about the sys-global I'd defined to store all the functions as a particular ID for highlighting UMC's data types :P

I can't exactly test it on ugeSetBone() from above because the entire formatting interface hasn't been supplied yet... heh

there's still major issues and tonz of undefined stuff :P
it is a rebuild after all... :AwesomeFace:

anyways... here's the go function update: :)

def Go(self): # run the import and export functions in the script
    lines = self.codeeditor.data.splitlines(1)
    sys.modules['UMC_SIDE_GO_CODE'] = [{}]*len(lines)
    for token in tokenize.generate_tokens( iter(lines).next ):
        toktype, toktext, (srow, scol), (erow, ecol), line = token
        if toktype == tokenize.NAME:

            try:
                toktext = sys.modules['UGE_Type_Names'][toktext]
                if toktext.count('('): toktext = toktext.replace('(','_')[:-1] # 'bu(#)' -> 'bu_#'
            except KeyError: pass

            try: sys.modules['UMC_SIDE_GO_CODE'][srow][toktext][0].append(scol)
            except KeyError: sys.modules['UMC_SIDE_GO_CODE'][srow][toktext] = [[scol],0]


    sys.modules['UMC_EXTERNAL'] = 'UMC_SIDE' # update the wrapper before executing

    def Header(T,F,L,I): pass
    NS = {'Header':Header, 'UGE_MODEL':0}
    NS.update(backend.models) # TODO: supply functions based on any script type
    exec self.codeeditor.data+'\nImportModel(".typ",[])' in NS, {}

    sys.modules['UMC_EXTERNAL'] = 'UMC' # reset the wrapper
    # ^ we don't want the function calls to get executed when testing for new data types

forgot to make a few minor edits to the decorator as well...
the code above will throw an error.

import sys, inspect
from functools import wraps

sys.modules['UMC_EXTERNAL'] = 'UMC'

def trace(func):
    @wraps(func)
    def wrapper( *args, **kwargs ):
        if sys.modules['UMC_EXTERNAL'] == 'UMC_SIDE':
            C = sys.modules['UMC_SIDE_GO_CODE']
            frame = inspect.currentframe()
            try:
                # do something with the frame
                f = frame.f_back
                name = func.__name__
                if name == '__init__': name = args[0].__class__.__name__

                columns,index = C[f.f_lineno][name]

                print 'Name, Row, Col:', ( name, f.f_lineno, columns[index] )
                # set tree data here ( displays the function name as well as stores the name and position )

                if index+1 == len(columns):
                    C[f.f_lineno][name][1] = 0
                else:
                    C[f.f_lineno][name][1] += 1

            finally: del frame

        return func( *args,**kwargs )
    return wrapper

I love how helpful you guys don't need to be for me to figure this out. XDD

but I've got what I was originally after (the function's column). :)
so I think I can mark this thread solved. ;)

thanks for your help Grib. ;D

commented: looks cool +14

I'm gonna make one last update later on after I finish what I want passed through the decorator.

I'll decribe the code better in that update so others can modify it to their needs. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.