text: # Segments are comprised of 2 main components: Transport and DataProcessingGroup (DPG)
  # The Transport represents an mechanism for input objects to be sent to the DPG. The DPG
  # is a cluster of different DataProcessor objects each created with a work function. The transport
  # does not need to be homogenous as the type of the input object on the queue is used in determining
  # which DataProcessor within the DPG is invoked.
  # In a fully composed dataflow system, one or more segments are chained together which will represent
  # the entire data processing pipeline
  
  class Segment:
      def __init__(self, transport, dpGroup, config):
          self.transport = transport
          self.dpGroup = dpGroup
          self.config = config
          self.procTypesAndCounts = self.dpGroup.getProcessorMapCounts()
  
      def hasWork(self):
          queueHasItems = self.transport.size()
          dpgIsBusy = self.dpGroup.countBusyDataProcessors() > 0
          return queueHasItems or dpgIsBusy
  
      def update(self):
          # This is how the Segment ticks and moves data from Transport into the DPG
  
          for pair in self.procTypesAndCounts.items():
              itemsToProcess = []
              typename = pair[0]
              numItemsToDequeue = pair[1]
  
              # dequeue up until we hit the number of available processors or
              # until the queue for that type is empty
              itemsFromTypeSpecificQueue = self.transport.dequeue(typename, numItemsToDequeue)
              itemsToProcess.extend(itemsFromTypeSpecificQueue)
  
              # stage the batch with threads
              for item in itemsToProcess:
                  workAccepted = self.dpGroup.processInput(*item)
  
                  # If we can't find a DP available for this item, send it back
                  # to the transport.
                  if workAccepted is False:
                      self.transport.enqueue(*item)
  