docs: def _shuffle_items(items, bucket_key=None, disable=None, seed=None, session=None):
      """
      Shuffles a list of `items` in place.
  
      If `bucket_key` is None, items are shuffled across the entire list.
  
      `bucket_key` is an optional function called for each item in `items` to
      calculate the key of bucket in which the item falls.
  
      Bucket defines the boundaries across which items will not
      be shuffled.
  
      `disable` is a function that takes an item and returns a falsey value
      if this item is ok to be shuffled. It returns a truthy value otherwise and
      the truthy value is used as part of the item's key when determining the bucket
      it belongs to.
      """
  
      if seed is not None:
          random.seed(seed)
  
      # If `bucket_key` is falsey, shuffle is global.
      if not bucket_key and not disable:
          random.shuffle(items)
          return
  
      def get_full_bucket_key(item):
          assert bucket_key or disable
          if bucket_key and disable:
              return ItemKey(bucket=bucket_key(item, session), disabled=disable(item, session))
          elif disable:
              return ItemKey(disabled=disable(item, session))
          else:
              return ItemKey(bucket=bucket_key(item, session))
  
      # For a sequence of items A1, A2, B1, B2, C1, C2,
      # where key(A1) == key(A2) == key(C1) == key(C2),
      # items A1, A2, C1, and C2 will end up in the same bucket.
      buckets = OrderedDict()
      for item in items:
          full_bucket_key = get_full_bucket_key(item)
          if full_bucket_key not in buckets:
              buckets[full_bucket_key] = []
          buckets[full_bucket_key].append(item)
  
      # Shuffle inside a bucket
  
      bucket_keys = list(buckets.keys())
  
      for full_bucket_key in buckets.keys():
          if full_bucket_key.bucket == FAILED_FIRST_LAST_FAILED_BUCKET_KEY:
              # Do not shuffle the last failed bucket
              continue
  
          if not full_bucket_key.disabled:
              random.shuffle(buckets[full_bucket_key])
  
      # Shuffle buckets
  
      # Only the first bucket can be FAILED_FIRST_LAST_FAILED_BUCKET_KEY
      if bucket_keys and bucket_keys[0].bucket == FAILED_FIRST_LAST_FAILED_BUCKET_KEY:
          new_bucket_keys = list(buckets.keys())[1:]
          random.shuffle(new_bucket_keys)
          new_bucket_keys.insert(0, bucket_keys[0])
      else:
          new_bucket_keys = list(buckets.keys())
          random.shuffle(new_bucket_keys)
  
      items[:] = [item for bk in new_bucket_keys for item in buckets[bk]]
      return