docs: def draw_frame(self, x, y, width, height, string, fg=Ellipsis, bg=Ellipsis):
          """Similar to L{draw_rect} but only draws the outline of the rectangle.
  
          `width or `height` can be None to extend to the bottom right of the
          console or can be a negative number to be sized reltive
          to the total size of the console.
  
          Args:
              x (int): The x-coordinate to start on.
              y (int): The y-coordinate to start on.
              width (Optional[int]): Width of the rectangle.
              height (Optional[int]): Height of the rectangle.
              string (Optional[Union[Text, int]]): An integer, single character
                  string, or None.
  
                  You can set this parameter as None if you only want
                  to change the colors of an area.
              fg (Optional[Union[Tuple[int, int, int], int, Ellipsis]])
              bg (Optional[Union[Tuple[int, int, int], int, Ellipsis]])
  
          Raises:
              AssertionError: Having x or y values that can't be placed inside
                               of the console will raise an AssertionError.
  
               You can use always use ``((x, y) in console)`` to check if a tile
               is drawable.
          .. seealso:: :any:`draw_rect`, :any:`Window`
          """
          x, y, width, height = self._normalizeRect(x, y, width, height)
          fg, bg = _format_color(fg, self._fg), _format_color(bg, self._bg)
          char = _format_char(string)
          if width == 1 or height == 1: # it's just a single width line here
              return self.draw_rect(x, y, width, height, char, fg, bg)
  
          # draw sides of frame with draw_rect
          self.draw_rect(x, y, 1, height, char, fg, bg)
          self.draw_rect(x, y, width, 1, char, fg, bg)
          self.draw_rect(x + width - 1, y, 1, height, char, fg, bg)
          self.draw_rect(x, y + height - 1, width, 1, char, fg, bg)