We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
TransportError: TransportError(500, 'search_phase_execution_exception', 'Result window is too lar...
https://app.getsentry.com/python-software-foundation/warehouse-production/issues/118467761/

```
TransportError: TransportError(500, 'search_phase_execution_exception', 'Result window is too large, from + size must be less than or equal to: [10000] but was [10640]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter.')
(18 additional frame(s) were not displayed)
...
  File "warehouse/utils/http.py", line 31, in wrapped
    return fn(context, request)
  File "warehouse/cache/origin/__init__.py", line 81, in wrapped
    return view(context, request)
  File "warehouse/views.py", line 179, in search
    url_maker=paginate_url_factory(request),
  File "warehouse/utils/paginate.py", line 43, in ElasticsearchPage
    return Page(*args, **kwargs)
  File "warehouse/utils/paginate.py", line 26, in __getitem__
    self.results = self.query[range].execute()
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `warehouse/utils/paginate.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 from paginate import Page
14 
15 
16 class _ElasticsearchWrapper:
17 
18     def __init__(self, query):
19         self.query = query
20         self.results = None
21         self.best_guess = None
22 
23     def __getitem__(self, range):
24         if self.results is not None:
25             raise RuntimeError("Cannot reslice after having already sliced.")
26         self.results = self.query[range].execute()
27 
28         if hasattr(self.results, "suggest"):
29             suggestion = self.results.suggest.name_suggestion[0]
30             if suggestion.options:
31                 self.best_guess = suggestion.options[0]
32 
33         return list(self.results)
34 
35     def __len__(self):
36         if self.results is None:
37             raise RuntimeError("Cannot get length until a slice.")
38         return self.results.hits.total
39 
40 
41 def ElasticsearchPage(*args, **kwargs):  # noqa
42     kwargs.setdefault("wrapper_class", _ElasticsearchWrapper)
43     return Page(*args, **kwargs)
44 
45 
46 def paginate_url_factory(request, query_arg="page"):
47     def make_url(page):
48         query_seq = [
49             (k, v)
50             for k, vs in request.GET.dict_of_lists().items()
51             for v in vs
52             if k != query_arg
53         ]
54         query_seq += [(query_arg, page)]
55         return request.current_route_path(_query=query_seq)
56     return make_url
57
```

Path: `warehouse/views.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 from pyramid.httpexceptions import (
14     HTTPException, HTTPSeeOther, HTTPMovedPermanently,
15 )
16 from pyramid.view import (
17     notfound_view_config, forbidden_view_config, view_config,
18 )
19 from sqlalchemy import func
20 from sqlalchemy.orm import aliased, joinedload
21 
22 from warehouse.accounts import REDIRECT_FIELD_NAME
23 from warehouse.accounts.models import User
24 from warehouse.cache.origin import origin_cache
25 from warehouse.cache.http import cache_control
26 from warehouse.packaging.models import Project, Release, File
27 from warehouse.utils.row_counter import RowCount
28 from warehouse.utils.paginate import ElasticsearchPage, paginate_url_factory
29 
30 
31 # We need to ensure that our HTTPException views do not require CSRF because
32 # they will be called in all different kinds of situations, not all of which
33 # will have valid CSRF tokens. For instance, they will be called on POST
34 # requests when the CSRF validator attempts to return a 400 response.
35 @view_config(context=HTTPException, require_csrf=False)
36 @notfound_view_config(append_slash=HTTPMovedPermanently, require_csrf=False)
37 def httpexception_view(exc, request):
38     return exc
39 
40 
41 @forbidden_view_config()
42 def forbidden(exc, request):
43     # If the forbidden error is because the user isn't logged in, then we'll
44     # redirect them to the log in page.
45     if request.authenticated_userid is None:
46         url = request.route_url(
47             "accounts.login",
48             _query={REDIRECT_FIELD_NAME: request.path_qs},
49         )
50         return HTTPSeeOther(url)
51 
52     # If we've reached here, then the user is logged in and they are genuinely
53     # not allowed to access this page.
54     # TODO: Style the forbidden page.
55     return exc
56 
57 
58 @view_config(
59     route_name="robots.txt",
60     renderer="robots.txt",
61     decorator=[
62         cache_control(1 * 24 * 60 * 60),         # 1 day
63         origin_cache(
64             1 * 24 * 60 * 60,                    # 1 day
65             stale_while_revalidate=6 * 60 * 60,  # 6 hours
66             stale_if_error=1 * 24 * 60 * 60,     # 1 day
67         ),
68     ],
69 )
70 def robotstxt(request):
71     request.response.content_type = "text/plain"
72     return {}
73 
74 
75 @view_config(
76     route_name="index",
77     renderer="index.html",
78     decorator=[
79         origin_cache(
80             1 * 60 * 60,                      # 1 hour
81             stale_while_revalidate=10 * 60,   # 10 minutes
82             stale_if_error=1 * 24 * 60 * 60,  # 1 day
83             keys=["all-projects"],
84         ),
85     ]
86 )
87 def index(request):
88     project_names = [
89         r[0] for r in (
90             request.db.query(File.name)
91                    .group_by(File.name)
92                    .order_by(func.sum(File.downloads).desc())
93                    .limit(5)
94                    .all())
95     ]
96     release_a = aliased(
97         Release,
98         request.db.query(Release)
99                   .distinct(Release.name)
100                   .filter(Release.name.in_(project_names))
101                   .order_by(Release.name, Release._pypi_ordering.desc())
102                   .subquery(),
103     )
104     top_projects = (
105         request.db.query(release_a)
106                .options(joinedload(release_a.project),
107                         joinedload(release_a.uploader))
108                .order_by(func.array_idx(project_names, release_a.name))
109                .all()
110     )
111 
112     latest_releases = (
113         request.db.query(Release)
114                   .options(joinedload(Release.project),
115                            joinedload(Release.uploader))
116                   .order_by(Release.created.desc())
117                   .limit(5)
118                   .all()
119     )
120 
121     counts = dict(
122         request.db.query(RowCount.table_name, RowCount.count)
123                   .filter(
124                       RowCount.table_name.in_([
125                           Project.__tablename__,
126                           Release.__tablename__,
127                           File.__tablename__,
128                           User.__tablename__,
129                       ]))
130                   .all()
131     )
132 
133     return {
134         "latest_releases": latest_releases,
135         "top_projects": top_projects,
136         "num_projects": counts.get(Project.__tablename__, 0),
137         "num_releases": counts.get(Release.__tablename__, 0),
138         "num_files": counts.get(File.__tablename__, 0),
139         "num_users": counts.get(User.__tablename__, 0),
140     }
141 
142 
143 @view_config(
144     route_name="search",
145     renderer="search/results.html",
146     decorator=[
147         origin_cache(
148             1 * 60 * 60,                      # 1 hour
149             stale_while_revalidate=10 * 60,   # 10 minutes
150             stale_if_error=1 * 24 * 60 * 60,  # 1 day
151             keys=["all-projects"],
152         )
153     ],
154 )
155 def search(request):
156     if request.params.get("q"):
157         query = request.es.query(
158             "multi_match",
159             query=request.params["q"],
160             fields=[
161                 "name^2", "version", "author", "author_email", "maintainer",
162                 "maintainer_email", "home_page", "license", "summary",
163                 "description", "keywords", "platform", "download_url",
164             ],
165         ).suggest(
166             name="name_suggestion",
167             text=request.params["q"],
168             term={"field": "name"}
169         )
170     else:
171         query = request.es.query()
172 
173     if request.params.get("o"):
174         query = query.sort(request.params["o"])
175 
176     page = ElasticsearchPage(
177         query,
178         page=int(request.params.get("page", 1)),
179         url_maker=paginate_url_factory(request),
180     )
181 
182     return {
183         "page": page,
184         "term": request.params.get("q"),
185         "order": request.params.get("o"),
186     }
187 
188 
189 @view_config(
190     route_name="includes.current-user-indicator",
191     renderer="includes/current-user-indicator.html",
192     uses_session=True,
193 )
194 def current_user_indicator(request):
195     return {}
196
```
--- 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/warehouse/utils/paginate.py b/warehouse/utils/paginate.py
--- a/warehouse/utils/paginate.py
+++ b/warehouse/utils/paginate.py
@@ -15,12 +15,30 @@
 
 class _ElasticsearchWrapper:
 
+    max_results = 10000
+
     def __init__(self, query):
         self.query = query
         self.results = None
         self.best_guess = None
 
     def __getitem__(self, range):
+        # If we're asking for a range that extends past our maximum results,
+        # then we need to clamp the start of our slice to our maximum results
+        # size, and make sure that the end of our slice >= to that to ensure a
+        # consistent slice.
+        if range.start > self.max_results:
+            range = slice(
+                self.max_results,
+                max(range.stop, self.max_results),
+                range.step,
+            )
+
+        # If we're being asked for a range that extends past our maximum result
+        # then we'll clamp it to the maximum result size and stop there.
+        if range.stop > self.max_results:
+            range = slice(range.start, self.max_results, range.step)
+
         if self.results is not None:
             raise RuntimeError("Cannot reslice after having already sliced.")
         self.results = self.query[range].execute()
@@ -35,7 +53,7 @@
     def __len__(self):
         if self.results is None:
             raise RuntimeError("Cannot get length until a slice.")
-        return self.results.hits.total
+        return min(self.results.hits.total, self.max_results)
 
 
 def ElasticsearchPage(*args, **kwargs):  # noqa
diff --git a/warehouse/views.py b/warehouse/views.py
--- a/warehouse/views.py
+++ b/warehouse/views.py
@@ -11,7 +11,7 @@
 # limitations under the License.
 
 from pyramid.httpexceptions import (
-    HTTPException, HTTPSeeOther, HTTPMovedPermanently,
+    HTTPException, HTTPSeeOther, HTTPMovedPermanently, HTTPNotFound,
 )
 from pyramid.view import (
     notfound_view_config, forbidden_view_config, view_config,
@@ -173,12 +173,16 @@
     if request.params.get("o"):
         query = query.sort(request.params["o"])
 
+    page_num = int(request.params.get("page", 1))
     page = ElasticsearchPage(
         query,
-        page=int(request.params.get("page", 1)),
+        page=page_num,
         url_maker=paginate_url_factory(request),
     )
 
+    if page_num > page.page_count:
+        raise HTTPNotFound
+
     return {
         "page": page,
         "term": request.params.get("q"),

{"golden_diff": "diff --git a/warehouse/utils/paginate.py b/warehouse/utils/paginate.py\n--- a/warehouse/utils/paginate.py\n+++ b/warehouse/utils/paginate.py\n@@ -15,12 +15,30 @@\n \n class _ElasticsearchWrapper:\n \n+    max_results = 10000\n+\n     def __init__(self, query):\n         self.query = query\n         self.results = None\n         self.best_guess = None\n \n     def __getitem__(self, range):\n+        # If we're asking for a range that extends past our maximum results,\n+        # then we need to clamp the start of our slice to our maximum results\n+        # size, and make sure that the end of our slice >= to that to ensure a\n+        # consistent slice.\n+        if range.start > self.max_results:\n+            range = slice(\n+                self.max_results,\n+                max(range.stop, self.max_results),\n+                range.step,\n+            )\n+\n+        # If we're being asked for a range that extends past our maximum result\n+        # then we'll clamp it to the maximum result size and stop there.\n+        if range.stop > self.max_results:\n+            range = slice(range.start, self.max_results, range.step)\n+\n         if self.results is not None:\n             raise RuntimeError(\"Cannot reslice after having already sliced.\")\n         self.results = self.query[range].execute()\n@@ -35,7 +53,7 @@\n     def __len__(self):\n         if self.results is None:\n             raise RuntimeError(\"Cannot get length until a slice.\")\n-        return self.results.hits.total\n+        return min(self.results.hits.total, self.max_results)\n \n \n def ElasticsearchPage(*args, **kwargs):  # noqa\ndiff --git a/warehouse/views.py b/warehouse/views.py\n--- a/warehouse/views.py\n+++ b/warehouse/views.py\n@@ -11,7 +11,7 @@\n # limitations under the License.\n \n from pyramid.httpexceptions import (\n-    HTTPException, HTTPSeeOther, HTTPMovedPermanently,\n+    HTTPException, HTTPSeeOther, HTTPMovedPermanently, HTTPNotFound,\n )\n from pyramid.view import (\n     notfound_view_config, forbidden_view_config, view_config,\n@@ -173,12 +173,16 @@\n     if request.params.get(\"o\"):\n         query = query.sort(request.params[\"o\"])\n \n+    page_num = int(request.params.get(\"page\", 1))\n     page = ElasticsearchPage(\n         query,\n-        page=int(request.params.get(\"page\", 1)),\n+        page=page_num,\n         url_maker=paginate_url_factory(request),\n     )\n \n+    if page_num > page.page_count:\n+        raise HTTPNotFound\n+\n     return {\n         \"page\": page,\n         \"term\": request.params.get(\"q\"),\n", "issue": "TransportError: TransportError(500, 'search_phase_execution_exception', 'Result window is too lar...\nhttps://app.getsentry.com/python-software-foundation/warehouse-production/issues/118467761/\n\n```\nTransportError: TransportError(500, 'search_phase_execution_exception', 'Result window is too large, from + size must be less than or equal to: [10000] but was [10640]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter.')\n(18 additional frame(s) were not displayed)\n...\n  File \"warehouse/utils/http.py\", line 31, in wrapped\n    return fn(context, request)\n  File \"warehouse/cache/origin/__init__.py\", line 81, in wrapped\n    return view(context, request)\n  File \"warehouse/views.py\", line 179, in search\n    url_maker=paginate_url_factory(request),\n  File \"warehouse/utils/paginate.py\", line 43, in ElasticsearchPage\n    return Page(*args, **kwargs)\n  File \"warehouse/utils/paginate.py\", line 26, in __getitem__\n    self.results = self.query[range].execute()\n```\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom paginate import Page\n\n\nclass _ElasticsearchWrapper:\n\n    def __init__(self, query):\n        self.query = query\n        self.results = None\n        self.best_guess = None\n\n    def __getitem__(self, range):\n        if self.results is not None:\n            raise RuntimeError(\"Cannot reslice after having already sliced.\")\n        self.results = self.query[range].execute()\n\n        if hasattr(self.results, \"suggest\"):\n            suggestion = self.results.suggest.name_suggestion[0]\n            if suggestion.options:\n                self.best_guess = suggestion.options[0]\n\n        return list(self.results)\n\n    def __len__(self):\n        if self.results is None:\n            raise RuntimeError(\"Cannot get length until a slice.\")\n        return self.results.hits.total\n\n\ndef ElasticsearchPage(*args, **kwargs):  # noqa\n    kwargs.setdefault(\"wrapper_class\", _ElasticsearchWrapper)\n    return Page(*args, **kwargs)\n\n\ndef paginate_url_factory(request, query_arg=\"page\"):\n    def make_url(page):\n        query_seq = [\n            (k, v)\n            for k, vs in request.GET.dict_of_lists().items()\n            for v in vs\n            if k != query_arg\n        ]\n        query_seq += [(query_arg, page)]\n        return request.current_route_path(_query=query_seq)\n    return make_url\n", "path": "warehouse/utils/paginate.py"}, {"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pyramid.httpexceptions import (\n    HTTPException, HTTPSeeOther, HTTPMovedPermanently,\n)\nfrom pyramid.view import (\n    notfound_view_config, forbidden_view_config, view_config,\n)\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import aliased, joinedload\n\nfrom warehouse.accounts import REDIRECT_FIELD_NAME\nfrom warehouse.accounts.models import User\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.cache.http import cache_control\nfrom warehouse.packaging.models import Project, Release, File\nfrom warehouse.utils.row_counter import RowCount\nfrom warehouse.utils.paginate import ElasticsearchPage, paginate_url_factory\n\n\n# We need to ensure that our HTTPException views do not require CSRF because\n# they will be called in all different kinds of situations, not all of which\n# will have valid CSRF tokens. For instance, they will be called on POST\n# requests when the CSRF validator attempts to return a 400 response.\n@view_config(context=HTTPException, require_csrf=False)\n@notfound_view_config(append_slash=HTTPMovedPermanently, require_csrf=False)\ndef httpexception_view(exc, request):\n    return exc\n\n\n@forbidden_view_config()\ndef forbidden(exc, request):\n    # If the forbidden error is because the user isn't logged in, then we'll\n    # redirect them to the log in page.\n    if request.authenticated_userid is None:\n        url = request.route_url(\n            \"accounts.login\",\n            _query={REDIRECT_FIELD_NAME: request.path_qs},\n        )\n        return HTTPSeeOther(url)\n\n    # If we've reached here, then the user is logged in and they are genuinely\n    # not allowed to access this page.\n    # TODO: Style the forbidden page.\n    return exc\n\n\n@view_config(\n    route_name=\"robots.txt\",\n    renderer=\"robots.txt\",\n    decorator=[\n        cache_control(1 * 24 * 60 * 60),         # 1 day\n        origin_cache(\n            1 * 24 * 60 * 60,                    # 1 day\n            stale_while_revalidate=6 * 60 * 60,  # 6 hours\n            stale_if_error=1 * 24 * 60 * 60,     # 1 day\n        ),\n    ],\n)\ndef robotstxt(request):\n    request.response.content_type = \"text/plain\"\n    return {}\n\n\n@view_config(\n    route_name=\"index\",\n    renderer=\"index.html\",\n    decorator=[\n        origin_cache(\n            1 * 60 * 60,                      # 1 hour\n            stale_while_revalidate=10 * 60,   # 10 minutes\n            stale_if_error=1 * 24 * 60 * 60,  # 1 day\n            keys=[\"all-projects\"],\n        ),\n    ]\n)\ndef index(request):\n    project_names = [\n        r[0] for r in (\n            request.db.query(File.name)\n                   .group_by(File.name)\n                   .order_by(func.sum(File.downloads).desc())\n                   .limit(5)\n                   .all())\n    ]\n    release_a = aliased(\n        Release,\n        request.db.query(Release)\n                  .distinct(Release.name)\n                  .filter(Release.name.in_(project_names))\n                  .order_by(Release.name, Release._pypi_ordering.desc())\n                  .subquery(),\n    )\n    top_projects = (\n        request.db.query(release_a)\n               .options(joinedload(release_a.project),\n                        joinedload(release_a.uploader))\n               .order_by(func.array_idx(project_names, release_a.name))\n               .all()\n    )\n\n    latest_releases = (\n        request.db.query(Release)\n                  .options(joinedload(Release.project),\n                           joinedload(Release.uploader))\n                  .order_by(Release.created.desc())\n                  .limit(5)\n                  .all()\n    )\n\n    counts = dict(\n        request.db.query(RowCount.table_name, RowCount.count)\n                  .filter(\n                      RowCount.table_name.in_([\n                          Project.__tablename__,\n                          Release.__tablename__,\n                          File.__tablename__,\n                          User.__tablename__,\n                      ]))\n                  .all()\n    )\n\n    return {\n        \"latest_releases\": latest_releases,\n        \"top_projects\": top_projects,\n        \"num_projects\": counts.get(Project.__tablename__, 0),\n        \"num_releases\": counts.get(Release.__tablename__, 0),\n        \"num_files\": counts.get(File.__tablename__, 0),\n        \"num_users\": counts.get(User.__tablename__, 0),\n    }\n\n\n@view_config(\n    route_name=\"search\",\n    renderer=\"search/results.html\",\n    decorator=[\n        origin_cache(\n            1 * 60 * 60,                      # 1 hour\n            stale_while_revalidate=10 * 60,   # 10 minutes\n            stale_if_error=1 * 24 * 60 * 60,  # 1 day\n            keys=[\"all-projects\"],\n        )\n    ],\n)\ndef search(request):\n    if request.params.get(\"q\"):\n        query = request.es.query(\n            \"multi_match\",\n            query=request.params[\"q\"],\n            fields=[\n                \"name^2\", \"version\", \"author\", \"author_email\", \"maintainer\",\n                \"maintainer_email\", \"home_page\", \"license\", \"summary\",\n                \"description\", \"keywords\", \"platform\", \"download_url\",\n            ],\n        ).suggest(\n            name=\"name_suggestion\",\n            text=request.params[\"q\"],\n            term={\"field\": \"name\"}\n        )\n    else:\n        query = request.es.query()\n\n    if request.params.get(\"o\"):\n        query = query.sort(request.params[\"o\"])\n\n    page = ElasticsearchPage(\n        query,\n        page=int(request.params.get(\"page\", 1)),\n        url_maker=paginate_url_factory(request),\n    )\n\n    return {\n        \"page\": page,\n        \"term\": request.params.get(\"q\"),\n        \"order\": request.params.get(\"o\"),\n    }\n\n\n@view_config(\n    route_name=\"includes.current-user-indicator\",\n    renderer=\"includes/current-user-indicator.html\",\n    uses_session=True,\n)\ndef current_user_indicator(request):\n    return {}\n", "path": "warehouse/views.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom paginate import Page\n\n\nclass _ElasticsearchWrapper:\n\n    max_results = 10000\n\n    def __init__(self, query):\n        self.query = query\n        self.results = None\n        self.best_guess = None\n\n    def __getitem__(self, range):\n        # If we're asking for a range that extends past our maximum results,\n        # then we need to clamp the start of our slice to our maximum results\n        # size, and make sure that the end of our slice >= to that to ensure a\n        # consistent slice.\n        if range.start > self.max_results:\n            range = slice(\n                self.max_results,\n                max(range.stop, self.max_results),\n                range.step,\n            )\n\n        # If we're being asked for a range that extends past our maximum result\n        # then we'll clamp it to the maximum result size and stop there.\n        if range.stop > self.max_results:\n            range = slice(range.start, self.max_results, range.step)\n\n        if self.results is not None:\n            raise RuntimeError(\"Cannot reslice after having already sliced.\")\n        self.results = self.query[range].execute()\n\n        if hasattr(self.results, \"suggest\"):\n            suggestion = self.results.suggest.name_suggestion[0]\n            if suggestion.options:\n                self.best_guess = suggestion.options[0]\n\n        return list(self.results)\n\n    def __len__(self):\n        if self.results is None:\n            raise RuntimeError(\"Cannot get length until a slice.\")\n        return min(self.results.hits.total, self.max_results)\n\n\ndef ElasticsearchPage(*args, **kwargs):  # noqa\n    kwargs.setdefault(\"wrapper_class\", _ElasticsearchWrapper)\n    return Page(*args, **kwargs)\n\n\ndef paginate_url_factory(request, query_arg=\"page\"):\n    def make_url(page):\n        query_seq = [\n            (k, v)\n            for k, vs in request.GET.dict_of_lists().items()\n            for v in vs\n            if k != query_arg\n        ]\n        query_seq += [(query_arg, page)]\n        return request.current_route_path(_query=query_seq)\n    return make_url\n", "path": "warehouse/utils/paginate.py"}, {"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pyramid.httpexceptions import (\n    HTTPException, HTTPSeeOther, HTTPMovedPermanently, HTTPNotFound,\n)\nfrom pyramid.view import (\n    notfound_view_config, forbidden_view_config, view_config,\n)\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import aliased, joinedload\n\nfrom warehouse.accounts import REDIRECT_FIELD_NAME\nfrom warehouse.accounts.models import User\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.cache.http import cache_control\nfrom warehouse.packaging.models import Project, Release, File\nfrom warehouse.utils.row_counter import RowCount\nfrom warehouse.utils.paginate import ElasticsearchPage, paginate_url_factory\n\n\n# We need to ensure that our HTTPException views do not require CSRF because\n# they will be called in all different kinds of situations, not all of which\n# will have valid CSRF tokens. For instance, they will be called on POST\n# requests when the CSRF validator attempts to return a 400 response.\n@view_config(context=HTTPException, require_csrf=False)\n@notfound_view_config(append_slash=HTTPMovedPermanently, require_csrf=False)\ndef httpexception_view(exc, request):\n    return exc\n\n\n@forbidden_view_config()\ndef forbidden(exc, request):\n    # If the forbidden error is because the user isn't logged in, then we'll\n    # redirect them to the log in page.\n    if request.authenticated_userid is None:\n        url = request.route_url(\n            \"accounts.login\",\n            _query={REDIRECT_FIELD_NAME: request.path_qs},\n        )\n        return HTTPSeeOther(url)\n\n    # If we've reached here, then the user is logged in and they are genuinely\n    # not allowed to access this page.\n    # TODO: Style the forbidden page.\n    return exc\n\n\n@view_config(\n    route_name=\"robots.txt\",\n    renderer=\"robots.txt\",\n    decorator=[\n        cache_control(1 * 24 * 60 * 60),         # 1 day\n        origin_cache(\n            1 * 24 * 60 * 60,                    # 1 day\n            stale_while_revalidate=6 * 60 * 60,  # 6 hours\n            stale_if_error=1 * 24 * 60 * 60,     # 1 day\n        ),\n    ],\n)\ndef robotstxt(request):\n    request.response.content_type = \"text/plain\"\n    return {}\n\n\n@view_config(\n    route_name=\"index\",\n    renderer=\"index.html\",\n    decorator=[\n        origin_cache(\n            1 * 60 * 60,                      # 1 hour\n            stale_while_revalidate=10 * 60,   # 10 minutes\n            stale_if_error=1 * 24 * 60 * 60,  # 1 day\n            keys=[\"all-projects\"],\n        ),\n    ]\n)\ndef index(request):\n    project_names = [\n        r[0] for r in (\n            request.db.query(File.name)\n                   .group_by(File.name)\n                   .order_by(func.sum(File.downloads).desc())\n                   .limit(5)\n                   .all())\n    ]\n    release_a = aliased(\n        Release,\n        request.db.query(Release)\n                  .distinct(Release.name)\n                  .filter(Release.name.in_(project_names))\n                  .order_by(Release.name, Release._pypi_ordering.desc())\n                  .subquery(),\n    )\n    top_projects = (\n        request.db.query(release_a)\n               .options(joinedload(release_a.project),\n                        joinedload(release_a.uploader))\n               .order_by(func.array_idx(project_names, release_a.name))\n               .all()\n    )\n\n    latest_releases = (\n        request.db.query(Release)\n                  .options(joinedload(Release.project),\n                           joinedload(Release.uploader))\n                  .order_by(Release.created.desc())\n                  .limit(5)\n                  .all()\n    )\n\n    counts = dict(\n        request.db.query(RowCount.table_name, RowCount.count)\n                  .filter(\n                      RowCount.table_name.in_([\n                          Project.__tablename__,\n                          Release.__tablename__,\n                          File.__tablename__,\n                          User.__tablename__,\n                      ]))\n                  .all()\n    )\n\n    return {\n        \"latest_releases\": latest_releases,\n        \"top_projects\": top_projects,\n        \"num_projects\": counts.get(Project.__tablename__, 0),\n        \"num_releases\": counts.get(Release.__tablename__, 0),\n        \"num_files\": counts.get(File.__tablename__, 0),\n        \"num_users\": counts.get(User.__tablename__, 0),\n    }\n\n\n@view_config(\n    route_name=\"search\",\n    renderer=\"search/results.html\",\n    decorator=[\n        origin_cache(\n            1 * 60 * 60,                      # 1 hour\n            stale_while_revalidate=10 * 60,   # 10 minutes\n            stale_if_error=1 * 24 * 60 * 60,  # 1 day\n            keys=[\"all-projects\"],\n        )\n    ],\n)\ndef search(request):\n    if request.params.get(\"q\"):\n        query = request.es.query(\n            \"multi_match\",\n            query=request.params[\"q\"],\n            fields=[\n                \"name^2\", \"version\", \"author\", \"author_email\", \"maintainer\",\n                \"maintainer_email\", \"home_page\", \"license\", \"summary\",\n                \"description\", \"keywords\", \"platform\", \"download_url\",\n            ],\n        ).suggest(\n            name=\"name_suggestion\",\n            text=request.params[\"q\"],\n            term={\"field\": \"name\"}\n        )\n    else:\n        query = request.es.query()\n\n    if request.params.get(\"o\"):\n        query = query.sort(request.params[\"o\"])\n\n    page_num = int(request.params.get(\"page\", 1))\n    page = ElasticsearchPage(\n        query,\n        page=page_num,\n        url_maker=paginate_url_factory(request),\n    )\n\n    if page_num > page.page_count:\n        raise HTTPNotFound\n\n    return {\n        \"page\": page,\n        \"term\": request.params.get(\"q\"),\n        \"order\": request.params.get(\"o\"),\n    }\n\n\n@view_config(\n    route_name=\"includes.current-user-indicator\",\n    renderer=\"includes/current-user-indicator.html\",\n    uses_session=True,\n)\ndef current_user_indicator(request):\n    return {}\n", "path": "warehouse/views.py"}]}