Skip to content

← Everyday Standard Library Tools step 19 of 55

Easy Primitives

Page Records and Check the Size at Call Time

A bulk upload sends records in pages. The pages should come one at a time, as the caller asks for them. A bad page size should fail at once, on the line that passed it. Write bulk_pages.

pages = bulk_pages(iter(["a", "b", "c"]), 2)
list(pages)              # [["a", "b"], ["c"]]
bulk_pages(iter([]), 0)  # raises ValueError right here

What to write

def bulk_pages(records: Iterable[str], size: int) -> Iterator[list[str]]: ...
  • For a size of 1 or more, yield new lists of at most size records each, in input order. Keep duplicates and empty strings.
  • Keep a shorter last page. Empty input gives no pages, not one empty page.
  • For a size below 1, raise ValueError("size must be >= 1") from the bulk_pages call itself, before you read any records. This holds even when records is empty.
  • Accept an iterable you can read only once. Do not use len, slicing or a second pass over it.
  • Build the pages one at a time, so memory grows with size, not with the input. Pages that the caller keeps do not count.
  • Let errors from the records iterator pass through when the caller reads the pages.
  • Use itertools.batched and turn each tuple into a list. Do not pass strict=True, because the exercise keeps the short last page and Python 3.12 has no strict argument.

What solve reports

The supplied solve(records, size) calls bulk_pages(iter(records), size) in one try block. Then it reads all the pages with list in a second try block. It returns a dict with three keys:

Outcome raised_on_call raised_on_iter pages
The call raises ValueError True False []
Reading the pages raises ValueError False True []
Both succeed False False All the pages

For records ["a", "b", "c"] and size 2, pages is [["a", "b"], ["c"]]. For size 0, raised_on_call must be True, even if records is empty. The solve function builds the full list only to report it, and bulk_pages itself must still produce one page at a time.

Fix the starter

The starter's bulk_pages is a generator function, because its body contains yield. So its size check runs at the first next(), and solve reports the error under raised_on_iter. Make bulk_pages a plain function that checks size and then returns an inner generator. Keep the return annotation Iterator[list[str]]. It describes what the function returns, not when its checks run.