I believe the confusion originates from the difference between the stack size you see when the error occurs and the limit. The thing is that the last call, that caused the crash, likely accounts for more than 1 frame on the stack, because it itself makes some function calls. And by the time you catch the exception, the call and its internal calls will have been removed from the stack. You can actually see them in the traceback. Let's look at this one.
In [1]: import inspect
In [2]: import sys
In [3]: sys.setrecursionlimit(50) # I'm setting this to 50 to make the traceback shorter.
In [4]: stack_log = []
In [5]: def recur():
stack_log.append(len(inspect.stack()))
recur()
...:
In [6]: recur()
We get the traceback (note: there is no need to read it now, so just move forward to the next section).
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
<ipython-input-6-45136123341b> in <module>()
----> 1 recur()
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
----> 2 stack_log.append(len(inspect.stack()))
3 recur()
4
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in stack(context)
1462 def stack(context=1):
1463 """Return a list of records for the stack above the caller's frame."""
-> 1464 return getouterframes(sys._getframe(1), context)
1465
1466 def trace(context=1):
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getouterframes(frame, context)
1439 framelist = []
1440 while frame:
-> 1441 frameinfo = (frame,) + getframeinfo(frame, context)
1442 framelist.append(FrameInfo(*frameinfo))
1443 frame = frame.f_back
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getframeinfo(frame, context)
1412 start = lineno - 1 - context//2
1413 try:
-> 1414 lines, lnum = findsource(frame)
1415 except OSError:
1416 lines = index = None
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in findsource(object)
742 is raised if the source code cannot be retrieved."""
743
--> 744 file = getsourcefile(object)
745 if file:
746 # Invalidate cache if needed.
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getsourcefile(object)
670 return filename
671 # only return a non-existent filename if the module has a PEP 302 loader
--> 672 if getattr(getmodule(object, filename), '__loader__', None) is not None:
673 return filename
674 # or it is in the linecache
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getmodule(object, _filename)
699 # Try the cache again with the absolute file name
700 try:
--> 701 file = getabsfile(object, _filename)
702 except TypeError:
703 return None
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getabsfile(object, _filename)
683 if _filename is None:
684 _filename = getsourcefile(object) or getfile(object)
--> 685 return os.path.normcase(os.path.abspath(_filename))
686
687 modulesbyfile = {}
/Users/ilia/.venvs/py3/bin/../lib/python3.5/posixpath.py in abspath(path)
355 def abspath(path):
356 """Return an absolute path."""
--> 357 if not isabs(path):
358 if isinstance(path, bytes):
359 cwd = os.getcwdb()
/Users/ilia/.venvs/py3/bin/../lib/python3.5/posixpath.py in isabs(s)
61 def isabs(s):
62 """Test whether a path is absolute"""
---> 63 sep = _get_sep(s)
64 return s.startswith(sep)
65
RecursionError: maximum recursion depth exceeded
What's with the stack log?
In [7]: stack_log[-1]
Out[7]: 39
Okay, we have 11 missing frames. Now, scroll down the traceback to the last recur
call, i.e.
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
2 stack_log.append(len(inspect.stack()))
----> 3 recur()
4
<ipython-input-5-643b16f38b2e> in recur()
1 def recur():
----> 2 stack_log.append(len(inspect.stack()))
3 recur()
4
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in stack(context)
1462 def stack(context=1):
1463 """Return a list of records for the stack above the caller's frame."""
-> 1464 return getouterframes(sys._getframe(1), context)
1465
1466 def trace(context=1):
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getouterframes(frame, context)
1439 framelist = []
1440 while frame:
-> 1441 frameinfo = (frame,) + getframeinfo(frame, context)
1442 framelist.append(FrameInfo(*frameinfo))
1443 frame = frame.f_back
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getframeinfo(frame, context)
1412 start = lineno - 1 - context//2
1413 try:
-> 1414 lines, lnum = findsource(frame)
1415 except OSError:
1416 lines = index = None
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in findsource(object)
742 is raised if the source code cannot be retrieved."""
743
--> 744 file = getsourcefile(object)
745 if file:
746 # Invalidate cache if needed.
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getsourcefile(object)
670 return filename
671 # only return a non-existent filename if the module has a PEP 302 loader
--> 672 if getattr(getmodule(object, filename), '__loader__', None) is not None:
673 return filename
674 # or it is in the linecache
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getmodule(object, _filename)
699 # Try the cache again with the absolute file name
700 try:
--> 701 file = getabsfile(object, _filename)
702 except TypeError:
703 return None
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/inspect.py in getabsfile(object, _filename)
683 if _filename is None:
684 _filename = getsourcefile(object) or getfile(object)
--> 685 return os.path.normcase(os.path.abspath(_filename))
686
687 modulesbyfile = {}
/Users/ilia/.venvs/py3/bin/../lib/python3.5/posixpath.py in abspath(path)
355 def abspath(path):
356 """Return an absolute path."""
--> 357 if not isabs(path):
358 if isinstance(path, bytes):
359 cwd = os.getcwdb()
/Users/ilia/.venvs/py3/bin/../lib/python3.5/posixpath.py in isabs(s)
61 def isabs(s):
62 """Test whether a path is absolute"""
---> 63 sep = _get_sep(s)
64 return s.startswith(sep)
65
RecursionError: maximum recursion depth exceeded
And here you are, there are exactly 11 function calls (the arrows on the left), that is 11 frames on the stack that were removed when the exception was raised.