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: MEDIUM
Tags: Complete search
Skill types: Complete search

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

Solutions:
import sys

def count_calls(func, *args, **kwargs):
	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):
	nonlocal orig_func
	nonlocal calls
	sys.settrace(profiler)
	orig_func = func
	rv = func(*args, **kwargs)
	count = calls - 1
	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):

	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):
	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)


import sys

def count_calls(func, *args, **kwargs):
	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):
	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