In many clients, NextToken parameter is used to iterate over all resources, with say while loop.
However, you can't pass NextToken param for the first fetch, which makes codes complicated.
I believe allowing NextToken to be None will solve this.
Here's well-work example to get all cloudwatch alarms.
cloudwatch = boto3.client('cloudwatch')
alarms = cloudwatch.describe_alarms() # First fetch: can't pass NextToken param
# ...
# Do something
# ...
while 'NextToken' in alarms:
alarms = cloudwatch.describe_alarms(NextToken=alarms['NextToken'])
# ...
# Do same thing again
# ...
If NextToken can be None, the code can be simpler:
cloudwatch = boto3.client('cloudwatch')
alarms = {'NextToken': None}
while True:
try:
alarms = cloudwatch.describe_alarms(NextToken=alarms['NextToken'])
except KeyError:
break
else:
# ...
# Do something
# ...
Currently, above code will get following error
ParamValidationError: Parameter validation failed:
Invalid type for parameter NextToken, value: None, type: <type 'NoneType'>, valid types: <type 'basestring'>
For me, botocore is too complicated to make some changes, but I found following code may have something to do with the issue.
https://github.com/boto/botocore/blob/develop/botocore/client.py#L547
One idea is adding following lines:
if 'NextToken' in api_params and api_params['NextToken'] is None:
del api_params['NextToken']
If you tell me where to fix, I feel no reluctance in making a pull request.
>>> platform.system()
'Darwin'
>>> platform.mac_ver()
('10.11.3', ('', '', ''), 'x86_64')
>>> platform.python_version()
'2.7.11'
>>> botocore.__version__
'1.4.28'
>>> boto3.__version__
'1.3.1'
The issue is that None could be a valid value for a service. If we start using it as if it were a noop, then we would not be able to support that behavior. If you would like to make pagination simpler, I recommend using our paginator.
@JordonPhillips there are not paginators for all operations.
Only:
CloudWatch.Paginator.DescribeAlarmHistory
CloudWatch.Paginator.DescribeAlarms
CloudWatch.Paginator.ListMetrics
I support @tmshn suggestion of allowing None for nextToken.
The value None can't be used to represent "no value is set"; since it could be a valid value in some APIs.
IMHO, the better suggestion is to add new (singleton) class like NotSet to represent "no value is set".
It could be like this (an example from PyGithub):
https://github.com/PyGithub/PyGithub/blob/v1.29/github/GithubObject.py#L37-L42
What do you say, @JordonPhillips, @ryguyrg ?
Because ended up here searching for NextToken usage, I wanted to point out that providing a boto.describe_things(NextToken='') seems to result in the behavior @tmshn desired.
@gmanfunky not always, you can try ssm api for get_parameters_by_path
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the GetParametersByPath operation: 1 validation error detected: Value '' at 'nextToken' failed to satisfy constraint: Member must have length greater than or equal to 1
Now there is a pagination supported in the latest release of botocore but I am using AWS Lambda which lags behind in updating the SDK. I know I can push the latest botocore package but I am trying to keep my Lambda slim for cold start times.
It would be nice to have a NotSet as suggested by @tmshn.
Failing that, the code in the example can be simplified:
cw = boto3.client('events')
extraArgs = {}
while True:
rulesPage = cw.list_rules(**extraArgs)
for rule in rulesPage['Rules']:
# do something
if 'NextToken' in rulesPage:
extraArgs['NextToken'] = rulesPage['NextToken']
else:
break
it isn't pretty, but it allows you to avoid repeating something.
Most helpful comment
It would be nice to have a
NotSetas suggested by @tmshn.Failing that, the code in the example can be simplified:
it isn't pretty, but it allows you to avoid repeating
something.