We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Using `--no-paginate` with `aws cloudformation describe-stacks` throws error
Version: `aws-cli/1.10.34 Python/2.7.9 Linux/3.16.0-4-amd64 botocore/1.4.24`

When I attempt to run `aws cloudformation describe-stacks --no-paginate --region us-west-2` I receive:

```
'Namespace' object has no attribute 'page_size'
```

This does not happen if I do not use the `--no-paginate` flag.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `awscli/customizations/paginate.py`
Content:
```
1 # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"). You
4 # may not use this file except in compliance with the License. A copy of
5 # the License is located at
6 #
7 #     http://aws.amazon.com/apache2.0/
8 #
9 # or in the "license" file accompanying this file. This file is
10 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 # ANY KIND, either express or implied. See the License for the specific
12 # language governing permissions and limitations under the License.
13 """This module has customizations to unify paging paramters.
14 
15 For any operation that can be paginated, we will:
16 
17     * Hide the service specific pagination params.  This can vary across
18     services and we're going to replace them with a consistent set of
19     arguments.  The arguments will still work, but they are not
20     documented.  This allows us to add a pagination config after
21     the fact and still remain backwards compatible with users that
22     were manually doing pagination.
23     * Add a ``--starting-token`` and a ``--max-items`` argument.
24 
25 """
26 import logging
27 from functools import partial
28 
29 from botocore import xform_name
30 from botocore.exceptions import DataNotFoundError, PaginationError
31 from botocore import model
32 
33 from awscli.arguments import BaseCLIArgument
34 
35 
36 logger = logging.getLogger(__name__)
37 
38 
39 STARTING_TOKEN_HELP = """
40 <p>A token to specify where to start paginating.  This is the
41 <code>NextToken</code> from a previously truncated response.</p>
42 """
43 
44 MAX_ITEMS_HELP = """
45 <p>The total number of items to return.  If the total number
46 of items available is more than the value specified in
47 max-items then a <code>NextToken</code> will
48 be provided in the output that you can use to resume pagination.
49 This <code>NextToken</code> response element should <b>not</b> be
50 used directly outside of the AWS CLI.</p>
51 """
52 
53 PAGE_SIZE_HELP = """
54 <p>The size of each page.<p>
55 """
56 
57 
58 def register_pagination(event_handlers):
59     event_handlers.register('building-argument-table', unify_paging_params)
60     event_handlers.register_last('doc-description', add_paging_description)
61 
62 
63 def get_paginator_config(session, service_name, operation_name):
64     try:
65         paginator_model = session.get_paginator_model(service_name)
66     except DataNotFoundError:
67         return None
68     try:
69         operation_paginator_config = paginator_model.get_paginator(
70             operation_name)
71     except ValueError:
72         return None
73     return operation_paginator_config
74 
75 
76 def add_paging_description(help_command, **kwargs):
77     # This customization is only applied to the description of
78     # Operations, so we must filter out all other events.
79     if not isinstance(help_command.obj, model.OperationModel):
80         return
81     service_name = help_command.obj.service_model.service_name
82     paginator_config = get_paginator_config(
83         help_command.session, service_name, help_command.obj.name)
84     if not paginator_config:
85         return
86     help_command.doc.style.new_paragraph()
87     help_command.doc.writeln(
88         ('``%s`` is a paginated operation. Multiple API calls may be issued '
89          'in order to retrieve the entire data set of results. You can '
90          'disable pagination by providing the ``--no-paginate`` argument.')
91         % help_command.name)
92     # Only include result key information if it is present.
93     if paginator_config.get('result_key'):
94         queries = paginator_config['result_key']
95         if type(queries) is not list:
96             queries = [queries]
97         queries = ", ".join([('``%s``' % s) for s in queries])
98         help_command.doc.writeln(
99             ('When using ``--output text`` and the ``--query`` argument on a '
100              'paginated response, the ``--query`` argument must extract data '
101              'from the results of the following query expressions: %s')
102             % queries)
103 
104 
105 def unify_paging_params(argument_table, operation_model, event_name,
106                         session, **kwargs):
107     paginator_config = get_paginator_config(
108         session, operation_model.service_model.service_name,
109         operation_model.name)
110     if paginator_config is None:
111         # We only apply these customizations to paginated responses.
112         return
113     logger.debug("Modifying paging parameters for operation: %s",
114                  operation_model.name)
115     _remove_existing_paging_arguments(argument_table, paginator_config)
116     parsed_args_event = event_name.replace('building-argument-table.',
117                                            'operation-args-parsed.')
118     shadowed_args = {}
119     add_paging_argument(argument_table, 'starting-token',
120                         PageArgument('starting-token', STARTING_TOKEN_HELP,
121                                      parse_type='string',
122                                      serialized_name='StartingToken'),
123                         shadowed_args)
124     input_members = operation_model.input_shape.members
125     type_name = 'integer'
126     if 'limit_key' in paginator_config:
127         limit_key_shape = input_members[paginator_config['limit_key']]
128         type_name = limit_key_shape.type_name
129         if type_name not in PageArgument.type_map:
130             raise TypeError(
131                 ('Unsupported pagination type {0} for operation {1}'
132                  ' and parameter {2}').format(
133                     type_name, operation_model.name,
134                     paginator_config['limit_key']))
135         add_paging_argument(argument_table, 'page-size',
136                             PageArgument('page-size', PAGE_SIZE_HELP,
137                                          parse_type=type_name,
138                                          serialized_name='PageSize'),
139                             shadowed_args)
140 
141     add_paging_argument(argument_table, 'max-items',
142                         PageArgument('max-items', MAX_ITEMS_HELP,
143                                      parse_type=type_name,
144                                      serialized_name='MaxItems'),
145                         shadowed_args)
146     session.register(
147         parsed_args_event,
148         partial(check_should_enable_pagination,
149                 list(_get_all_cli_input_tokens(paginator_config)),
150                 shadowed_args, argument_table))
151 
152 
153 def add_paging_argument(argument_table, arg_name, argument, shadowed_args):
154     if arg_name in argument_table:
155         # If there's already an entry in the arg table for this argument,
156         # this means we're shadowing an argument for this operation.  We
157         # need to store this later in case pagination is turned off because
158         # we put these arguments back.
159         # See the comment in check_should_enable_pagination() for more info.
160         shadowed_args[arg_name] = argument_table[arg_name]
161     argument_table[arg_name] = argument
162 
163 
164 def check_should_enable_pagination(input_tokens, shadowed_args, argument_table,
165                                    parsed_args, parsed_globals, **kwargs):
166     normalized_paging_args = ['start_token', 'max_items']
167     for token in input_tokens:
168         py_name = token.replace('-', '_')
169         if getattr(parsed_args, py_name) is not None and \
170                 py_name not in normalized_paging_args:
171             # The user has specified a manual (undocumented) pagination arg.
172             # We need to automatically turn pagination off.
173             logger.debug("User has specified a manual pagination arg. "
174                          "Automatically setting --no-paginate.")
175             parsed_globals.paginate = False
176 
177             # Because pagination is now disabled, there's a chance that
178             # we were shadowing arguments.  For example, we inject a
179             # --max-items argument in unify_paging_params().  If the
180             # the operation also provides its own MaxItems (which we
181             # expose as --max-items) then our custom pagination arg
182             # was shadowing the customers arg.  When we turn pagination
183             # off we need to put back the original argument which is
184             # what we're doing here.
185             for key, value in shadowed_args.items():
186                 argument_table[key] = value
187     
188     if not parsed_globals.paginate:
189         ensure_paging_params_not_set(parsed_args, shadowed_args)
190 
191 
192 def ensure_paging_params_not_set(parsed_args, shadowed_args):
193     paging_params = ['starting_token', 'page_size', 'max_items']
194     shadowed_params = [p.replace('-', '_') for p in shadowed_args.keys()]
195     params_used = [p for p in paging_params if
196                    p not in shadowed_params and getattr(parsed_args, p)]
197 
198     if len(params_used) > 0:
199         converted_params = ', '.join(
200             ["--" + p.replace('_', '-') for p in params_used])
201         raise PaginationError(
202             message="Cannot specify --no-paginate along with pagination "
203                     "arguments: %s" % converted_params)
204 
205 
206 def _remove_existing_paging_arguments(argument_table, pagination_config):
207     for cli_name in _get_all_cli_input_tokens(pagination_config):
208         argument_table[cli_name]._UNDOCUMENTED = True
209 
210 
211 def _get_all_cli_input_tokens(pagination_config):
212     # Get all input tokens including the limit_key
213     # if it exists.
214     tokens = _get_input_tokens(pagination_config)
215     for token_name in tokens:
216         cli_name = xform_name(token_name, '-')
217         yield cli_name
218     if 'limit_key' in pagination_config:
219         key_name = pagination_config['limit_key']
220         cli_name = xform_name(key_name, '-')
221         yield cli_name
222 
223 
224 def _get_input_tokens(pagination_config):
225     tokens = pagination_config['input_token']
226     if not isinstance(tokens, list):
227         return [tokens]
228     return tokens
229 
230 
231 def _get_cli_name(param_objects, token_name):
232     for param in param_objects:
233         if param.name == token_name:
234             return param.cli_name.lstrip('-')
235 
236 
237 class PageArgument(BaseCLIArgument):
238     type_map = {
239         'string': str,
240         'integer': int,
241     }
242 
243     def __init__(self, name, documentation, parse_type, serialized_name):
244         self.argument_model = model.Shape('PageArgument', {'type': 'string'})
245         self._name = name
246         self._serialized_name = serialized_name
247         self._documentation = documentation
248         self._parse_type = parse_type
249         self._required = False
250 
251     @property
252     def cli_name(self):
253         return '--' + self._name
254 
255     @property
256     def cli_type_name(self):
257         return self._parse_type
258 
259     @property
260     def required(self):
261         return self._required
262 
263     @required.setter
264     def required(self, value):
265         self._required = value
266 
267     @property
268     def documentation(self):
269         return self._documentation
270 
271     def add_to_parser(self, parser):
272         parser.add_argument(self.cli_name, dest=self.py_name,
273                             type=self.type_map[self._parse_type])
274 
275     def add_to_params(self, parameters, value):
276         if value is not None:
277             pagination_config = parameters.get('PaginationConfig', {})
278             pagination_config[self._serialized_name] = value
279             parameters['PaginationConfig'] = pagination_config
280
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/awscli/customizations/paginate.py b/awscli/customizations/paginate.py
--- a/awscli/customizations/paginate.py
+++ b/awscli/customizations/paginate.py
@@ -184,7 +184,7 @@
             # what we're doing here.
             for key, value in shadowed_args.items():
                 argument_table[key] = value
-    
+
     if not parsed_globals.paginate:
         ensure_paging_params_not_set(parsed_args, shadowed_args)
 
@@ -193,7 +193,7 @@
     paging_params = ['starting_token', 'page_size', 'max_items']
     shadowed_params = [p.replace('-', '_') for p in shadowed_args.keys()]
     params_used = [p for p in paging_params if
-                   p not in shadowed_params and getattr(parsed_args, p)]
+                   p not in shadowed_params and getattr(parsed_args, p, None)]
 
     if len(params_used) > 0:
         converted_params = ', '.join(

{"golden_diff": "diff --git a/awscli/customizations/paginate.py b/awscli/customizations/paginate.py\n--- a/awscli/customizations/paginate.py\n+++ b/awscli/customizations/paginate.py\n@@ -184,7 +184,7 @@\n             # what we're doing here.\n             for key, value in shadowed_args.items():\n                 argument_table[key] = value\n-    \n+\n     if not parsed_globals.paginate:\n         ensure_paging_params_not_set(parsed_args, shadowed_args)\n \n@@ -193,7 +193,7 @@\n     paging_params = ['starting_token', 'page_size', 'max_items']\n     shadowed_params = [p.replace('-', '_') for p in shadowed_args.keys()]\n     params_used = [p for p in paging_params if\n-                   p not in shadowed_params and getattr(parsed_args, p)]\n+                   p not in shadowed_params and getattr(parsed_args, p, None)]\n \n     if len(params_used) > 0:\n         converted_params = ', '.join(\n", "issue": "Using `--no-paginate` with `aws cloudformation describe-stacks` throws error\nVersion: `aws-cli/1.10.34 Python/2.7.9 Linux/3.16.0-4-amd64 botocore/1.4.24`\n\nWhen I attempt to run `aws cloudformation describe-stacks --no-paginate --region us-west-2` I receive:\n\n```\n'Namespace' object has no attribute 'page_size'\n```\n\nThis does not happen if I do not use the `--no-paginate` flag.\n\n", "before_files": [{"content": "# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\"\"\"This module has customizations to unify paging paramters.\n\nFor any operation that can be paginated, we will:\n\n    * Hide the service specific pagination params.  This can vary across\n    services and we're going to replace them with a consistent set of\n    arguments.  The arguments will still work, but they are not\n    documented.  This allows us to add a pagination config after\n    the fact and still remain backwards compatible with users that\n    were manually doing pagination.\n    * Add a ``--starting-token`` and a ``--max-items`` argument.\n\n\"\"\"\nimport logging\nfrom functools import partial\n\nfrom botocore import xform_name\nfrom botocore.exceptions import DataNotFoundError, PaginationError\nfrom botocore import model\n\nfrom awscli.arguments import BaseCLIArgument\n\n\nlogger = logging.getLogger(__name__)\n\n\nSTARTING_TOKEN_HELP = \"\"\"\n<p>A token to specify where to start paginating.  This is the\n<code>NextToken</code> from a previously truncated response.</p>\n\"\"\"\n\nMAX_ITEMS_HELP = \"\"\"\n<p>The total number of items to return.  If the total number\nof items available is more than the value specified in\nmax-items then a <code>NextToken</code> will\nbe provided in the output that you can use to resume pagination.\nThis <code>NextToken</code> response element should <b>not</b> be\nused directly outside of the AWS CLI.</p>\n\"\"\"\n\nPAGE_SIZE_HELP = \"\"\"\n<p>The size of each page.<p>\n\"\"\"\n\n\ndef register_pagination(event_handlers):\n    event_handlers.register('building-argument-table', unify_paging_params)\n    event_handlers.register_last('doc-description', add_paging_description)\n\n\ndef get_paginator_config(session, service_name, operation_name):\n    try:\n        paginator_model = session.get_paginator_model(service_name)\n    except DataNotFoundError:\n        return None\n    try:\n        operation_paginator_config = paginator_model.get_paginator(\n            operation_name)\n    except ValueError:\n        return None\n    return operation_paginator_config\n\n\ndef add_paging_description(help_command, **kwargs):\n    # This customization is only applied to the description of\n    # Operations, so we must filter out all other events.\n    if not isinstance(help_command.obj, model.OperationModel):\n        return\n    service_name = help_command.obj.service_model.service_name\n    paginator_config = get_paginator_config(\n        help_command.session, service_name, help_command.obj.name)\n    if not paginator_config:\n        return\n    help_command.doc.style.new_paragraph()\n    help_command.doc.writeln(\n        ('``%s`` is a paginated operation. Multiple API calls may be issued '\n         'in order to retrieve the entire data set of results. You can '\n         'disable pagination by providing the ``--no-paginate`` argument.')\n        % help_command.name)\n    # Only include result key information if it is present.\n    if paginator_config.get('result_key'):\n        queries = paginator_config['result_key']\n        if type(queries) is not list:\n            queries = [queries]\n        queries = \", \".join([('``%s``' % s) for s in queries])\n        help_command.doc.writeln(\n            ('When using ``--output text`` and the ``--query`` argument on a '\n             'paginated response, the ``--query`` argument must extract data '\n             'from the results of the following query expressions: %s')\n            % queries)\n\n\ndef unify_paging_params(argument_table, operation_model, event_name,\n                        session, **kwargs):\n    paginator_config = get_paginator_config(\n        session, operation_model.service_model.service_name,\n        operation_model.name)\n    if paginator_config is None:\n        # We only apply these customizations to paginated responses.\n        return\n    logger.debug(\"Modifying paging parameters for operation: %s\",\n                 operation_model.name)\n    _remove_existing_paging_arguments(argument_table, paginator_config)\n    parsed_args_event = event_name.replace('building-argument-table.',\n                                           'operation-args-parsed.')\n    shadowed_args = {}\n    add_paging_argument(argument_table, 'starting-token',\n                        PageArgument('starting-token', STARTING_TOKEN_HELP,\n                                     parse_type='string',\n                                     serialized_name='StartingToken'),\n                        shadowed_args)\n    input_members = operation_model.input_shape.members\n    type_name = 'integer'\n    if 'limit_key' in paginator_config:\n        limit_key_shape = input_members[paginator_config['limit_key']]\n        type_name = limit_key_shape.type_name\n        if type_name not in PageArgument.type_map:\n            raise TypeError(\n                ('Unsupported pagination type {0} for operation {1}'\n                 ' and parameter {2}').format(\n                    type_name, operation_model.name,\n                    paginator_config['limit_key']))\n        add_paging_argument(argument_table, 'page-size',\n                            PageArgument('page-size', PAGE_SIZE_HELP,\n                                         parse_type=type_name,\n                                         serialized_name='PageSize'),\n                            shadowed_args)\n\n    add_paging_argument(argument_table, 'max-items',\n                        PageArgument('max-items', MAX_ITEMS_HELP,\n                                     parse_type=type_name,\n                                     serialized_name='MaxItems'),\n                        shadowed_args)\n    session.register(\n        parsed_args_event,\n        partial(check_should_enable_pagination,\n                list(_get_all_cli_input_tokens(paginator_config)),\n                shadowed_args, argument_table))\n\n\ndef add_paging_argument(argument_table, arg_name, argument, shadowed_args):\n    if arg_name in argument_table:\n        # If there's already an entry in the arg table for this argument,\n        # this means we're shadowing an argument for this operation.  We\n        # need to store this later in case pagination is turned off because\n        # we put these arguments back.\n        # See the comment in check_should_enable_pagination() for more info.\n        shadowed_args[arg_name] = argument_table[arg_name]\n    argument_table[arg_name] = argument\n\n\ndef check_should_enable_pagination(input_tokens, shadowed_args, argument_table,\n                                   parsed_args, parsed_globals, **kwargs):\n    normalized_paging_args = ['start_token', 'max_items']\n    for token in input_tokens:\n        py_name = token.replace('-', '_')\n        if getattr(parsed_args, py_name) is not None and \\\n                py_name not in normalized_paging_args:\n            # The user has specified a manual (undocumented) pagination arg.\n            # We need to automatically turn pagination off.\n            logger.debug(\"User has specified a manual pagination arg. \"\n                         \"Automatically setting --no-paginate.\")\n            parsed_globals.paginate = False\n\n            # Because pagination is now disabled, there's a chance that\n            # we were shadowing arguments.  For example, we inject a\n            # --max-items argument in unify_paging_params().  If the\n            # the operation also provides its own MaxItems (which we\n            # expose as --max-items) then our custom pagination arg\n            # was shadowing the customers arg.  When we turn pagination\n            # off we need to put back the original argument which is\n            # what we're doing here.\n            for key, value in shadowed_args.items():\n                argument_table[key] = value\n    \n    if not parsed_globals.paginate:\n        ensure_paging_params_not_set(parsed_args, shadowed_args)\n\n\ndef ensure_paging_params_not_set(parsed_args, shadowed_args):\n    paging_params = ['starting_token', 'page_size', 'max_items']\n    shadowed_params = [p.replace('-', '_') for p in shadowed_args.keys()]\n    params_used = [p for p in paging_params if\n                   p not in shadowed_params and getattr(parsed_args, p)]\n\n    if len(params_used) > 0:\n        converted_params = ', '.join(\n            [\"--\" + p.replace('_', '-') for p in params_used])\n        raise PaginationError(\n            message=\"Cannot specify --no-paginate along with pagination \"\n                    \"arguments: %s\" % converted_params)\n\n\ndef _remove_existing_paging_arguments(argument_table, pagination_config):\n    for cli_name in _get_all_cli_input_tokens(pagination_config):\n        argument_table[cli_name]._UNDOCUMENTED = True\n\n\ndef _get_all_cli_input_tokens(pagination_config):\n    # Get all input tokens including the limit_key\n    # if it exists.\n    tokens = _get_input_tokens(pagination_config)\n    for token_name in tokens:\n        cli_name = xform_name(token_name, '-')\n        yield cli_name\n    if 'limit_key' in pagination_config:\n        key_name = pagination_config['limit_key']\n        cli_name = xform_name(key_name, '-')\n        yield cli_name\n\n\ndef _get_input_tokens(pagination_config):\n    tokens = pagination_config['input_token']\n    if not isinstance(tokens, list):\n        return [tokens]\n    return tokens\n\n\ndef _get_cli_name(param_objects, token_name):\n    for param in param_objects:\n        if param.name == token_name:\n            return param.cli_name.lstrip('-')\n\n\nclass PageArgument(BaseCLIArgument):\n    type_map = {\n        'string': str,\n        'integer': int,\n    }\n\n    def __init__(self, name, documentation, parse_type, serialized_name):\n        self.argument_model = model.Shape('PageArgument', {'type': 'string'})\n        self._name = name\n        self._serialized_name = serialized_name\n        self._documentation = documentation\n        self._parse_type = parse_type\n        self._required = False\n\n    @property\n    def cli_name(self):\n        return '--' + self._name\n\n    @property\n    def cli_type_name(self):\n        return self._parse_type\n\n    @property\n    def required(self):\n        return self._required\n\n    @required.setter\n    def required(self, value):\n        self._required = value\n\n    @property\n    def documentation(self):\n        return self._documentation\n\n    def add_to_parser(self, parser):\n        parser.add_argument(self.cli_name, dest=self.py_name,\n                            type=self.type_map[self._parse_type])\n\n    def add_to_params(self, parameters, value):\n        if value is not None:\n            pagination_config = parameters.get('PaginationConfig', {})\n            pagination_config[self._serialized_name] = value\n            parameters['PaginationConfig'] = pagination_config\n", "path": "awscli/customizations/paginate.py"}], "after_files": [{"content": "# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\"\"\"This module has customizations to unify paging paramters.\n\nFor any operation that can be paginated, we will:\n\n    * Hide the service specific pagination params.  This can vary across\n    services and we're going to replace them with a consistent set of\n    arguments.  The arguments will still work, but they are not\n    documented.  This allows us to add a pagination config after\n    the fact and still remain backwards compatible with users that\n    were manually doing pagination.\n    * Add a ``--starting-token`` and a ``--max-items`` argument.\n\n\"\"\"\nimport logging\nfrom functools import partial\n\nfrom botocore import xform_name\nfrom botocore.exceptions import DataNotFoundError, PaginationError\nfrom botocore import model\n\nfrom awscli.arguments import BaseCLIArgument\n\n\nlogger = logging.getLogger(__name__)\n\n\nSTARTING_TOKEN_HELP = \"\"\"\n<p>A token to specify where to start paginating.  This is the\n<code>NextToken</code> from a previously truncated response.</p>\n\"\"\"\n\nMAX_ITEMS_HELP = \"\"\"\n<p>The total number of items to return.  If the total number\nof items available is more than the value specified in\nmax-items then a <code>NextToken</code> will\nbe provided in the output that you can use to resume pagination.\nThis <code>NextToken</code> response element should <b>not</b> be\nused directly outside of the AWS CLI.</p>\n\"\"\"\n\nPAGE_SIZE_HELP = \"\"\"\n<p>The size of each page.<p>\n\"\"\"\n\n\ndef register_pagination(event_handlers):\n    event_handlers.register('building-argument-table', unify_paging_params)\n    event_handlers.register_last('doc-description', add_paging_description)\n\n\ndef get_paginator_config(session, service_name, operation_name):\n    try:\n        paginator_model = session.get_paginator_model(service_name)\n    except DataNotFoundError:\n        return None\n    try:\n        operation_paginator_config = paginator_model.get_paginator(\n            operation_name)\n    except ValueError:\n        return None\n    return operation_paginator_config\n\n\ndef add_paging_description(help_command, **kwargs):\n    # This customization is only applied to the description of\n    # Operations, so we must filter out all other events.\n    if not isinstance(help_command.obj, model.OperationModel):\n        return\n    service_name = help_command.obj.service_model.service_name\n    paginator_config = get_paginator_config(\n        help_command.session, service_name, help_command.obj.name)\n    if not paginator_config:\n        return\n    help_command.doc.style.new_paragraph()\n    help_command.doc.writeln(\n        ('``%s`` is a paginated operation. Multiple API calls may be issued '\n         'in order to retrieve the entire data set of results. You can '\n         'disable pagination by providing the ``--no-paginate`` argument.')\n        % help_command.name)\n    # Only include result key information if it is present.\n    if paginator_config.get('result_key'):\n        queries = paginator_config['result_key']\n        if type(queries) is not list:\n            queries = [queries]\n        queries = \", \".join([('``%s``' % s) for s in queries])\n        help_command.doc.writeln(\n            ('When using ``--output text`` and the ``--query`` argument on a '\n             'paginated response, the ``--query`` argument must extract data '\n             'from the results of the following query expressions: %s')\n            % queries)\n\n\ndef unify_paging_params(argument_table, operation_model, event_name,\n                        session, **kwargs):\n    paginator_config = get_paginator_config(\n        session, operation_model.service_model.service_name,\n        operation_model.name)\n    if paginator_config is None:\n        # We only apply these customizations to paginated responses.\n        return\n    logger.debug(\"Modifying paging parameters for operation: %s\",\n                 operation_model.name)\n    _remove_existing_paging_arguments(argument_table, paginator_config)\n    parsed_args_event = event_name.replace('building-argument-table.',\n                                           'operation-args-parsed.')\n    shadowed_args = {}\n    add_paging_argument(argument_table, 'starting-token',\n                        PageArgument('starting-token', STARTING_TOKEN_HELP,\n                                     parse_type='string',\n                                     serialized_name='StartingToken'),\n                        shadowed_args)\n    input_members = operation_model.input_shape.members\n    type_name = 'integer'\n    if 'limit_key' in paginator_config:\n        limit_key_shape = input_members[paginator_config['limit_key']]\n        type_name = limit_key_shape.type_name\n        if type_name not in PageArgument.type_map:\n            raise TypeError(\n                ('Unsupported pagination type {0} for operation {1}'\n                 ' and parameter {2}').format(\n                    type_name, operation_model.name,\n                    paginator_config['limit_key']))\n        add_paging_argument(argument_table, 'page-size',\n                            PageArgument('page-size', PAGE_SIZE_HELP,\n                                         parse_type=type_name,\n                                         serialized_name='PageSize'),\n                            shadowed_args)\n\n    add_paging_argument(argument_table, 'max-items',\n                        PageArgument('max-items', MAX_ITEMS_HELP,\n                                     parse_type=type_name,\n                                     serialized_name='MaxItems'),\n                        shadowed_args)\n    session.register(\n        parsed_args_event,\n        partial(check_should_enable_pagination,\n                list(_get_all_cli_input_tokens(paginator_config)),\n                shadowed_args, argument_table))\n\n\ndef add_paging_argument(argument_table, arg_name, argument, shadowed_args):\n    if arg_name in argument_table:\n        # If there's already an entry in the arg table for this argument,\n        # this means we're shadowing an argument for this operation.  We\n        # need to store this later in case pagination is turned off because\n        # we put these arguments back.\n        # See the comment in check_should_enable_pagination() for more info.\n        shadowed_args[arg_name] = argument_table[arg_name]\n    argument_table[arg_name] = argument\n\n\ndef check_should_enable_pagination(input_tokens, shadowed_args, argument_table,\n                                   parsed_args, parsed_globals, **kwargs):\n    normalized_paging_args = ['start_token', 'max_items']\n    for token in input_tokens:\n        py_name = token.replace('-', '_')\n        if getattr(parsed_args, py_name) is not None and \\\n                py_name not in normalized_paging_args:\n            # The user has specified a manual (undocumented) pagination arg.\n            # We need to automatically turn pagination off.\n            logger.debug(\"User has specified a manual pagination arg. \"\n                         \"Automatically setting --no-paginate.\")\n            parsed_globals.paginate = False\n\n            # Because pagination is now disabled, there's a chance that\n            # we were shadowing arguments.  For example, we inject a\n            # --max-items argument in unify_paging_params().  If the\n            # the operation also provides its own MaxItems (which we\n            # expose as --max-items) then our custom pagination arg\n            # was shadowing the customers arg.  When we turn pagination\n            # off we need to put back the original argument which is\n            # what we're doing here.\n            for key, value in shadowed_args.items():\n                argument_table[key] = value\n\n    if not parsed_globals.paginate:\n        ensure_paging_params_not_set(parsed_args, shadowed_args)\n\n\ndef ensure_paging_params_not_set(parsed_args, shadowed_args):\n    paging_params = ['starting_token', 'page_size', 'max_items']\n    shadowed_params = [p.replace('-', '_') for p in shadowed_args.keys()]\n    params_used = [p for p in paging_params if\n                   p not in shadowed_params and getattr(parsed_args, p, None)]\n\n    if len(params_used) > 0:\n        converted_params = ', '.join(\n            [\"--\" + p.replace('_', '-') for p in params_used])\n        raise PaginationError(\n            message=\"Cannot specify --no-paginate along with pagination \"\n                    \"arguments: %s\" % converted_params)\n\n\ndef _remove_existing_paging_arguments(argument_table, pagination_config):\n    for cli_name in _get_all_cli_input_tokens(pagination_config):\n        argument_table[cli_name]._UNDOCUMENTED = True\n\n\ndef _get_all_cli_input_tokens(pagination_config):\n    # Get all input tokens including the limit_key\n    # if it exists.\n    tokens = _get_input_tokens(pagination_config)\n    for token_name in tokens:\n        cli_name = xform_name(token_name, '-')\n        yield cli_name\n    if 'limit_key' in pagination_config:\n        key_name = pagination_config['limit_key']\n        cli_name = xform_name(key_name, '-')\n        yield cli_name\n\n\ndef _get_input_tokens(pagination_config):\n    tokens = pagination_config['input_token']\n    if not isinstance(tokens, list):\n        return [tokens]\n    return tokens\n\n\ndef _get_cli_name(param_objects, token_name):\n    for param in param_objects:\n        if param.name == token_name:\n            return param.cli_name.lstrip('-')\n\n\nclass PageArgument(BaseCLIArgument):\n    type_map = {\n        'string': str,\n        'integer': int,\n    }\n\n    def __init__(self, name, documentation, parse_type, serialized_name):\n        self.argument_model = model.Shape('PageArgument', {'type': 'string'})\n        self._name = name\n        self._serialized_name = serialized_name\n        self._documentation = documentation\n        self._parse_type = parse_type\n        self._required = False\n\n    @property\n    def cli_name(self):\n        return '--' + self._name\n\n    @property\n    def cli_type_name(self):\n        return self._parse_type\n\n    @property\n    def required(self):\n        return self._required\n\n    @required.setter\n    def required(self, value):\n        self._required = value\n\n    @property\n    def documentation(self):\n        return self._documentation\n\n    def add_to_parser(self, parser):\n        parser.add_argument(self.cli_name, dest=self.py_name,\n                            type=self.type_map[self._parse_type])\n\n    def add_to_params(self, parameters, value):\n        if value is not None:\n            pagination_config = parameters.get('PaginationConfig', {})\n            pagination_config[self._serialized_name] = value\n            parameters['PaginationConfig'] = pagination_config\n", "path": "awscli/customizations/paginate.py"}]}