API reference
Everything below is importable from browser_download_filename. See
Usage for examples.
suggested_filename
def suggested_filename(
headers: HeadersLike | None = None,
*,
url: str | None = None,
content_disposition: str | bytes | None = None,
content_type: str | bytes | None = None,
download_attribute: str | None = None,
default_name: str | None = "download",
fallback_charset: str | None = None,
headers_encoding: str | None = None,
platform: Literal["posix", "windows"] | None = None,
) -> str
The file name Chromium suggests for a download (net::GetSuggestedFilename),
which is the name Chrome reports to DevTools and Playwright, except in the
cases listed under Limits of the port. It is never
empty.
| Parameter | Meaning |
|---|---|
headers |
The response's headers (see HeadersLike); None for none. Three headers are read from them: Content-Disposition, the first comma-separated value of its first line; every Content-Type line, for the MIME type; and X-Content-Type-Options, whose first value tells whether Chrome sniffs the body for another MIME type (see The MIME type). |
url |
The final URL of the response, after redirects; None for no URL. When Content-Disposition and download_attribute give no name, the name comes from the last segment of its path (none for about: and data: URLs), else from its host if default_name is None. With headers or content_type, its scheme also decides whether Chrome may sniff the body for a MIME type: only http:, https:, file: and filesystem: URLs are sniffed, and the package assumes one of them when url is None (see The MIME type). |
content_disposition |
The Content-Disposition header, as bytes or as the str an HTTP library produced. None, the default, reads it from headers, if any. Any other value takes the place of every Content-Disposition line in headers, even an empty string: pass "" to ignore the file name the server sent. Only its first comma-separated value is used. |
content_type |
The Content-Type header, as bytes or as the str an HTTP library produced. None, the default, reads it from headers, if any. Any other value takes the place of every Content-Type line in headers; an empty string is an empty Content-Type. The MIME type adds or corrects an extension when the name does not come from Content-Disposition; with neither headers nor content_type, no MIME type is used. |
download_attribute |
The value of an <a download> attribute. Used when Content-Disposition gives no name. |
default_name |
The name when nothing else gives one; it cannot be empty. With None, the URL's host is tried first, and download is the last resort. |
fallback_charset |
A charset for raw bytes that are not UTF-8, in filename and in the URL's path; Chrome uses the browser's default text encoding here. A label Chromium knows, such as "windows-1252" or "big5". |
headers_encoding |
The Python codec your HTTP library decoded the headers with, such as "latin-1" for requests or response.headers.encoding for httpx. Every str header value, in headers, content_disposition and content_type, is encoded back to bytes with it; bytes are used as they are. None guesses the bytes instead (see Inputs as str). It is a Python codec name, not a charset label: "latin-1" is ISO-8859-1 here, not windows-1252. |
platform |
"posix" for Chrome's rules on Linux, macOS and other POSIX systems, "windows" for its rules on Windows. None, the default, picks "windows" when the call runs on Windows (sys.platform == "win32") and "posix" elsewhere. It chooses Chrome's rules for names, not the system's MIME types (see Limits of the port). |
sanitize_filename
def sanitize_filename(
name: str,
*,
default_name: str = "download",
platform: Literal["posix", "windows"] | None = None,
) -> str
Chrome's file name cleaning, applied to a name from anywhere, such as a page
title or user input. The result is what
suggested_filename(download_attribute=name, default_name=default_name, platform=platform)
returns: name is cleaned as Chrome cleans an <a download> value. It is
never empty.
| Parameter | Meaning |
|---|---|
name |
The name to clean. |
default_name |
The name when name gives none, as in suggested_filename. A non-empty str. |
platform |
As in suggested_filename. |
HeadersLike
The type of a response's headers, in the shapes HTTP libraries hold them:
- an object with an
items()method (SupportsHeaderItems), such as a dict, thehttp.client.HTTPMessagethaturllib.requestgives asresponse.headers, the urllib3 headers that requests gives asresponse.raw.headers, or theresponse.headersof requests, httpx and httpx2; the package callsitems(); - otherwise, an iterable of
(name, value)pairs, such as a list of tuples, theresponse.headers.rawof httpx and httpx2, or aiohttp'sresponse.raw_headers.
A name is a str or bytes, matched as Reading header
lines describes. A value (HeaderValue) is
a str or bytes, or a list or tuple of them, one for each header line. The
lines keep their order, and repeated lines all count. The headers are read
only once, so a generator works too.
Each value is the rest of its header line as it was sent. It can hold line
breaks, which are read as Chrome reads them: a line that starts with a space
or a tab is joined on, and any other line, or a blank line, ends the value
(see Reading header lines). The same goes
for the values of content_disposition, content_type and
parse_content_disposition.
A str or bytes is not headers: suggested_filename raises TypeError for one;
pass a single Content-Disposition value as content_disposition.
parse_content_disposition
def parse_content_disposition(
value: str | bytes | HeadersLike | None,
/,
*,
fallback_charset: str | None = None,
headers_encoding: str | None = None,
) -> ContentDisposition
Parses Content-Disposition as Chromium's net::HttpContentDisposition does.
value is one header, as bytes or as the str an HTTP library produced; or a
response's headers (see HeadersLike), of which every
Content-Disposition line is read; None stands for a missing header. type
and filename come from the first comma-separated value of the first line,
which is the value Chrome's download code reads, and blocks_download says
whether Chrome refuses the response because the values differ or the headers
hold a NUL. fallback_charset and headers_encoding are as above.
ContentDisposition
@dataclass(frozen=True, slots=True)
class ContentDisposition:
type: Literal["inline", "attachment"]
filename: str | None
blocks_download: bool = False
@property
def is_attachment(self) -> bool: ...
A parsed header: an immutable value that compares and hashes by its fields.
| Attribute | Meaning |
|---|---|
type |
"attachment" for attachment and for any other valid disposition type Chromium does not know; "inline" for inline, a missing header, or a header that does not start with a valid type. |
filename |
The decoded file name, or None when the header has none that Chromium can use. It is not sanitized: it can contain path separators, control characters and other characters that are unsafe in a file name. |
blocks_download |
Whether Chrome refuses the whole response for one of two reasons: True when the Content-Disposition values, split on commas outside quotes and trimmed of spaces and tabs, are not all the same, byte for byte (ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION), or when the headers hold a NUL, in a name or a value of any header (ERR_INVALID_HTTP_RESPONSE; see Reading header lines). Chrome refuses responses for other reasons too, for which it is False (see Limits of the port). type and filename still come from the first value. See Several Content-Disposition headers. |
is_attachment |
Whether type is "attachment". |
__version__
The installed version of the package, as a str.
Exceptions
Malformed headers and URLs never raise; only invalid arguments do.
| Exception | When it occurs |
|---|---|
LookupError |
fallback_charset is not a charset Chromium can decode, such as utf-7; or headers_encoding is not a Python text codec, such as rot13. |
TypeError |
headers is a str or bytes; headers, or the value of parse_content_disposition, is not a HeadersLike: neither a mapping nor an iterable, an item that is not a (name, value) pair, or a name or a value of another type; a header value or content_type is not str, bytes or None; url, download_attribute, fallback_charset, headers_encoding or the default_name of suggested_filename is not str or None; or name or the default_name of sanitize_filename is not a str. |
ValueError |
platform is not "posix", "windows" or None; default_name is an empty string; or a str header value does not encode with headers_encoding, which means the headers were decoded with another codec. |
All of these are checked, for every header in headers, before any header is
parsed.
Inputs as str
Chromium reads headers as bytes, so a str is mapped back to bytes first. With
headers_encoding, that is exact: the str is encoded with that codec, with
surrogate escapes turned back into the bytes they stand for
(value.encode(headers_encoding, "surrogateescape")).
Without it, the bytes are guessed. If every character is in U+0000–U+00FF and
their Latin-1 bytes are valid UTF-8, those bytes are used; that is what
requests, urllib3 and http.client, which urllib.request uses, give for a
header that carried UTF-8. Any other str is encoded as UTF-8, with the
surrogate escapes that aiohttp uses for undecodable bytes turned back into
those bytes. The guess can be wrong, for example for bytes in a legacy charset
that an HTTP library decoded as Latin-1, such as caf\xe9.txt in
windows-1252. It changes nothing in Content-Type and X-Content-Type-Options,
though: only ASCII characters can match a MIME type or nosniff, and the guess
always gives them back as the bytes they were.
URLs and the other str arguments are taken as text; lone surrogates in them become U+FFFD.