Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between a stack and a frame?

Under what situations would I want to use one over the other?

What is the difference between:

>>> import inspect >>> print(inspect.getouterframes(inspect.currentframe())) [(<frame object at 0x8fc262c>, '<stdin>', 1, '<module>', None, None)] 

And:

>>> import traceback >>> traceback.extract_stack() [('<stdin>', 1, '<module>', None)] 

Update:

Another:

>>> import sys >>> print(sys._getframe().f_trace,sys._getframe().f_code) (None, <code object <module> at 0x8682a88, file "<stdin>", line 1>) 

I do not understand the nuances here:

  • Stack Frame
  • Frame Object
  • Stack Trace

update 2, a bit of time since the question was asked, but very relevant

  • Explain the concept of a stack frame in a nutshell
like image 344
jmunsch Avatar asked May 24 '14 18:05

jmunsch


People also ask

What is the difference between stack and frame pointer?

The stack pointer always points to the top (or bottom, if you prefer) of the stack. The frame pointer always points to the frame. Stack operations (e.g., push, pop, call) do not modify the frame (in a properly operating system) or the frame pointer (ever).

What is a stack frame and call stack?

A call stack is composed of 1 or many several stack frames. Each stack frame corresponds to a call to a function or procedure which has not yet terminated with a return. To use a stack frame, a thread keeps two pointers, one is called the Stack Pointer (SP), and the other is called the Frame Pointer (FP).

What is a frame in a stack trace?

A stack frame represents a single function call. You can visualize functions that call one another as virtual frames stacking on top of one another. The stack data structure is actually used for this! When one function call returns its data to its caller, then its stack frame is removed from the stack.

What is stored in a stack frame?

A stack frame contains all the data for one function call: its parameters, the return address, and its local variables. Stack-allocated objects are part of these local variables. The return address determines which code is executed after the function returns.


2 Answers

Alright, since this appears to be more about what stack frames/call stacks are in general, let's go through this:

def f():     try:         g()     except:         # WE WILL DO THINGS HERE  def g():     h()  def h():     raise Exception('stuff')  #CALL f() 

When we're in h(), there are 4 frames on the call stack.

[top level]  [f()]   [g()]    [h()] #<-- we're here 

(if we tried to put more than sys.getrecursionlimit() frames on the stack, we would get a RuntimeError, which is python's version of StackOverflow ;-))

"Outer" refers to everything above us (literally: the direction "up") in the call stack. So in order, g, then f, then the top (module) level. Likewise, "inner" refers to everything downwards in the call stack. If we catch an exception in f(), that traceback object will have references to all of the inner stack frames that were unwound to get us to that point.

def f():     try:         g()     except:         import inspect         import sys         #the third(last) item in sys.exc_info() is the current traceback object         return inspect.getinnerframes(sys.exc_info()[-1]) 

This gives:

[(<frame object at 0xaad758>, 'test.py', 3, 'f', ['        g()\n'], 0),  (<frame object at 0x7f5edeb23648>, 'test.py', 10, 'g', ['    h()\n'], 0),  (<frame object at 0x7f5edeabdc50>, 'test.py', 13, 'h', ["    raise Exception('stuff')\n"], 0)] 

As expected, the three inner frames f, g, and h. Now, we can take that last frame object (the one from h()) and ask for its outer frames:

[(<frame object at 0x7f6e996e6a48>, 'test.py', 13, 'h', ["    raise Exception('stuff')\n"], 0),  (<frame object at 0x1bf58b8>, 'test.py', 10, 'g', ['    h()\n'], 0),  (<frame object at 0x7f6e99620240>, 'test.py', 7, 'f', ['        return inspect.getinnerframes(sys.exc_info()[-1])\n'], 0),  (<frame object at 0x7f6e99725438>, 'test.py', 23, '<module>', ['print(inspect.getouterframes(f()[-1][0]))\n'], 0)] 

So, there you go, that's all that's going on: we're simply navigating the call stack. For comparison, here's what traceback.extract_stack(f()[-1][0]) gives:

[('test.py', 23, '<module>', 'print(traceback.extract_stack(f()[-1][0]))'),  ('test.py', 7, 'f', 'return inspect.getinnerframes(sys.exc_info()[-1])'),  ('test.py', 10, 'g', 'h()'),  ('test.py', 13, 'h', "raise Exception('stuff')")] 

Notice the inverted order here compared to getouterframes, and the reduced output. In fact, if you squint your eyes, this basically looks like a regular traceback (and hey, it is, with just a little bit more formatting).

Summing up: both inspect.getouterframes and traceback.extract_stack contain all the information to reproduce what you generally see in your everyday traceback; extract_stack just removes the references to the stack frames, since it is very common to no longer need them once you get to the point of formatting your stack trace from-a-given-frame-outwards.

like image 190
roippi Avatar answered Oct 14 '22 06:10

roippi


The documentation for the inspect module says:

When the following functions return “frame records,” each record is a tuple of six items: the frame object, the filename, the line number of the current line, the function name, a list of lines of context from the source code, and the index of the current line within that list.

The documentation for the traceback module says:

A "pre-processed" stack trace entry is a 4-tuple (filename, line number, function name, text)

Hence the difference is that the frame record also includes the frame object and some lines of context, while the traceback only includes the text of the individual lines in the call stack (i.e., the calls that led to the extract_stack call).

You would use the stack from traceback if you just want to print a traceback. As the documentation suggests, this is information processed for showing to the user. You would need access to the frame object from inspect if you wanted to actually do anything with the call stack (e.g., read variables from calling frames).

like image 22
BrenBarn Avatar answered Oct 14 '22 06:10

BrenBarn