docs: def parts(self):
          """Return an array of batch parts to submit"""
  
          parts = []
  
          upserts = dict()
          deletes = []
  
          # we keep track of the batch size as we go (pretty close approximation!) so we can chunk it small enough
          # to limit the HTTP posts to under 700KB - server limits to 750KB, so play it safe
          max_upload_size = 700000
  
          # loop upserts first - fit the deletes in afterward
          # '{"replace_all": true, "complete": false, "guid": "6659fbfc-3f08-42ee-998c-9109f650f4b7", "upserts": [], "deletes": []}'
          base_part_size = 118
          if not self.replace_all:
              base_part_size += 1  # yeah, this is totally overkill :)
  
          part_size = base_part_size
          for value in self.upserts:
              if (part_size + self.upserts_size[value]) >= max_upload_size:
                  # this record would put us over the limit - close out the batch part and start a new one
                  parts.append(BatchPart(self.replace_all, upserts, deletes))
                  upserts = dict()
                  deletes = []
                  part_size = base_part_size
  
              # for the new upserts dict, drop the lower-casing of value
              upserts[self.lower_val_to_val[value]] = self.upserts[value]
              part_size += self.upserts_size[value]    # updating the approximate size of the batch
  
          for value in self.deletes:
              # delete adds length of string plus quotes, comma and space
              if (part_size + len(value) + 4) >= max_upload_size:
                  parts.append(BatchPart(self.replace_all, upserts, deletes))
                  upserts = dict()
                  deletes = []
                  part_size = base_part_size
  
              # for the new deletes set, drop the lower-casing of value
              deletes.append({'value': self.lower_val_to_val[value]})
              part_size += len(value) + 4
  
          if len(upserts) + len(deletes) > 0:
              # finish the batch
              parts.append(BatchPart(self.replace_all, upserts, deletes))
  
          if len(parts) == 0:
              if not self.replace_all:
                  raise ValueError("Batch has no data, and 'replace_all' is False")
              parts.append(BatchPart(self.replace_all, dict(), []))
  
          # last part finishes the batch
          parts[-1].set_last_part()
          return parts