title: PyQt5: recursion exception on QTextEdit line numbers
body: <p>I'm currently working on a GUI which embeds a text editor, which is a QTextEdit (not QPlainTextEdit because I need to use some rich text).
  This QTextEdit embeds a line number area. There are tons of content on doing this with a QPlainTextEdit, but I struggled to find anything with QTextEdit, but <a href="https://stackoverflow.com/questions/2443358/how-to-add-lines-numbers-to-qtextedit">ended to find a reliable answer on this topic</a>:</p>
  <p>It's in C++, but I translated it with this version in Python:</p>
  <pre><code>class SyntaxHighlighter(QtGui.QSyntaxHighlighter):
      def __init__(self, parent:typing.Union[QtCore.QObject, QtGui.QTextDocument, None]=None):
          super().__init__(parent)
          self.spaceFmt = QtGui.QTextCharFormat()
          self.spaceFmt.setForeground(QtGui.QColor('orange'))
          self.expression = re.compile(r'\s+', re.U | re.S | re.M)
  
  
      def highlightBlock(self, text:str):
          for match in self.expression.finditer(text):
              start, end = match.span()
              self.setFormat(start, end - start, self.spaceFmt)
  
  
  class LineNumberArea(QtWidgets.QWidget):
  
      def __init__(self, editor):
          super().__init__(editor)
          self.myeditor = editor
  
  
      def sizeHint(self):
          return QtCore.Qsize(self.editor.lineNumberAreaWidth(), 0)
  
  
      def paintEvent(self, event):
          self.myeditor.lineNumberAreaPaintEvent(event)
  
  
  class text_editor_line(QtWidgets.QTextEdit):
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          self.lineNumberArea = LineNumberArea(self)
  
          # generic document setting for hidden marks
          self.req_doc = QtGui.QTextDocument()
          option = QtGui.QTextOption()
          #option.setFlags(QtGui.QTextOption.ShowTabsAndSpaces | QtGui.QTextOption.ShowLineAndParagraphSeparators)
          self.req_doc.setDefaultTextOption(option)
          #doc.setPlainText(req_tab_widget.req_editor.toPlainText())
  
          # to Change color of space characters.
          highlighter = SyntaxHighlighter(self.req_doc)
  
          # highlight color
          self.editor_palette = self.palette()
          self.editor_palette.setColor(QtGui.QPalette().Highlight, QtGui.QColor(11, 215, 11)) # green
          self.editor_palette.setColor(QtGui.QPalette().HighlightedText, QtGui.QColor(QtCore.Qt.black))
          self.setPalette(self.editor_palette)
  
          self.setDocument(self.req_doc)
  
          self.req_doc.blockCountChanged.connect(self.updateLineNumberAreaWidth)
          self.verticalScrollBar().valueChanged.connect(self.updateLineNumberArea)
          self.textChanged.connect(self.updateLineNumberArea)
          self.cursorPositionChanged.connect(self.updateLineNumberArea)
          self.updateLineNumberAreaWidth(0)
  
  
  
      def keyPressEvent(self,event):
          if event.key() == QtCore.Qt.Key_Tab:
              tc = self.textCursor()
              tc.insertText(&quot;    &quot;)
              return
          return QtWidgets.QTextEdit.keyPressEvent(self,event)
  
  
  
      def lineNumberAreaWidth(self):
          digits = 1
          max_value = max(1, self.req_doc.blockCount())
          while max_value &gt;= 10:
              max_value /= 10
              digits += 1
          space = 13 + self.fontMetrics().width('9') * digits
          return space
  
      def updateLineNumberAreaWidth(self, _):
          self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
  
  
      def updateLineNumberArea(self):
  
          rect = self.contentsRect()
          self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())
          self.updateLineNumberAreaWidth(0)
          dy = self.verticalScrollBar().sliderPosition()
          if dy:
              self.lineNumberArea.scroll(0, dy)
  
          first_block_id = self.getFirstVisibleBlockId()
          if first_block_id == 0 or self.textCursor().block().blockNumber() == first_block_id - 1:
              self.verticalScrollBar().setSliderPosition(dy - self.req_doc.documentMargin())
  
  
      def resizeEvent(self, event):
          super().resizeEvent(event)
          cr = self.contentsRect()
          self.lineNumberArea.setGeometry(QtCore.QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
  
      # see if we do it?
      # def highlightCurrentLine(self):
      #     extraSelections = []
      #     if not self.isReadOnly():
      #         selection = QtWidgets.QTextEdit.ExtraSelection()
      #         lineColor = QtGui.QColor(QtCore.Qt.yellow).lighter(160)
      #         selection.format.setBackground(lineColor)
      #         selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
      #         selection.cursor = self.textCursor()
      #         selection.cursor.clearSelection()
      #         extraSelections.append(selection)
      #     self.setExtraSelections(extraSelections)
  
      def getFirstVisibleBlockId(self):
          cursor = QtGui.QTextCursor(self.req_doc)
          cursor.movePosition(QtGui.QTextCursor.Start)
  
          for block_idx in range(self.req_doc.blockCount()):
              block = cursor.block()
              r1 = self.viewport().geometry()
              r2 = self.req_doc.documentLayout().blockBoundingRect(block).translated(r1.x(), r1.y() - self.verticalScrollBar().sliderPosition()).toRect()
  
              if r1.contains(r2, True):
                  return block_idx
  
              cursor.movePosition(QtGui.QTextCursor.NextBlock)
  
          return 0
  
      def lineNumberAreaPaintEvent(self, event):
  
          self.verticalScrollBar().setSliderPosition(self.verticalScrollBar().sliderPosition())
  
          painter = QtGui.QPainter(self.lineNumberArea)
  
          painter.fillRect(event.rect(), QtCore.Qt.lightGray)
  
          blockNumber = self.getFirstVisibleBlockId()
          block = self.req_doc.findBlockByNumber(blockNumber)
          prev_block = self.req_doc.findBlockByNumber(blockNumber - 1) if blockNumber &gt; 0 else block
          translate_y = -self.verticalScrollBar().sliderPosition() if blockNumber &gt; 0 else 0;
  
          top = self.viewport().geometry().top()
  
          # Adjust text position according to the previous &quot;non entirely visible&quot; block
          # if applicable. Also takes in consideration the document's margin offset.
          if blockNumber == 0:
              # simply adjust to document's margin
              additional_margin = int(self.req_doc.documentMargin() - 1 - self.verticalScrollBar().sliderPosition())
          else:
              # getting the height of the visible part of the previous &quot;non entirely visible&quot; block
              additional_margin = int(self.req_doc.documentLayout().blockBoundingRect(prev_block).translated(0, translate_y).intersected(QtCore.QRectF(self.viewport().geometry())).height())
  
          top += additional_margin
  
          bottom = top + int(self.req_doc.documentLayout().blockBoundingRect(block).height())
  
          col_1 = QtGui.QColor(90, 255, 30) # current line
          col_0 = QtGui.QColor(120, 120, 120) # other line
  
          # Draw the numbers (displaying the current line number in green)
          while block.isValid() and (top &lt;= event.rect().bottom()):
              if block.isVisible() and (bottom &gt;= event.rect().top()):
                  number = str(blockNumber + 1)
                  painter.setPen(col_0)
                  painter.setPen(col_1 if self.textCursor().blockNumber() == blockNumber else col_0)
                  painter.drawText(-5, top, self.lineNumberArea.width(), self.fontMetrics().height(), QtCore.Qt.AlignRight, number)
  
              block = block.next()
              top = bottom
              bottom = top + int(self.req_doc.documentLayout().blockBoundingRect(block).height())
              blockNumber += 1
  
  </code></pre>
  <p>It seemed to work perfectly well, but on a 700+ lines text file from which I append all the content to editor, I ended up have the following exception</p>
  <pre><code>in updateLineNumberArea
      rect = self.contentsRect()
  
  RecursionError: maximum recursion depth exceeded while calling a Python object
  </code></pre>
  <p>I'm struggling finding how to solve this issue, I'd be glad to have some help.</p>