class_docstr: A data loader adaptor for ``torch.utils.data.DataLoader``.
  
  This class iterates and returns items from the Reader in batches.
  
  This loader can be used as an iterator and will terminate when the reader used in the construction of the class
  runs out of samples.
docstr: The Data Loader iterator stops the for-loop when reader runs out of samples.
code: def _iter_impl(self):
      """
      The Data Loader iterator stops the for-loop when reader runs out of samples.
      """
      # As we iterate over incoming samples, we are going to store them in `self._batch_acc`, until we have a batch of
      # the requested batch_size ready.
  
      keys = None
      if self.shuffling_queue_capacity > 0:
          # We can not know what is the reasonable number to use for the extra capacity, so we set a huge number
          # and give up on the unbound growth protection mechanism.
          min_after_dequeue = self.shuffling_queue_capacity - 1
          self._shuffling_buffer = RandomShufflingBuffer(
              self.shuffling_queue_capacity,
              min_after_retrieve=min_after_dequeue,
              extra_capacity=100000000,
          )
      else:
          self._shuffling_buffer = NoopShufflingBuffer()
  
      for row in self.reader:
          # Default collate does not work nicely on namedtuples and treat them as lists
          # Using dict will result in the yielded structures being dicts as well
          row_as_dict = row._asdict()
  
          keys = row_as_dict.keys()
  
          # Promote some types that are incompatible with pytorch to be pytorch friendly.
          _sanitize_pytorch_types(row_as_dict)
  
          # Add rows to shuffling buffer
          if not self.reader.batched_output:
              self._shuffling_buffer.add_many([row_as_dict])
          else:
              # Transposition:
              #   row_as_dict:        {'a': [1,2,3], 'b':[4,5,6]}
              #   row_group_as_tuple: [(1, 4), (2, 5), (3, 6)]
              # The order within a tuple is defined by key order in 'keys'
              row_group_as_tuple = list(
                  zip(*(row_as_dict[k] for k in keys))
              )
  
              # Adding data as 'row-by-row' into a shuffling buffer. This is a pretty
              # slow implementation though. Probably can comeup with a faster way to shuffle,
              # perhaps at the expense of a larger memory consumption...
              self._shuffling_buffer.add_many(row_group_as_tuple)
  
          # _yield_batches will emit as much batches as are allowed by the shuffling_buffer (RandomShufflingBuffer
          # will avoid underflowing below a certain number of samples to guarantee some samples decorrelation)
          for batch in self._yield_batches(keys):
              yield batch
  
      # Once reader can not read new rows, we might still have a bunch of rows waiting in the shuffling buffer.
      # Telling shuffling buffer that we are finished allows to deplete the buffer completely, regardless its
      # min_after_dequeue setting.
      self._shuffling_buffer.finish()
  
      for batch in self._yield_batches(keys):
          yield batch
  
      # Yield the last and partial batch
      if self._batch_acc:
          yield self.collate_fn(self._batch_acc)
  
imports: import collections.abc
  import decimal
  import re
  import logging
  import numpy
  from six import PY2
  from torch.utils.data.dataloader import default_collate
  import torch
  from packaging import version
  from petastorm.reader_impl.shuffling_buffer import RandomShufflingBuffer, NoopShufflingBuffer
  from petastorm.reader_impl.pytorch_shuffling_buffer import BatchedRandomShufflingBuffer, BatchedNoopShufflingBuffer
label_desc: Use image node_id 2 for calling the DataLoader obj's underlying member method code with example usage: obj._iter_impl() without return types