Access to the name created by the created object,

I am writing code to determine the name to which the object is assigned. This is for general debugging work and for further familiarization with the internal components of python.

I structured it as a class decorator, so that all instances of this class will record their names, if possible. The code is quite long, so I will not publish it unless specified. The general technique is such that

  • decorate the __init__ class method with code to do what I want

  • set caller = inspect.currentframe().f_back and open inspect.getframeinfo(caller).filename and send it to ast.parse . I am not making any mistakes here because (1) it is just for debugging / profiling / hacking (2) this exact process was “just” completed or the code would not work. Is there a problem with this?

  • find the ast.Assignment instance that runs the currently executing __init__ method

  • if len(assignment.targets) == 1 , then there is only one element on the left side, and I can get the name from targets[0].id . In a simple form, such as a = Foo() , then assignment.value is an instance of ast.Call . if it is a literal list (for example, a list), then value will be that list and the key, because the object of interest to me is not assigned a name.

What is the best way to confirm that assignment.value.func is actually a type(obj).__call__ object that interests me. I’m sure that I’m guaranteed that it’s there somewhere or the code will not even work. I just need him to be top notch. The obvious thing to do is take a walk and make sure that it does not contain internal calls. Then I am guaranteed that I have a name. (My reasoning is correct, I'm not sure what his assumptions are). This is not ideal, because if I'm interested in Foo , it can lead me to drop a = Foo(Bar()) , because I don't know if it is a = Bar(Foo()) .

Of course, I can just check assignment.value.func.id , but then someone could do Foobar = Foo or something like that, so I don't want to rely on it too much

Any help would be greatly appreciated. As always, I am interested in any other suggestions or problems that I can ignore.

Also, I'm really surprised that I just needed to come up with the python-internals tag.

+6
python python-internals
source share
4 answers

AST cannot give you this answer. Try using frame.f_lasti, and then take a look at the bytecode. If the next line is not STORE_FAST, you have internal calls or something else other than the simple destination you are looking for.

 def f(): f = sys._getframe() i = f.f_lasti + 3 # capture current point of execution, advance to expected store print dis.disco(f.f_code, i) 
+2
source share

I don’t know how much this will help, but do you think you are using the locals() call? It returns a dict that contains the name and value of all local variables.

For example:

 s = '' locals() >>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None} t = s # I think this is what is of most importance to you locals() >>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None} 

So, you can go through this dictionary and check which variables have (as their value) an object of the type you are looking for.

As I said, I do not know how this answer will help, but if you need to clarify anything, then leave a comment and I will try to answer as best as possible.

0
source share

I don’t check errors here, because (1) it’s just for debugging / profiling / hacking (2) this exact process was “just” completed or the code will not run. Is there a problem with this?

Yes:

  • Run the program

  • Wait, it imports a specific module foo.py

  • Edit foo.py

Now the code loaded into the Python process does not match the code found on disk.

Another reason why parsing bytecode might be the best method.

0
source share

Here's how to do it. Much thanks to an anonymous hint. Great luck in your quest to build a reputation for your alt account.

 import inspect import opcode def get_name(f): """Gets the name that the return value of a function is assigned to. This could be modified for classes as well. This is a basic version for illustration that only prints out the assignment instead of trying to do anything with it. A more flexible way would be to pass it a callback for when it identified an assignment. It does nothing for assignment to attributes. The solution for that isn't much more complicated though. If the instruction after the function call is aa `LOAD_GLOBAL`, `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by a chain of `LOAD_ATTR`'s. The last one is the attribute assigned to. """ def inner(*args, **kwargs): name = None frame = inspect.currentframe().f_back i = frame.f_lasti + 3 # get the name if it exists code = frame.f_code instr = ord(code.co_code[i]) arg = ord(code.co_code[i+1]) # no extended arg here. if instr == opcode.opmap['STORE_FAST']: name = code.co_varnames[arg] elif instr in (opcode.opmap['STORE_GLOBAL'], opcode.opmap['STORE_NAME']): name = code.co_names[arg] elif instr == opcode.opmap['STORE_DEREF']: try: name = code.co_cellvars[arg] except IndexError: name = code.co_freevars[arg - len(code.co_cellvars)] ret = f(*args, **kwargs) print opcode.opname[instr] if name: print "{0} = {1}".format(name, ret) return ret return inner @get_name def square(x): return x**2 def test_local(): x = square(2) def test_deref(): x = square(2) def closure(): y = x return closure x = square(2) test_local() test_deref()() 

It is not too complicated to calculate the assignments for list_[i] = foo() , including the value of i , using frame.f_locals . The difficult ones will be literals, and when they pass as an argument. Both of these cases should be quite complex.

0
source share

All Articles