class_docstr: Supports the same ``get_text*`` methods as ``ASTTokens``,
  but uses the AST to determine the text positions instead of tokens.
  This is faster than ``ASTTokens`` as it requires less setup work.
  
  It also (sometimes) supports nodes inside f-strings, which ``ASTTokens`` doesn't.
  
  Some node types and/or Python versions are not supported.
  In these cases the ``get_text*`` methods will fall back to using ``ASTTokens``
  which incurs the usual setup cost the first time.
  If you want to avoid this, check ``supports_tokenless(node)`` before calling ``get_text*`` methods.
class_docstr_tok: ["Supports","the","same","``","get_text","*","``","methods","as","``","ASTTokens","``",",","but","uses","the","AST","to","determine","the","text","positions","instead","of","tokens",".","This","is","faster","than","``","ASTTokens","``","as","it","requires","less","setup","work",".","It","also","(","sometimes",")","supports","nodes","inside","f-strings",",","which","``","ASTTokens","``","does","n't",".","Some","node","types","and\/or","Python","versions","are","not","supported",".","In","these","cases","the","``","get_text","*","``","methods","will","fall","back","to","using","``","ASTTokens","``","which","incurs","the","usual","setup","cost","the","first","time",".","If","you","want","to","avoid","this",",","check","``","supports_tokenless","(","node",")","``","before","calling","``","get_text","*","``","methods","."]
docstr: Version of ``get_text_positions()`` that doesn't use tokens.
docstr_tok: ["Version","of","``","get_text_positions","(",")","``","that","does","n't","use","tokens","."]
code: def _get_text_positions_tokenless(self, node, padded):
      # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]]
      """
      Version of ``get_text_positions()`` that doesn't use tokens.
      """
      if sys.version_info[:2] < (3, 8):  # pragma: no cover
          # This is just for mpypy
          raise AssertionError(
              "This method should only be called internally after checking supports_tokenless()"
          )
  
      if is_module(node):
          # Modules don't have position info, so just return the range of the whole text.
          # The token-using method does something different, but its behavior seems weird and inconsistent.
          # For example, in a file with only comments, it only returns the first line.
          # It's hard to imagine a case when this matters.
          return (1, 0), self._line_numbers.offset_to_line(
              len(self._text)
          )
  
      if getattr(node, "lineno", None) is None:
          return (1, 0), (1, 0)
  
      assert node  # tell mypy that node is not None, which we allowed up to here for compatibility
  
      decorators = getattr(node, "decorator_list", [])
      if not decorators:
          # Astroid uses node.decorators.nodes instead of node.decorator_list.
          decorators_node = getattr(node, "decorators", None)
          decorators = getattr(decorators_node, "nodes", [])
      if decorators:
          # Function/Class definition nodes are marked by AST as starting at def/class,
          # not the first decorator. This doesn't match the token-using behavior,
          # or inspect.getsource(), and just seems weird.
          start_node = decorators[0]
      else:
          start_node = node
  
      start_lineno = start_node.lineno
      end_node = last_stmt(node)
  
      # Include leading indentation for multiline statements.
      # This doesn't mean simple statements that happen to be on multiple lines,
      # but compound statements where inner indentation matters.
      # So we don't just compare node.lineno and node.end_lineno,
      # we check for a contained statement starting on a different line.
      if padded and (
          start_lineno != end_node.lineno
          or (
              # Astroid docstrings aren't treated as separate statements.
              # So to handle function/class definitions with a docstring but no other body,
              # we just check that the node is a statement with a docstring
              # and spanning multiple lines in the simple, literal sense.
              start_lineno != node.end_lineno
              and getattr(node, "doc_node", None)
              and is_stmt(node)
          )
      ):
          start_col_offset = 0
      else:
          start_col_offset = self._line_numbers.from_utf8_col(
              start_lineno, start_node.col_offset
          )
  
      start = (start_lineno, start_col_offset)
  
      # To match the token-using behaviour, we exclude trailing semicolons and comments.
      # This means that for blocks containing multiple statements, we have to use the last one
      # instead of the actual node for end_lineno and end_col_offset.
      end_lineno = cast(int, end_node.end_lineno)
      end_col_offset = cast(int, end_node.end_col_offset)
      end_col_offset = self._line_numbers.from_utf8_col(
          end_lineno, end_col_offset
      )
      end = (end_lineno, end_col_offset)
  
      return start, end
  
imports: import abc
  import ast
  import bisect
  import sys
  import token
  from ast import Module
  from typing import Iterable, Iterator, List, Optional, Tuple, Any, cast, TYPE_CHECKING
  import six
  from six.moves import xrange
  from .line_numbers import LineNumbers
  from .util import Token, match_token, is_non_coding_token, patched_generate_tokens, last_stmt, annotate_fstring_nodes, generate_tokens, is_module, is_stmt
label_desc: Use image node_id 4 for calling the ASTText obj's underlying member method code with example usage: obj._get_text_positions_tokenless(node, padded) and returns: start,  end, int,  int,  self, int,  int,  int,  int