code: def preprocess(*_unused, **processors):
      """
      Decorator that applies pre-processors to the arguments of a function before
      calling the function.
  
      Parameters
      ----------
      **processors : dict
          Map from argument name -> processor function.
  
          A processor function takes three arguments: (func, argname, argvalue).
  
          `func` is the the function for which we're processing args.
          `argname` is the name of the argument we're processing.
          `argvalue` is the value of the argument we're processing.
  
      Examples
      --------
      >>> def _ensure_tuple(func, argname, arg):
      ...     if isinstance(arg, tuple):
      ...         return argvalue
      ...     try:
      ...         return tuple(arg)
      ...     except TypeError:
      ...         raise TypeError(
      ...             "%s() expected argument '%s' to"
      ...             " be iterable, but got %s instead." % (
      ...                 func.__name__, argname, arg,
      ...             )
      ...         )
      ...
      >>> @preprocess(arg=_ensure_tuple)
      ... def foo(arg):
      ...     return arg
      ...
      >>> foo([1, 2, 3])
      (1, 2, 3)
      >>> foo("a")
      ('a',)
      >>> foo(2)
      Traceback (most recent call last):
          ...
      TypeError: foo() expected argument 'arg' to be iterable, but got 2 instead.
      """
      if _unused:
          raise TypeError(
              "preprocess() doesn't accept positional arguments"
          )
  
      def _decorator(f):
          args, varargs, varkw, defaults = argspec = getargspec(f)
          if defaults is None:
              defaults = ()
          no_defaults = (NO_DEFAULT,) * (len(args) - len(defaults))
          args_defaults = list(zip(args, no_defaults + defaults))
          if varargs:
              args_defaults.append((varargs, NO_DEFAULT))
          if varkw:
              args_defaults.append((varkw, NO_DEFAULT))
  
          argset = set(args) | {varargs, varkw} - {None}
  
          # Arguments can be declared as tuples in Python 2.
          if not all(isinstance(arg, str) for arg in args):
              raise TypeError(
                  "Can't validate functions using tuple unpacking: %s"
                  % (argspec,)
              )
  
          # Ensure that all processors map to valid names.
          bad_names = viewkeys(processors) - argset
          if bad_names:
              raise TypeError(
                  "Got processors for unknown arguments: %s."
                  % bad_names
              )
  
          return _build_preprocessed_function(
              f,
              processors,
              args_defaults,
              varargs,
              varkw,
          )
  
      return _decorator
  
file_path: catalyst/catalyst/utils/preprocess.py
imports: from textwrap import dedent
  from types import CodeType
  from functools import wraps
  from inspect import getargspec
  from uuid import uuid4
  from toolz.curried.operator import getitem
  from six import viewkeys, exec_, PY3
label_desc: Use image node_id 1 for calling a global function with example usage: preprocess() and returns: _decorator, _build_preprocessed_function