class_docstr: Provides a stateful method that returns the number of potential extra
  empty lines needed before and after the currently processed line.
  
  Note: this tracker works on lines that haven't been split yet.  It assumes
  the prefix of the first leaf consists of optional newlines.  Those newlines
  are consumed by `maybe_empty_lines()` and included in the computation.
class_docstr_tok: ["Provides","a","stateful","method","that","returns","the","number","of","potential","extra","empty","lines","needed","before","and","after","the","currently","processed","line",".","Note",":","this","tracker","works","on","lines","that","have","n't","been","split","yet",".","It","assumes","the","prefix","of","the","first","leaf","consists","of","optional","newlines",".","Those","newlines","are","consumed","by","`","maybe_empty_lines","(",")","`","and","included","in","the","computation","."]
code: def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
      max_allowed = 1
      if current_line.depth == 0:
          max_allowed = 1 if self.mode.is_pyi else 2
      if current_line.leaves:
          # Consume the first leaf's extra newlines.
          first_leaf = current_line.leaves[0]
          before = first_leaf.prefix.count("\n")
          before = min(before, max_allowed)
          first_leaf.prefix = ""
      else:
          before = 0
  
      user_had_newline = bool(before)
      depth = current_line.depth
  
      previous_def = None
      while (
          self.previous_defs and self.previous_defs[-1].depth >= depth
      ):
          previous_def = self.previous_defs.pop()
  
      if previous_def is not None:
          assert self.previous_line is not None
          if self.mode.is_pyi:
              if (
                  Preview.blank_line_after_nested_stub_class
                  in self.mode
                  and previous_def.is_class
                  and not previous_def.is_stub_class
              ):
                  before = 1
              elif (
                  depth
                  and not current_line.is_def
                  and self.previous_line.is_def
              ):
                  # Empty lines between attributes and methods should be preserved.
                  before = 1 if user_had_newline else 0
              elif depth:
                  before = 0
              else:
                  before = 1
          else:
              if depth:
                  before = 1
              elif (
                  not depth
                  and previous_def.depth
                  and current_line.leaves[-1].type == token.COLON
                  and (
                      current_line.leaves[0].value
                      not in (
                          "with",
                          "try",
                          "for",
                          "while",
                          "if",
                          "match",
                      )
                  )
              ):
                  # We shouldn't add two newlines between an indented function and
                  # a dependent non-indented clause. This is to avoid issues with
                  # conditional function definitions that are technically top-level
                  # and therefore get two trailing newlines, but look weird and
                  # inconsistent when they're followed by elif, else, etc. This is
                  # worse because these functions only get *one* preceding newline
                  # already.
                  before = 1
              else:
                  before = 2
  
      if (
          current_line.is_decorator
          or current_line.is_def
          or current_line.is_class
      ):
          return self._maybe_empty_lines_for_class_or_def(
              current_line, before, user_had_newline
          )
  
      if (
          self.previous_line
          and self.previous_line.is_import
          and not current_line.is_import
          and not current_line.is_fmt_pass_converted(
              first_leaf_matches=is_import
          )
          and depth == self.previous_line.depth
      ):
          return (before or 1), 0
  
      if (
          self.previous_line
          and self.previous_line.is_class
          and current_line.is_docstring
      ):
          if (
              Preview.no_blank_line_before_class_docstring
              in current_line.mode
          ):
              return 0, 1
          return before, 1
  
      # In preview mode, always allow blank lines, except right before a function
      # docstring
      is_empty_first_line_ok = (
          Preview.allow_empty_first_line_in_block in current_line.mode
          and (
              not current_line.is_docstring
              or (self.previous_line and not self.previous_line.is_def)
          )
      )
  
      if (
          self.previous_line
          and self.previous_line.opens_block
          and not is_empty_first_line_ok
      ):
          return 0, 0
      return before, 0
  
imports: import itertools
  import math
  from dataclasses import dataclass, field
  from typing import Callable, Dict, Iterator, List, Optional, Sequence, Tuple, TypeVar, Union, cast
  from black.brackets import COMMA_PRIORITY, DOT_PRIORITY, BracketTracker
  from black.mode import Mode, Preview
  from black.nodes import BRACKETS, CLOSING_BRACKETS, OPENING_BRACKETS, STANDALONE_COMMENT, TEST_DESCENDANTS, child_towards, is_docstring, is_import, is_multiline_string, is_one_sequence_between, is_type_comment, is_type_ignore_comment, is_with_or_async_with_stmt, make_simple_prefix, replace_child, syms, whitespace
  from black.strings import str_width
  from blib2to3.pgen2 import token
  from blib2to3.pytree import Leaf, Node
label_desc: Use image node_id 2 for calling the EmptyLineTracker obj's underlying member method code with example usage: obj._maybe_empty_lines(current_line) and returns: before,  int, self, unknown,  int, before,  int, int,  int, int,  int