# Buggy code
def _build_splits(devkit):
    """Splits the train data into train/val/test by video.

    Ensures that images from the same video do not traverse the splits.

    Args:
      devkit: object that iterates over the devkit archive.

    Returns:
      train_images: File ids for the training set images.
      validation_images: File ids for the validation set images.
      test_images: File ids for the test set images.
    """
    mapping_line_ids = None
    mapping_lines = None
    for fpath, fobj in devkit:
        if fpath == "mapping/train_rand.txt":
            # Converts 1-based line index to 0-based line index.
            mapping_line_ids = [
                int(x.strip()) - 1 for x in fobj.read().decode("utf-8").split(",")
            ]
        if fpath == "mapping/train_mapping.txt":
            mapping_lines = fobj.readlines()
            mapping_lines = [x.decode("utf-8") for x in mapping_lines]

    assert mapping_line_ids
    assert mapping_lines

    video_to_image = collections.defaultdict(list)
    for image_id, mapping_lineid in enumerate(mapping_line_ids):
        line = mapping_lines[mapping_lineid]
        video_id = line.split(" ")[1]
        video_to_image[video_id].append(image_id)

    # Sets numpy random state.
    numpy_original_state = np.random.get_state()
    np.random.seed(seed=123)

    # Max 1 for testing.
    num_test_videos = max(1, _TEST_SPLIT_PERCENT_VIDEOS * len(video_to_image) // 100)
    num_validation_videos = max(
        1, _VALIDATION_SPLIT_PERCENT_VIDEOS * len(video_to_image) // 100
    )
    test_videos = set(
        np.random.choice(
            sorted(list(video_to_image.keys())), num_test_videos, replace=False
        )
    )
    validation_videos = set(
        np.random.choice(
            sorted(list(set(video_to_image.keys()) - set(test_videos))),
            num_validation_videos,
            replace=False,
        )
    )
    test_images = []
    validation_images = []
    train_images = []
    for k, v in video_to_image.items():
        if k in test_videos:
            test_images.extend(v)
        elif k in validation_videos:
            validation_images.extend(v)
        else:
            train_images.extend(v)

    # Resets numpy random state.
    np.random.set_state(numpy_original_state)
    return train_images, validation_images, test_images


# Traceback (AssertionError)
AssertionError                            Traceback (most recent call last)
<ipython-input-1-0cac24b5abed> in <module>
      1 import tensorflow_datasets as tfds
      2 
----> 3 ds, ds_info = tfds.load("caltech_birds2011", with_info=True)
      4 print(ds_info)

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\api_utils.py in disallow_positional_args_dec(fn, instance, args, kwargs)
     51     _check_no_positional(fn, args, ismethod, allowed=allowed)
     52     _check_required(fn, kwargs)
---> 53     return fn(*args, **kwargs)
     54 
     55   return disallow_positional_args_dec(wrapped)  # pylint: disable=no-value-for-parameter

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\registered.py in load(name, split, data_dir, batch_size, in_memory, shuffle_files, download, as_supervised, decoders, read_config, with_info, builder_kwargs, download_and_prepare_kwargs, as_dataset_kwargs, try_gcs)
    307   if download:
    308     download_and_prepare_kwargs = download_and_prepare_kwargs or {}
--> 309     dbuilder.download_and_prepare(**download_and_prepare_kwargs)
    310 
    311   if as_dataset_kwargs is None:

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\api_utils.py in disallow_positional_args_dec(fn, instance, args, kwargs)
     51     _check_no_positional(fn, args, ismethod, allowed=allowed)
     52     _check_required(fn, kwargs)
---> 53     return fn(*args, **kwargs)
     54 
     55   return disallow_positional_args_dec(wrapped)  # pylint: disable=no-value-for-parameter

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\dataset_builder.py in download_and_prepare(self, download_dir, download_config)
    339           self._download_and_prepare(
    340               dl_manager=dl_manager,
--> 341               download_config=download_config)
    342 
    343           # NOTE: If modifying the lines below to put additional information in

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\dataset_builder.py in _download_and_prepare(self, dl_manager, download_config)
   1077     super(GeneratorBasedBuilder, self)._download_and_prepare(
   1078         dl_manager=dl_manager,
-> 1079         max_examples_per_split=download_config.max_examples_per_split,
   1080     )
   1081 

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\dataset_builder.py in _download_and_prepare(self, dl_manager, **prepare_split_kwargs)
    930 
    931       # Prepare split will record examples associated to the split
--> 932       self._prepare_split(split_generator, **prepare_split_kwargs)
    933 
    934     # Update the info object with the splits.

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\dataset_builder.py in _prepare_split(self, split_generator, max_examples_per_split)
   1105       example = self.info.features.encode_example(record)
   1106       writer.write(key, example)
-> 1107     shard_lengths, total_size = writer.finalize()
   1108     split_generator.split_info.shard_lengths.extend(shard_lengths)
   1109     split_generator.split_info.num_bytes = total_size

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\tfrecords_writer.py in finalize(self)
    210     print("Shuffling and writing examples to %s" % self._path)
    211     shard_specs = _get_shard_specs(self._num_examples, self._shuffler.size,
--> 212                                    self._shuffler.bucket_lengths, self._path)
    213     # Here we just loop over the examples, and don't use the instructions, just
    214     # the final number of examples in every shard. Instructions could be used to

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\tfrecords_writer.py in _get_shard_specs(num_examples, total_size, bucket_lengths, path)
     87   """
     88   num_shards = _get_number_shards(total_size, num_examples)
---> 89   shard_boundaries = _get_shard_boundaries(num_examples, num_shards)
     90   shard_specs = []
     91   bucket_indexes = list(range(len(bucket_lengths)))

~\Desktop\TF_TFDS\datasets\tensorflow_datasets\core\tfrecords_writer.py in _get_shard_boundaries(num_examples, number_of_shards)
    106 def _get_shard_boundaries(num_examples, number_of_shards):
    107   if num_examples == 0:
--> 108     raise AssertionError("No examples were yielded.")
    109   if num_examples < number_of_shards:
    110     raise AssertionError("num_examples ({}) < number_of_shards ({})".format(

AssertionError: No examples were yielded.

# Fixed code
def _build_splits(devkit):
    """Splits the train data into train/val/test by video.

    Ensures that images from the same video do not traverse the splits.

    Args:
      devkit: object that iterates over the devkit archive.

    Returns:
      train_images: File ids for the training set images.
      validation_images: File ids for the validation set images.
      test_images: File ids for the test set images.
    """
    mapping_line_ids = None
    mapping_lines = None
    for fpath, fobj in devkit:
        if fpath == os.path.join("mapping", "train_rand.txt"):
            # Converts 1-based line index to 0-based line index.
            mapping_line_ids = [
                int(x.strip()) - 1 for x in fobj.read().decode("utf-8").split(",")
            ]
        elif fpath == os.path.join("mapping", "train_mapping.txt"):
            mapping_lines = fobj.read().splitlines()
            mapping_lines = [x.decode("utf-8") for x in mapping_lines]

    assert mapping_line_ids
    assert mapping_lines

    video_to_image = collections.defaultdict(list)
    for image_id, mapping_lineid in enumerate(mapping_line_ids):
        line = mapping_lines[mapping_lineid]
        video_id = line.split(" ")[1]
        video_to_image[video_id].append(image_id)

    # Sets numpy random state.
    numpy_original_state = np.random.get_state()
    np.random.seed(seed=123)

    # Max 1 for testing.
    num_test_videos = max(1, _TEST_SPLIT_PERCENT_VIDEOS * len(video_to_image) // 100)
    num_validation_videos = max(
        1, _VALIDATION_SPLIT_PERCENT_VIDEOS * len(video_to_image) // 100
    )
    test_videos = set(
        np.random.choice(
            sorted(list(video_to_image.keys())), num_test_videos, replace=False
        )
    )
    validation_videos = set(
        np.random.choice(
            sorted(list(set(video_to_image.keys()) - set(test_videos))),
            num_validation_videos,
            replace=False,
        )
    )
    test_images = []
    validation_images = []
    train_images = []
    for k, v in video_to_image.items():
        if k in test_videos:
            test_images.extend(v)
        elif k in validation_videos:
            validation_images.extend(v)
        else:
            train_images.extend(v)

    # Resets numpy random state.
    np.random.set_state(numpy_original_state)
    return train_images, validation_images, test_images