|
2 | 2 | """
|
3 | 3 | import cgi
|
4 | 4 | import logging
|
| 5 | +import mimetypes |
5 | 6 | import os
|
6 | 7 |
|
7 | 8 | from pip._vendor.requests.models import CONTENT_CHUNK_SIZE
|
8 | 9 |
|
9 | 10 | from pip._internal.models.index import PyPI
|
10 | 11 | from pip._internal.network.cache import is_from_cache
|
11 | 12 | from pip._internal.network.utils import response_chunks
|
12 |
| -from pip._internal.utils.misc import format_size, redact_auth_from_url |
| 13 | +from pip._internal.utils.misc import ( |
| 14 | + format_size, |
| 15 | + redact_auth_from_url, |
| 16 | + splitext, |
| 17 | +) |
13 | 18 | from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
14 | 19 | from pip._internal.utils.ui import DownloadProgressProvider
|
15 | 20 |
|
@@ -96,3 +101,27 @@ def parse_content_disposition(content_disposition, default_filename):
|
96 | 101 | # in case the filename contains ".." path parts.
|
97 | 102 | filename = sanitize_content_filename(filename)
|
98 | 103 | return filename or default_filename
|
| 104 | + |
| 105 | + |
| 106 | +def _get_http_response_filename(resp, link): |
| 107 | + # type: (Response, Link) -> str |
| 108 | + """Get an ideal filename from the given HTTP response, falling back to |
| 109 | + the link filename if not provided. |
| 110 | + """ |
| 111 | + filename = link.filename # fallback |
| 112 | + # Have a look at the Content-Disposition header for a better guess |
| 113 | + content_disposition = resp.headers.get('content-disposition') |
| 114 | + if content_disposition: |
| 115 | + filename = parse_content_disposition(content_disposition, filename) |
| 116 | + ext = splitext(filename)[1] # type: Optional[str] |
| 117 | + if not ext: |
| 118 | + ext = mimetypes.guess_extension( |
| 119 | + resp.headers.get('content-type', '') |
| 120 | + ) |
| 121 | + if ext: |
| 122 | + filename += ext |
| 123 | + if not ext and link.url != resp.url: |
| 124 | + ext = os.path.splitext(resp.url)[1] |
| 125 | + if ext: |
| 126 | + filename += ext |
| 127 | + return filename |
0 commit comments