code: def apply(
          self, doc_loader, clear=True, parallelism=None, progress_bar=True, **kwargs
      ):
          
          # Clear everything downstream of this UDF if requested
          if clear:
              self.clear(**kwargs)
  
          # Track the last documents parsed by apply
          self.last_docs = set(doc.name for doc in doc_loader)
  
          # Execute the UDF
          self.logger.info("Running UDF...")
  
          # Setup progress bar
          if progress_bar:
              self.logger.debug("Setting up progress bar...")
              if hasattr(doc_loader, "__len__"):
                  self.pb = tqdm(total=len(doc_loader))
              else:
                  self.logger.error("Could not determine size of progress bar")
  
          # Use the parallelism of the class if none is provided to apply
          parallelism = parallelism if parallelism else self.parallelism
          if parallelism < 2:
              self._apply_st(doc_loader, clear=clear, **kwargs)
          else:
              self._apply_mt(doc_loader, parallelism, clear=clear, **kwargs)
  
          # Close progress bar
          if self.pb is not None:
              self.logger.debug("Closing progress bar...")
              self.pb.close()
docs: Apply the given UDF to the set of objects returned by the doc_loader, either
          single or multi-threaded, and optionally calling clear() first.