🔴 {self}
Using plain {self}
in the docstring will cause a recursion. As well as something like this:
examples/recursion.py
import sys
from documented import DocumentedError
sys.setrecursionlimit(30)
class Recursion(DocumentedError):
"""
This exception is a bugger.
It will crash when rendering {self.recursive_property}.
"""
@property
def recursive_property(self):
return self
raise Recursion()
python
Traceback (most recent call last):
File "📂/recursion.py", line 20, in <module>
raise Recursion()
RecursionCould not print a Recursion object.
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 13, in __str__
template = self.__docstring_template()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 32, in __docstring_template
return textwrap.dedent(
^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/textwrap.py", line 466, in dedent
text = re.sub(r'(?m)^' + margin, '', text)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/re/__init__.py", line 185, in sub
return _compile(pattern, flags).sub(repl, string, count)
^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 25, in __str__
logger.exception(
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1524, in exception
self.error(msg, *args, exc_info=exc_info, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1518, in error
self._log(ERROR, msg, args, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1632, in _log
record = self.makeRecord(self.name, level, fn, lno, msg, args,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1601, in makeRecord
rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 322, in __init__
self.levelname = getLevelName(level)
^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 142, in getLevelName
result = _levelToName.get(level)
^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded while calling a Python object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 25, in __str__
logger.exception(
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1524, in exception
self.error(msg, *args, exc_info=exc_info, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1518, in error
self._log(ERROR, msg, args, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1634, in _log
self.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1644, in handle
self.callHandlers(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1714, in callHandlers
lastResort.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 978, in handle
self.emit(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1110, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 953, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 688, in format
if self.usesTime():
^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 656, in usesTime
return self._style.usesTime()
^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 433, in usesTime
return self._fmt.find(self.asctime_search) >= 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded while calling a Python object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 25, in __str__
logger.exception(
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1524, in exception
self.error(msg, *args, exc_info=exc_info, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1518, in error
self._log(ERROR, msg, args, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1634, in _log
self.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1644, in handle
self.callHandlers(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1714, in callHandlers
lastResort.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 978, in handle
self.emit(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1110, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 953, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 695, in format
record.exc_text = self.formatException(record.exc_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 645, in formatException
traceback.print_exception(ei[0], ei[1], tb, None, sio)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 124, in print_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 702, in __init__
self.stack = StackSummary._extract_from_extended_frame_gen(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 405, in _extract_from_extended_frame_gen
limit = getattr(sys, 'tracebacklimit', None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded while calling a Python object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 25, in __str__
logger.exception(
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1524, in exception
self.error(msg, *args, exc_info=exc_info, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1518, in error
self._log(ERROR, msg, args, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1634, in _log
self.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1644, in handle
self.callHandlers(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1714, in callHandlers
lastResort.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 978, in handle
self.emit(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1110, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 953, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 695, in format
record.exc_text = self.formatException(record.exc_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 645, in formatException
traceback.print_exception(ei[0], ei[1], tb, None, sio)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 124, in print_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 702, in __init__
self.stack = StackSummary._extract_from_extended_frame_gen(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 436, in _extract_from_extended_frame_gen
f.line
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 321, in line
self._line = linecache.getline(self.filename, self.lineno)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 30, in getline
lines = getlines(filename, module_globals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 46, in getlines
return updatecache(filename, module_globals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 136, in updatecache
with tokenize.open(fullname) as fp:
^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/tokenize.py", line 396, in open
buffer = _builtin_open(filename, 'rb')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded while calling a Python object
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/runner/work/documented/documented/documented/documented.py", line 22, in __str__
return template.format(self=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/documented/documented/documented/documented.py", line 25, in __str__
logger.exception(
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1524, in exception
self.error(msg, *args, exc_info=exc_info, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1518, in error
self._log(ERROR, msg, args, **kwargs)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1634, in _log
self.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1644, in handle
self.callHandlers(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1714, in callHandlers
lastResort.handle(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 978, in handle
self.emit(record)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 1110, in emit
msg = self.format(record)
^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 953, in format
return fmt.format(record)
^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 695, in format
record.exc_text = self.formatException(record.exc_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/logging/__init__.py", line 645, in formatException
traceback.print_exception(ei[0], ei[1], tb, None, sio)
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 124, in print_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 702, in __init__
self.stack = StackSummary._extract_from_extended_frame_gen(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 436, in _extract_from_extended_frame_gen
f.line
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/traceback.py", line 321, in line
self._line = linecache.getline(self.filename, self.lineno)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 30, in getline
lines = getlines(filename, module_globals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 46, in getlines
return updatecache(filename, module_globals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.6/x64/lib/python3.11/linecache.py", line 136, in updatecache
with tokenize.open(fullname) as fp:
RecursionError: maximum recursion depth exceeded while calling a Python object
: <exception str() failed>
So please do not do that
We could have filtered out the plain call to {self}
but we're unable to do so for more involved cases as illustrated above, so for now we are keeping it as it is.