code: def _to_key_ranges_by_shard(cls, app, namespaces, shard_count, query_spec):
      
      key_ranges_by_ns = []
      # Split each ns into n splits. If a ns doesn't have enough scatter to
      # split into n, the last few splits are None.
      for namespace in namespaces:
        ranges = cls._split_ns_by_scatter(
            shard_count,
            namespace,
            query_spec.entity_kind,
            app)
        # The nth split of each ns will be assigned to the nth shard.
        # Shuffle so that None are not all by the end.
        random.shuffle(ranges)
        key_ranges_by_ns.append(ranges)
  
      # KeyRanges from different namespaces might be very different in size.
      # Use round robin to make sure each shard can have at most one split
      # or a None from a ns.
      ranges_by_shard = [[] for _ in range(shard_count)]
      for ranges in key_ranges_by_ns:
        for i, k_range in enumerate(ranges):
          if k_range:
            ranges_by_shard[i].append(k_range)
  
      key_ranges_by_shard = []
      for ranges in ranges_by_shard:
        if ranges:
          key_ranges_by_shard.append(key_ranges.KeyRangesFactory.create_from_list(
              ranges))
      return key_ranges_by_shard
docs: Get a list of key_ranges.KeyRanges objects, one for each shard.
  
      This method uses scatter index to split each namespace into pieces
      and assign those pieces to shards.
  
      Args:
        app: app_id in str.
        namespaces: a list of namespaces in str.
        shard_count: number of shards to split.
        query_spec: model.QuerySpec.
  
      Returns:
        a list of key_ranges.KeyRanges objects.