The aim of this kata is to determine the number of sub-function calls made by an unknown function.

You have to write a function named `count_calls` which:

* takes as parameter a function and its arguments (args, kwargs)
* calls the function
* returns a tuple containing:

 * the number of function calls made inside it and inside all the sub-called functions recursively
 * the function return value.

NB: The call to the function itself is not counted.

HINT: The sys module may come in handy.

Difficulty: interview

Starter code:

def count_calls(func, *args, **kwargs):
	

Solutions:
import sys

def count_calls(func, *args, **kwargs):
    """Count calls in function func"""

    calls = [ -1 ]
    def tracer(frame, event, arg):
        if event == 'call':
            calls[0] += 1
        return tracer
    sys.settrace(tracer)
  
    rv = func(*args, **kwargs)
  
    return calls[0], rv



import sys
def count_calls(f, *args, **kwargs):
    total = 0
    def count(frame, event, arg):
        nonlocal total
        if event == 'call':
            total += 1
    sys.settrace(count)
    ret = f(*args, **kwargs)
    return total -1, ret



import sys

calls = 0
orig_func = ''

def profiler(frame, event, arg):
    nonlocal calls
    if event == 'call':
        if frame.f_code.co_name != orig_func:
            calls += 1

    return profiler


def count_calls(func, *args, **kwargs):
    """Count calls in function func"""
    nonlocal orig_func
    nonlocal calls
    sys.settrace(profiler)
    orig_func = func

    rv = func(*args, **kwargs)
    count = calls - 1

    # Clean up
    sys.settrace(None)
    calls = 0
    orig_func = ''

    return count, rv

import sys

calls = 0
def trace(frame, event, arg):
  nonlocal calls
  if event == 'call':
      calls += 1
sys.settrace(trace)

def count_calls(func, *args, **kwargs):
  nonlocal calls
  calls = -1
  result = func(*args, **kwargs)
  return calls, result

import sys

def count_calls(func, *args, **kwargs):

    def handler(_,line,k):
        handler.count += int(line=='call')
        
    handler.__dict__['count'] = 0
    sys.settrace(handler)
    
    rv = func(*args, **kwargs)
    return handler.count - 1, rv

import sys

def count_calls(func, *args, **kwargs):
  """Count calls in function func"""
  def tracefunc(frame, event, arg):
      nonlocal calls
      if event == "call":
          calls += 1
      return tracefunc
  
  calls = -1
  sys.settrace(tracefunc)
  rv = func(*args, **kwargs)
  return calls, rv


import sys

def count_calls(func, *args, **kwargs):
  """Count calls in function func"""
  calls = 0
  
  def trace_counter(frame, event, arg):
    nonlocal calls
    if event != 'call':
        return 
    calls += 1

  sys.settrace(trace_counter)
  rv = func(*args, **kwargs)
  sys.settrace(None)

  return calls-1, rv

def count_calls(func, *args, **kwargs):
    """Count calls in function func"""
    calls = 0
    d = {'calls': 0}
    import sys
    def tracefunc(frame, event, arg):
        if event=='call':           
            d['calls'] +=  1
    sys.settrace(tracefunc)
    rv = func(*args, **kwargs)
    calls = d['calls'] - 1
    return calls, rv



import sys


def count_calls(func, *args, **kwargs):
    """Count calls in function func"""
    tracer = Tracer()
    sys.settrace(tracer.my_tracer)
    rv = func(*args, **kwargs)
    return tracer.count, rv


class Tracer:
    count: int = -1

    def my_tracer(self, frame, event, arg=None):
        if event == 'call':
            self.count += 1
        return self.my_tracer

import sys


def count_calls(func, *args, **kwargs):
    """Count calls in function func"""
    tracer = Tracer(func)
    sys.settrace(tracer.my_tracer)
    rv = func(*args, **kwargs)
    return tracer.count, rv


class Tracer:
    count: int = 0
    func_name: str
    recursive: bool = False

    def __init__(self, func: callable):
        self.func_name = func.__name__

    def my_tracer(self, frame, event, arg=None):
        func_name = frame.f_code.co_name
        if event == 'call' and (self.recursive or func_name != self.func_name):
            self.count += 1
        elif func_name == self.func_name and not self.recursive:
            self.recursive = True
        return self.my_tracer