old_contents: """
  # First argument to the function should be the next
  # object to invoke (next_handler below)
  
  class EventHandler(object):
      def __init__(self, name):
          self.name = name
  
      def handle(self, next_handler, event, data):
          print("ENTER: Handler {} Event {}".format(self.name, event))
          next_foo(value)
          print("EXIT: Handler {} Event {}".format(self.name, event))
  
  handlers = [
      EventHandler("ip_throttle"),
      EventHandler("metrics"),
      EventHandler("auth"),
      EventHandler("auth_throttle"),
      EventHandler("cache")
      # Function invoked here
  ]
  
  # Create a chain for invoking any function
  # or for a single function
  handlers_chain = chain(handlers)
  fire = chain(handlers, 'handle')
  
  # Push a single event
  data = {'config': 'value'}
  
  handlers_chain.handle("init", data)
  fire("init", data)
  """
  
  import functools
  _noop = lambda *a, **kw: None
  
  
  def _compile(objs, method):
      _next = _noop
      for obj in reversed(objs):
          func = getattr(obj, method, None)
          if func:
              _next = functools.partial(func, _next)
      return _next
  
  
  class Chain(object):
      def __init__(self, objs):
          # Private to avoid clashes when we setattr after compiling
          self.__objs = objs
  
      def __getattr__(self, name):
          # Bind the compiled partial to self so we avoid
          # __getattr__ overhead on the next call
          func = _compile(self.__objs, name)
          setattr(self, name, func)
          return func
  
  
  def chain(objs, method_name=None):
      '''
      Pass a method name to create a fixed chain for a single method
      on the set of objects, or pass None to get a Chain that will
      create nested function calls for any method
      '''
      c = Chain(objs)
      if method_name:
          return getattr(c, method_name)
      return c
  
new_contents: """
  class EventHandler(object):
      def handle(self, next_handler, event, data):
          print("ENTER: Handler {} Event {}".format(event))
          next_handler(event, data)
          print("EXIT: Handler {} Event {}".format(event))
  
  handlers = [EventHandler(), EventHandler()]
  data = {'config': 'value'}
  
  handlers_chain = chain(handlers)
  handlers_chain.handle("init", data)
  
  fire = chain(handlers, 'handle')
  fire("init", data)
  """
  import functools
  import logging
  logger = logging.getLogger(__name__)
  
  
  class Chain(object):
      __noop = lambda *a, **kw: None
  
      def __init__(self, objs):
          # Private to avoid clashes when we setattr after compiling
          logger.info("init chain, objs: {}".format(objs))
          self.__objs = objs
          self.__call_partial = None
  
      def __compile(self, method):
          _next = self.__noop
          for obj in reversed(self.__objs):
              func = getattr(obj, method, None)
              if func:
                  _next = functools.partial(func, _next)
          return _next
  
      def __getattr__(self, name):
          # Bind the compiled partial to self so we avoid
          # __getattr__ overhead on the next call
          logger.info("chain.getattr")
          func = self.__compile(name)
          setattr(self, name, func)
          return func
  
      def __call__(self, *args, **kwargs):
          '''special case because __getattr__ does not handle __call__'''
          logger.info("chain.call")
          if not self.__call_partial:
              self.__call_partial = self.__compile('__call__')
          return self.__call_partial(*args, **kwargs)
  
subject: Simplify Chain, handle __call__