We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Deprecate & Remove X-PyPI-Last-Serial on File Responses
Ideally we'd like to not serve the file responses via the Warehouse web application, however we currently need to do this in order to add the `X-PyPI-Last-Serial` responses. Bandersnatch is currently using this, so it would be great to investigate what it's being used for and if we can safely deprecate and remove it.


--- 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/packaging/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 import fs.errors
14 
15 from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
16 from pyramid.response import FileIter, Response
17 from pyramid.view import view_config
18 from sqlalchemy import func
19 from sqlalchemy.orm.exc import NoResultFound
20 
21 from warehouse.accounts.models import User
22 from warehouse.cache.http import cache_control
23 from warehouse.cache.origin import origin_cache
24 from warehouse.packaging.interfaces import IDownloadStatService
25 from warehouse.packaging.models import Release, File, Role, JournalEntry
26 
27 
28 @view_config(
29     route_name="packaging.project",
30     renderer="packaging/detail.html",
31     decorator=[
32         cache_control(1 * 24 * 60 * 60),  # 1 day
33         origin_cache(7 * 24 * 60 * 60),   # 7 days
34     ],
35 )
36 def project_detail(project, request):
37     if project.name != request.matchdict.get("name", project.name):
38         return HTTPMovedPermanently(
39             request.current_route_url(name=project.name),
40         )
41 
42     try:
43         release = project.releases.order_by(
44             Release._pypi_ordering.desc()
45         ).limit(1).one()
46     except NoResultFound:
47         raise HTTPNotFound from None
48 
49     return release_detail(release, request)
50 
51 
52 @view_config(
53     route_name="packaging.release",
54     renderer="packaging/detail.html",
55     decorator=[
56         cache_control(7 * 24 * 60 * 60),  # 7 days
57         origin_cache(30 * 24 * 60 * 60),  # 30 days
58     ],
59 )
60 def release_detail(release, request):
61     project = release.project
62 
63     if project.name != request.matchdict.get("name", project.name):
64         return HTTPMovedPermanently(
65             request.current_route_url(name=project.name),
66         )
67 
68     # Get all of the registered versions for this Project, in order of newest
69     # to oldest.
70     all_releases = (
71         project.releases
72         .with_entities(Release.version, Release.created)
73         .order_by(Release._pypi_ordering.desc())
74         .all()
75     )
76 
77     # Get all of the maintainers for this project.
78     maintainers = [
79         r.user
80         for r in (
81             request.db.query(Role)
82             .join(User)
83             .filter(Role.project == project)
84             .distinct(User.username)
85             .order_by(User.username)
86             .all()
87         )
88     ]
89 
90     stats_svc = request.find_service(IDownloadStatService)
91 
92     return {
93         "project": project,
94         "release": release,
95         "files": release.files.all(),
96         "all_releases": all_releases,
97         "maintainers": maintainers,
98         "download_stats": {
99             "daily": stats_svc.get_daily_stats(project.name),
100             "weekly": stats_svc.get_weekly_stats(project.name),
101             "monthly": stats_svc.get_monthly_stats(project.name),
102         },
103     }
104 
105 
106 @view_config(
107     route_name="packaging.file",
108     decorator=[
109         cache_control(365 * 24 * 60 * 60),  # 1 year
110     ],
111 )
112 def packages(request):
113     # The amount of logic that we can do in this view is very limited, this
114     # view needs to be able to be handled by Fastly directly hitting S3 instead
115     # of actually hitting this view. This more or less means that we're limited
116     # to just setting headers and serving the actual file. In addition the
117     # headers that we can set, have to be able to be determined at file upload
118     # time instead of dynamically.
119 
120     # Grab the path of the file that we're attempting to serve
121     path = request.matchdict["path"]
122 
123     # We need to look up the File that is associated with this path, either the
124     # package path or the pgp path. If that doesn't exist then we'll bail out
125     # early with a 404.
126     try:
127         file_ = (
128             request.db.query(File)
129                       .filter((File.path == path) | (File.pgp_path == path))
130                       .one()
131         )
132     except NoResultFound:
133         raise HTTPNotFound from None
134 
135     # If this request is for a PGP signature, and the file doesn't have a PGP
136     # signature, then we can go ahead and 404 now before hitting the file
137     # storage.
138     if path == file_.pgp_path and not file_.has_pgp_signature:
139         raise HTTPNotFound
140 
141     # Try to open the file, streaming if possible, and if this file doesn't
142     # exist then we'll return a 404 error. However we'll log an error because
143     # if the database thinks we have a file, then a file should exist here.
144     try:
145         # TODO: We need to use mode="rb" here because this is a binary file
146         #       and we don't want Python to attempt to decode it. However S3FS
147         #       checks explicitly for mode="r-" to support streaming access.
148         #       We need to get S3FS so that it support rb- as well as r-.
149         f = request.registry["filesystems"]["packages"].open(path, mode="rb")
150     except fs.errors.ResourceNotFoundError:
151         # TODO: Log an error here, this file doesn't exists for some reason,
152         #       but it should because the database thinks it should.
153         raise HTTPNotFound from None
154 
155     # If the path we're accessing is the path for the package itself, as
156     # opposed to the path for the signature, then we can include a
157     # Content-Length header.
158     content_length = None
159     if path == file_.path:
160         content_length = file_.size
161 
162     resp = Response(
163         # If we have a wsgi.file_wrapper, we'll want to use that so that, if
164         # possible, this will use an optimized method of sending. Otherwise
165         # we'll just use Pyramid's FileIter as a fallback.
166         app_iter=request.environ.get("wsgi.file_wrapper", FileIter)(f),
167         # We use application/octet-stream instead of something nicer because
168         # different HTTP libraries will treat different combinations of
169         # Content-Type and Content-Encoding differently. The only thing that
170         # works sanely across all things without having something in the middle
171         # decide it can decompress the result to "help" the end user is with
172         # Content-Type: applicaton/octet-stream and no Content-Encoding.
173         content_type="application/octet-stream",
174         content_encoding=None,
175         # We need to specify an ETag for this response. Since ETags compared
176         # between URLs have no meaning (and thus, is safe for two URLs to share
177         # the same ETag) we will just use the MD5 hash of the package as our
178         # ETag.
179         etag=file_.md5_digest,
180         # Similarly to the ETag header, we'll just use the date that the file
181         # was uploaded as the Last-Modified header.
182         last_modified=file_.upload_time,
183         # If we have a Content-Length, we'll go ahead and use it here to
184         # hopefully enable the server and clients alike to be smarter about how
185         # they handle downloading this response.
186         content_length=content_length,
187     )
188 
189     # We also need to get the X-PyPI-Last-Serial for the project associated
190     # with this file. Bandersnatch (and other mirroring clients) will use this
191     # to determine what kind of action to take if the MD5 hash does not match
192     # what it expected.
193     serial = (
194         request.db.query(func.max(JournalEntry.id))
195                   .filter(JournalEntry.name == file_.name)
196                   .scalar()
197     )
198     resp.headers["X-PyPI-Last-Serial"] = serial or 0
199 
200     return resp
201
```
--- 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/packaging/views.py b/warehouse/packaging/views.py
--- a/warehouse/packaging/views.py
+++ b/warehouse/packaging/views.py
@@ -15,14 +15,13 @@
 from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound
 from pyramid.response import FileIter, Response
 from pyramid.view import view_config
-from sqlalchemy import func
 from sqlalchemy.orm.exc import NoResultFound
 
 from warehouse.accounts.models import User
 from warehouse.cache.http import cache_control
 from warehouse.cache.origin import origin_cache
 from warehouse.packaging.interfaces import IDownloadStatService
-from warehouse.packaging.models import Release, File, Role, JournalEntry
+from warehouse.packaging.models import Release, File, Role
 
 
 @view_config(
@@ -113,9 +112,7 @@
     # The amount of logic that we can do in this view is very limited, this
     # view needs to be able to be handled by Fastly directly hitting S3 instead
     # of actually hitting this view. This more or less means that we're limited
-    # to just setting headers and serving the actual file. In addition the
-    # headers that we can set, have to be able to be determined at file upload
-    # time instead of dynamically.
+    # to just serving the actual file.
 
     # Grab the path of the file that we're attempting to serve
     path = request.matchdict["path"]
@@ -159,7 +156,7 @@
     if path == file_.path:
         content_length = file_.size
 
-    resp = Response(
+    return Response(
         # If we have a wsgi.file_wrapper, we'll want to use that so that, if
         # possible, this will use an optimized method of sending. Otherwise
         # we'll just use Pyramid's FileIter as a fallback.
@@ -185,16 +182,3 @@
         # they handle downloading this response.
         content_length=content_length,
     )
-
-    # We also need to get the X-PyPI-Last-Serial for the project associated
-    # with this file. Bandersnatch (and other mirroring clients) will use this
-    # to determine what kind of action to take if the MD5 hash does not match
-    # what it expected.
-    serial = (
-        request.db.query(func.max(JournalEntry.id))
-                  .filter(JournalEntry.name == file_.name)
-                  .scalar()
-    )
-    resp.headers["X-PyPI-Last-Serial"] = serial or 0
-
-    return resp

{"golden_diff": "diff --git a/warehouse/packaging/views.py b/warehouse/packaging/views.py\n--- a/warehouse/packaging/views.py\n+++ b/warehouse/packaging/views.py\n@@ -15,14 +15,13 @@\n from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound\n from pyramid.response import FileIter, Response\n from pyramid.view import view_config\n-from sqlalchemy import func\n from sqlalchemy.orm.exc import NoResultFound\n \n from warehouse.accounts.models import User\n from warehouse.cache.http import cache_control\n from warehouse.cache.origin import origin_cache\n from warehouse.packaging.interfaces import IDownloadStatService\n-from warehouse.packaging.models import Release, File, Role, JournalEntry\n+from warehouse.packaging.models import Release, File, Role\n \n \n @view_config(\n@@ -113,9 +112,7 @@\n     # The amount of logic that we can do in this view is very limited, this\n     # view needs to be able to be handled by Fastly directly hitting S3 instead\n     # of actually hitting this view. This more or less means that we're limited\n-    # to just setting headers and serving the actual file. In addition the\n-    # headers that we can set, have to be able to be determined at file upload\n-    # time instead of dynamically.\n+    # to just serving the actual file.\n \n     # Grab the path of the file that we're attempting to serve\n     path = request.matchdict[\"path\"]\n@@ -159,7 +156,7 @@\n     if path == file_.path:\n         content_length = file_.size\n \n-    resp = Response(\n+    return Response(\n         # If we have a wsgi.file_wrapper, we'll want to use that so that, if\n         # possible, this will use an optimized method of sending. Otherwise\n         # we'll just use Pyramid's FileIter as a fallback.\n@@ -185,16 +182,3 @@\n         # they handle downloading this response.\n         content_length=content_length,\n     )\n-\n-    # We also need to get the X-PyPI-Last-Serial for the project associated\n-    # with this file. Bandersnatch (and other mirroring clients) will use this\n-    # to determine what kind of action to take if the MD5 hash does not match\n-    # what it expected.\n-    serial = (\n-        request.db.query(func.max(JournalEntry.id))\n-                  .filter(JournalEntry.name == file_.name)\n-                  .scalar()\n-    )\n-    resp.headers[\"X-PyPI-Last-Serial\"] = serial or 0\n-\n-    return resp\n", "issue": "Deprecate & Remove X-PyPI-Last-Serial on File Responses\nIdeally we'd like to not serve the file responses via the Warehouse web application, however we currently need to do this in order to add the `X-PyPI-Last-Serial` responses. Bandersnatch is currently using this, so it would be great to investigate what it's being used for and if we can safely deprecate and remove it.\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\nimport fs.errors\n\nfrom pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound\nfrom pyramid.response import FileIter, Response\nfrom pyramid.view import view_config\nfrom sqlalchemy import func\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom warehouse.accounts.models import User\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.interfaces import IDownloadStatService\nfrom warehouse.packaging.models import Release, File, Role, JournalEntry\n\n\n@view_config(\n    route_name=\"packaging.project\",\n    renderer=\"packaging/detail.html\",\n    decorator=[\n        cache_control(1 * 24 * 60 * 60),  # 1 day\n        origin_cache(7 * 24 * 60 * 60),   # 7 days\n    ],\n)\ndef project_detail(project, request):\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_url(name=project.name),\n        )\n\n    try:\n        release = project.releases.order_by(\n            Release._pypi_ordering.desc()\n        ).limit(1).one()\n    except NoResultFound:\n        raise HTTPNotFound from None\n\n    return release_detail(release, request)\n\n\n@view_config(\n    route_name=\"packaging.release\",\n    renderer=\"packaging/detail.html\",\n    decorator=[\n        cache_control(7 * 24 * 60 * 60),  # 7 days\n        origin_cache(30 * 24 * 60 * 60),  # 30 days\n    ],\n)\ndef release_detail(release, request):\n    project = release.project\n\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_url(name=project.name),\n        )\n\n    # Get all of the registered versions for this Project, in order of newest\n    # to oldest.\n    all_releases = (\n        project.releases\n        .with_entities(Release.version, Release.created)\n        .order_by(Release._pypi_ordering.desc())\n        .all()\n    )\n\n    # Get all of the maintainers for this project.\n    maintainers = [\n        r.user\n        for r in (\n            request.db.query(Role)\n            .join(User)\n            .filter(Role.project == project)\n            .distinct(User.username)\n            .order_by(User.username)\n            .all()\n        )\n    ]\n\n    stats_svc = request.find_service(IDownloadStatService)\n\n    return {\n        \"project\": project,\n        \"release\": release,\n        \"files\": release.files.all(),\n        \"all_releases\": all_releases,\n        \"maintainers\": maintainers,\n        \"download_stats\": {\n            \"daily\": stats_svc.get_daily_stats(project.name),\n            \"weekly\": stats_svc.get_weekly_stats(project.name),\n            \"monthly\": stats_svc.get_monthly_stats(project.name),\n        },\n    }\n\n\n@view_config(\n    route_name=\"packaging.file\",\n    decorator=[\n        cache_control(365 * 24 * 60 * 60),  # 1 year\n    ],\n)\ndef packages(request):\n    # The amount of logic that we can do in this view is very limited, this\n    # view needs to be able to be handled by Fastly directly hitting S3 instead\n    # of actually hitting this view. This more or less means that we're limited\n    # to just setting headers and serving the actual file. In addition the\n    # headers that we can set, have to be able to be determined at file upload\n    # time instead of dynamically.\n\n    # Grab the path of the file that we're attempting to serve\n    path = request.matchdict[\"path\"]\n\n    # We need to look up the File that is associated with this path, either the\n    # package path or the pgp path. If that doesn't exist then we'll bail out\n    # early with a 404.\n    try:\n        file_ = (\n            request.db.query(File)\n                      .filter((File.path == path) | (File.pgp_path == path))\n                      .one()\n        )\n    except NoResultFound:\n        raise HTTPNotFound from None\n\n    # If this request is for a PGP signature, and the file doesn't have a PGP\n    # signature, then we can go ahead and 404 now before hitting the file\n    # storage.\n    if path == file_.pgp_path and not file_.has_pgp_signature:\n        raise HTTPNotFound\n\n    # Try to open the file, streaming if possible, and if this file doesn't\n    # exist then we'll return a 404 error. However we'll log an error because\n    # if the database thinks we have a file, then a file should exist here.\n    try:\n        # TODO: We need to use mode=\"rb\" here because this is a binary file\n        #       and we don't want Python to attempt to decode it. However S3FS\n        #       checks explicitly for mode=\"r-\" to support streaming access.\n        #       We need to get S3FS so that it support rb- as well as r-.\n        f = request.registry[\"filesystems\"][\"packages\"].open(path, mode=\"rb\")\n    except fs.errors.ResourceNotFoundError:\n        # TODO: Log an error here, this file doesn't exists for some reason,\n        #       but it should because the database thinks it should.\n        raise HTTPNotFound from None\n\n    # If the path we're accessing is the path for the package itself, as\n    # opposed to the path for the signature, then we can include a\n    # Content-Length header.\n    content_length = None\n    if path == file_.path:\n        content_length = file_.size\n\n    resp = Response(\n        # If we have a wsgi.file_wrapper, we'll want to use that so that, if\n        # possible, this will use an optimized method of sending. Otherwise\n        # we'll just use Pyramid's FileIter as a fallback.\n        app_iter=request.environ.get(\"wsgi.file_wrapper\", FileIter)(f),\n        # We use application/octet-stream instead of something nicer because\n        # different HTTP libraries will treat different combinations of\n        # Content-Type and Content-Encoding differently. The only thing that\n        # works sanely across all things without having something in the middle\n        # decide it can decompress the result to \"help\" the end user is with\n        # Content-Type: applicaton/octet-stream and no Content-Encoding.\n        content_type=\"application/octet-stream\",\n        content_encoding=None,\n        # We need to specify an ETag for this response. Since ETags compared\n        # between URLs have no meaning (and thus, is safe for two URLs to share\n        # the same ETag) we will just use the MD5 hash of the package as our\n        # ETag.\n        etag=file_.md5_digest,\n        # Similarly to the ETag header, we'll just use the date that the file\n        # was uploaded as the Last-Modified header.\n        last_modified=file_.upload_time,\n        # If we have a Content-Length, we'll go ahead and use it here to\n        # hopefully enable the server and clients alike to be smarter about how\n        # they handle downloading this response.\n        content_length=content_length,\n    )\n\n    # We also need to get the X-PyPI-Last-Serial for the project associated\n    # with this file. Bandersnatch (and other mirroring clients) will use this\n    # to determine what kind of action to take if the MD5 hash does not match\n    # what it expected.\n    serial = (\n        request.db.query(func.max(JournalEntry.id))\n                  .filter(JournalEntry.name == file_.name)\n                  .scalar()\n    )\n    resp.headers[\"X-PyPI-Last-Serial\"] = serial or 0\n\n    return resp\n", "path": "warehouse/packaging/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\nimport fs.errors\n\nfrom pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound\nfrom pyramid.response import FileIter, Response\nfrom pyramid.view import view_config\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom warehouse.accounts.models import User\nfrom warehouse.cache.http import cache_control\nfrom warehouse.cache.origin import origin_cache\nfrom warehouse.packaging.interfaces import IDownloadStatService\nfrom warehouse.packaging.models import Release, File, Role\n\n\n@view_config(\n    route_name=\"packaging.project\",\n    renderer=\"packaging/detail.html\",\n    decorator=[\n        cache_control(1 * 24 * 60 * 60),  # 1 day\n        origin_cache(7 * 24 * 60 * 60),   # 7 days\n    ],\n)\ndef project_detail(project, request):\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_url(name=project.name),\n        )\n\n    try:\n        release = project.releases.order_by(\n            Release._pypi_ordering.desc()\n        ).limit(1).one()\n    except NoResultFound:\n        raise HTTPNotFound from None\n\n    return release_detail(release, request)\n\n\n@view_config(\n    route_name=\"packaging.release\",\n    renderer=\"packaging/detail.html\",\n    decorator=[\n        cache_control(7 * 24 * 60 * 60),  # 7 days\n        origin_cache(30 * 24 * 60 * 60),  # 30 days\n    ],\n)\ndef release_detail(release, request):\n    project = release.project\n\n    if project.name != request.matchdict.get(\"name\", project.name):\n        return HTTPMovedPermanently(\n            request.current_route_url(name=project.name),\n        )\n\n    # Get all of the registered versions for this Project, in order of newest\n    # to oldest.\n    all_releases = (\n        project.releases\n        .with_entities(Release.version, Release.created)\n        .order_by(Release._pypi_ordering.desc())\n        .all()\n    )\n\n    # Get all of the maintainers for this project.\n    maintainers = [\n        r.user\n        for r in (\n            request.db.query(Role)\n            .join(User)\n            .filter(Role.project == project)\n            .distinct(User.username)\n            .order_by(User.username)\n            .all()\n        )\n    ]\n\n    stats_svc = request.find_service(IDownloadStatService)\n\n    return {\n        \"project\": project,\n        \"release\": release,\n        \"files\": release.files.all(),\n        \"all_releases\": all_releases,\n        \"maintainers\": maintainers,\n        \"download_stats\": {\n            \"daily\": stats_svc.get_daily_stats(project.name),\n            \"weekly\": stats_svc.get_weekly_stats(project.name),\n            \"monthly\": stats_svc.get_monthly_stats(project.name),\n        },\n    }\n\n\n@view_config(\n    route_name=\"packaging.file\",\n    decorator=[\n        cache_control(365 * 24 * 60 * 60),  # 1 year\n    ],\n)\ndef packages(request):\n    # The amount of logic that we can do in this view is very limited, this\n    # view needs to be able to be handled by Fastly directly hitting S3 instead\n    # of actually hitting this view. This more or less means that we're limited\n    # to just serving the actual file.\n\n    # Grab the path of the file that we're attempting to serve\n    path = request.matchdict[\"path\"]\n\n    # We need to look up the File that is associated with this path, either the\n    # package path or the pgp path. If that doesn't exist then we'll bail out\n    # early with a 404.\n    try:\n        file_ = (\n            request.db.query(File)\n                      .filter((File.path == path) | (File.pgp_path == path))\n                      .one()\n        )\n    except NoResultFound:\n        raise HTTPNotFound from None\n\n    # If this request is for a PGP signature, and the file doesn't have a PGP\n    # signature, then we can go ahead and 404 now before hitting the file\n    # storage.\n    if path == file_.pgp_path and not file_.has_pgp_signature:\n        raise HTTPNotFound\n\n    # Try to open the file, streaming if possible, and if this file doesn't\n    # exist then we'll return a 404 error. However we'll log an error because\n    # if the database thinks we have a file, then a file should exist here.\n    try:\n        # TODO: We need to use mode=\"rb\" here because this is a binary file\n        #       and we don't want Python to attempt to decode it. However S3FS\n        #       checks explicitly for mode=\"r-\" to support streaming access.\n        #       We need to get S3FS so that it support rb- as well as r-.\n        f = request.registry[\"filesystems\"][\"packages\"].open(path, mode=\"rb\")\n    except fs.errors.ResourceNotFoundError:\n        # TODO: Log an error here, this file doesn't exists for some reason,\n        #       but it should because the database thinks it should.\n        raise HTTPNotFound from None\n\n    # If the path we're accessing is the path for the package itself, as\n    # opposed to the path for the signature, then we can include a\n    # Content-Length header.\n    content_length = None\n    if path == file_.path:\n        content_length = file_.size\n\n    return Response(\n        # If we have a wsgi.file_wrapper, we'll want to use that so that, if\n        # possible, this will use an optimized method of sending. Otherwise\n        # we'll just use Pyramid's FileIter as a fallback.\n        app_iter=request.environ.get(\"wsgi.file_wrapper\", FileIter)(f),\n        # We use application/octet-stream instead of something nicer because\n        # different HTTP libraries will treat different combinations of\n        # Content-Type and Content-Encoding differently. The only thing that\n        # works sanely across all things without having something in the middle\n        # decide it can decompress the result to \"help\" the end user is with\n        # Content-Type: applicaton/octet-stream and no Content-Encoding.\n        content_type=\"application/octet-stream\",\n        content_encoding=None,\n        # We need to specify an ETag for this response. Since ETags compared\n        # between URLs have no meaning (and thus, is safe for two URLs to share\n        # the same ETag) we will just use the MD5 hash of the package as our\n        # ETag.\n        etag=file_.md5_digest,\n        # Similarly to the ETag header, we'll just use the date that the file\n        # was uploaded as the Last-Modified header.\n        last_modified=file_.upload_time,\n        # If we have a Content-Length, we'll go ahead and use it here to\n        # hopefully enable the server and clients alike to be smarter about how\n        # they handle downloading this response.\n        content_length=content_length,\n    )\n", "path": "warehouse/packaging/views.py"}]}