def paginate()

in backend/lambda_layers/boto_utils/python/boto_utils.py [0:0]


def paginate(client, method, iter_keys, **kwargs):
    """
    Auto paginates Boto3 client requests
    :param client: client to use for the request
    :param method: method on the client to call
    :param iter_keys: keys in the response dict to return. If a single iter_key is supplied
    each next call to the returned iterator will return the next available value If multiple iter_keys are supplied
    a tuple with an element per iter key
    Use dot notation for nested keys
    :param kwargs: kwargs to pass to method call
    :return: generator
    Example:
        paginate(s3, s3.list_object_versions, ["Versions"], Bucket="...", Prefix="...")
        paginate(s3, s3.list_object_versions, ["Versions", "DeleteMarkers"], Bucket="...", Prefix="...")
        paginate(athena, athena.get_query_results, ["ResultSet.Rows", "ResultSet.ResultSetMetadata.ColumnInfo"],
                 QueryExecutionId="...", MaxResults=10)
    """
    paginator = client.get_paginator(method.__name__)
    page_iterator = paginator.paginate(**kwargs)
    if isinstance(iter_keys, str):
        iter_keys = [iter_keys]
    for page in page_iterator:
        # Support dot notation nested keys
        results = [
            reduce(lambda d, x: d.get(x, []), k.split("."), page) for k in iter_keys
        ]
        longest = len(max(results, key=len))
        for i in range(0, longest):
            # If only one iter key supplied, return the next result for that key
            if len(iter_keys) == 1:
                yield results[0][i]
            # If multiple iter keys supplied, return a tuple of the next result for each iter key,
            # defaulting an element in the tuple to None if the corresponding iter key has no more results
            else:
                yield tuple(
                    [
                        iter_page[i] if len(iter_page) > i else None
                        for iter_page in results
                    ]
                )