def _find_current_fn_call(co, lasti):
  """Find current function call in the byte code."""
  qj._DEBUG_QJ and qj.LOG_FN('co = {}'.format(co))

  if sys.version_info[0] < 3:
    stack = _build_instruction_stack(co, lasti)
  else:
    stack = _build_instruction_stack3(co, lasti)

  source_lines, source_offset = inspect.getsourcelines(co)
  source_lines = [l.strip() for l in source_lines]

  # Apply stack effects backwards until we arrive at the stack entries for a complete function call
  fn_stack = _collect_pops(stack[:-1], stack[-1].stack_depth, [], 0)

  if not fn_stack and stack[-1].stack_depth == 0:
    # The function call took 0 arguments, so return early with a special string.
    return '<empty log>'

  qj._DEBUG_QJ and qj.LOG_FN('collected fn_stack:\n%s\n\n' % '\n'.join(str(se) for se in fn_stack))

  # Prepare to annotate the stack with extra symbols, and filter out MAKE_FUNCTION and MAKE_CLOSURE calls, which are no longer needed.
  annotate_stack = [se for se in fn_stack if not se.opname.startswith('MAKE_')]
  annotate_stack.reverse()
  _annotate_fn_args(annotate_stack, stack[-1].opname, stack[-1].oparg, -1, False)

  qj._DEBUG_QJ and qj.LOG_FN('annotated fn_stack:\n%s\n\n' % '\n'.join(str(se) for se in fn_stack))

  # Find the range of lines to search over.
  min_l = stack[-1].curr_l
  max_l = stack[0].curr_l
  for se in fn_stack:
    min_l = min(min_l, se.curr_l)
    max_l = max(max_l, se.curr_l)

  qj._DEBUG_QJ and qj.LOG_FN('source lines range: %d -> %d (indices: %d, %d)' % (min_l, max_l, min_l - source_offset, max_l - source_offset + 1))
  source_chunk = ' '.join(
      [l for l in source_lines[min_l - source_offset:max_l - source_offset + 1] if l and not l.startswith('#')])

  qj._DEBUG_QJ and qj.LOG_FN('source_chunk: %r' % source_chunk)

  # Build up all of the tokens for the function call.
  tokens = []
  for se in fn_stack:
    opname = se.opname
    for oparg_repr in se.oparg_repr[::-1]:
      # Clean up the tokens
      if not oparg_repr:
        continue
      oparg_repr = re.escape(oparg_repr.replace('\n', '\\n'))
      tokens.append(oparg_repr)

  tokens.reverse()
  qj._DEBUG_QJ and qj.LOG_FN('extracted tokens: {}'.format(tokens))
  if qj._DEBUG_QJ:
    assert tokens

  # Add tokens for the function call we're extracting.
  tokens = ['\\('] + tokens + ['\\)']

  reg = '\b*?.*?\b*?'.join(tokens)
  qj._DEBUG_QJ and qj.LOG_FN(reg)

  # Search for the function call using the full set of tokens.
  # Expand the search with source lines after the current set if we don't find a match.
  shortest_match = None
  match_attempts = 0
  max_match_attempts = 10
  while not shortest_match and match_attempts < max_match_attempts:
    (shortest_match, _, _) = (
        _find_earliest_shortest_match(source_chunk, reg, 0, len(source_chunk), num_attempts=10))
    if not shortest_match:
      match_attempts += 1

      min_l -= 1
      prev_line = ''
      while not prev_line and min_l - source_offset >= 0 and min_l - source_offset < len(source_lines):
        prev_line = source_lines[min_l - source_offset]
        if prev_line.startswith('#'):
          prev_line = ''
        if not prev_line:
          min_l -= 1
      prev_line += ' ' if prev_line else ''
      source_chunk = prev_line + source_chunk

      max_l += 1
      next_line = ''
      while not next_line and max_l - source_offset >= 0 and max_l - source_offset < len(source_lines):
        next_line = source_lines[max_l - source_offset]
        if next_line.startswith('#'):
          next_line = ''
        if not next_line:
          max_l += 1
      next_line = (' ' if next_line else '') + next_line
      source_chunk += next_line

  # Return the string for the function call
  if qj._DEBUG_QJ:
    assert shortest_match
  if shortest_match is not None:
    match = shortest_match.group(0)
    if qj._DEBUG_QJ:
      assert match.startswith('(') and match.endswith(')')
    # Do some parentheses cleanup.
    if match.startswith('('):
      match = match[1:]
    lparens = match.count('(')
    rparens = match.count(')')
    if lparens > rparens:
      match += ')' * (lparens - rparens)
    elif lparens < rparens:
      rparens_to_remove = rparens - lparens
      while len(match) and match[-1] == ')' and rparens_to_remove > 0:
        match = match[:-1]
        rparens_to_remove -= 1
    match = match.strip()
    return match
  else:
    return ''
def _find_current_fn_call ( co , lasti ) : qj . _DEBUG_QJ and qj . LOG_FN ( 'co = {}' . format ( co ) ) if sys . version_info [ 0 ] < 3 : stack = _build_instruction_stack ( co , lasti ) else : stack = _build_instruction_stack3 ( co , lasti ) source_lines , source_offset = inspect . getsourcelines ( co ) source_lines = [ l . strip ( ) for l in source_lines ] fn_stack = _collect_pops ( stack [ : - 1 ] , stack [ - 1 ] . stack_depth , [ ] , 0 ) if not fn_stack and stack [ - 1 ] . stack_depth == 0 : return '<empty log>' qj . _DEBUG_QJ and qj . LOG_FN ( 'collected fn_stack:\n%s\n\n' % '\n' . join ( str ( se ) for se in fn_stack ) ) annotate_stack = [ se for se in fn_stack if not se . opname . startswith ( 'MAKE_' ) ] annotate_stack . reverse ( ) _annotate_fn_args ( annotate_stack , stack [ - 1 ] . opname , stack [ - 1 ] . oparg , - 1 , False ) qj . _DEBUG_QJ and qj . LOG_FN ( 'annotated fn_stack:\n%s\n\n' % '\n' . join ( str ( se ) for se in fn_stack ) ) min_l = stack [ - 1 ] . curr_l max_l = stack [ 0 ] . curr_l for se in fn_stack : min_l = min ( min_l , se . curr_l ) max_l = max ( max_l , se . curr_l ) qj . _DEBUG_QJ and qj . LOG_FN ( 'source lines range: %d -> %d (indices: %d, %d)' % ( min_l , max_l , min_l - source_offset , max_l - source_offset + 1 ) ) source_chunk = ' ' . join ( [ l for l in source_lines [ min_l - source_offset : max_l - source_offset + 1 ] if l and not l . startswith ( '#' ) ] ) qj . _DEBUG_QJ and qj . LOG_FN ( 'source_chunk: %r' % source_chunk ) tokens = [ ] for se in fn_stack : opname = se . opname for oparg_repr in se . oparg_repr [ : : - 1 ] : if not oparg_repr : continue oparg_repr = re . escape ( oparg_repr . replace ( '\n' , '\\n' ) ) tokens . append ( oparg_repr ) tokens . reverse ( ) qj . _DEBUG_QJ and qj . LOG_FN ( 'extracted tokens: {}' . format ( tokens ) ) if qj . _DEBUG_QJ : assert tokens tokens = [ '\\(' ] + tokens + [ '\\)' ] reg = '\b*?.*?\b*?' . join ( tokens ) qj . _DEBUG_QJ and qj . LOG_FN ( reg ) shortest_match = None match_attempts = 0 max_match_attempts = 10 while not shortest_match and match_attempts < max_match_attempts : ( shortest_match , _ , _ ) = ( _find_earliest_shortest_match ( source_chunk , reg , 0 , len ( source_chunk ) , num_attempts = 10 ) ) if not shortest_match : match_attempts += 1 min_l -= 1 prev_line = '' while not prev_line and min_l - source_offset >= 0 and min_l - source_offset < len ( source_lines ) : prev_line = source_lines [ min_l - source_offset ] if prev_line . startswith ( '#' ) : prev_line = '' if not prev_line : min_l -= 1 prev_line += ' ' if prev_line else '' source_chunk = prev_line + source_chunk max_l += 1 next_line = '' while not next_line and max_l - source_offset >= 0 and max_l - source_offset < len ( source_lines ) : next_line = source_lines [ max_l - source_offset ] if next_line . startswith ( '#' ) : next_line = '' if not next_line : max_l += 1 next_line = ( ' ' if next_line else '' ) + next_line source_chunk += next_line if qj . _DEBUG_QJ : assert shortest_match if shortest_match is not None : match = shortest_match . group ( 0 ) if qj . _DEBUG_QJ : assert match . startswith ( '(' ) and match . endswith ( ')' ) if match . startswith ( '(' ) : match = match [ 1 : ] lparens = match . count ( '(' ) rparens = match . count ( ')' ) if lparens > rparens : match += ')' * ( lparens - rparens ) elif lparens < rparens : rparens_to_remove = rparens - lparens while len ( match ) and match [ - 1 ] == ')' and rparens_to_remove > 0 : match = match [ : - 1 ] rparens_to_remove -= 1 match = match . strip ( ) return match else : return ''
Find current function call in the byte code.