docs: def batch(enumerable, batch_size):
      '''
      Breaks enumerable argument into batch size enumerable pieces. The last chunk can
      be of any length up to batch_size.
  
      batch(xrange(5), 3)
      # => [0, 1, 2], [3, 4]
      '''
      batch_size = max(int(batch_size), 1)
      try:
          enumerable.__getitem__
          total_size = len(enumerable)
      except (TypeError, AttributeError):
          enumerable = list(enumerable)
          total_size = len(enumerable)
      if total_size == 0:
          yield tuple()
      try:
          for batch_index in xrange(0, total_size, batch_size):
              yield enumerable[batch_index:min(batch_index + batch_size, total_size)]
      except TypeError:
          # Fall back on islice, though it's not as efficient the way we're using it
          for batch_start in xrange(0, total_size, batch_size):
              yield tuple(islice(enumerable, batch_start, min(batch_start + batch_size, total_size)))