def echo(fn, write=sys.stdout.write):
  """ Echo calls to a function.

  Returns a decorated version of the input function which "echoes" calls
  made to it by writing out the function's name and the arguments it was
  called with.
  """
  import functools
  # Unpack function's arg count, arg names, arg defaults
  code = fn.func_code
  argcount = code.co_argcount
  argnames = code.co_varnames[:argcount]
  fn_defaults = fn.func_defaults or list()
  argdefs = dict(zip(argnames[-len(fn_defaults):], fn_defaults))

  @functools.wraps(fn)
  def wrapped(*v, **k):
    # Collect function arguments by chaining together positional,
    # defaulted, extra positional and keyword arguments.
    positional = map(format_arg_value, zip(argnames, v))
    defaulted = [format_arg_value((a, argdefs[a]))
                 for a in argnames[len(v):] if a not in k]
    nameless = map(repr, v[argcount:])
    keyword = map(format_arg_value, k.items())
    args = positional + defaulted + nameless + keyword
    write((name(fn), ", ".join(args)) % "%s(%s)\n")
    return fn(*v, **k)

  return wrapped
dataset/ETHPy150Open austinwagner/sublime-sourcepawn/watchdog/utils/echo.py/echo/swapped operands of `%`